C# debugging conditional breakpoints

Advanced Debugging Techniques with Conditional Breakpoints in Visual Studio and C#

Part of me wants to make this a post all about the upcoming Tom Clancy’s Ghost Recon Breakpoint video game. I don’t really know anything about video games, especially first-person shooters, but I did read a bunch of Tom Clancy books back in the 90s so I’m clearly qualified.

Talking geopolitics and tactics wouldn’t make a whole lot of sense on a programming blog; the breakpoints we’re talking about here are more the red blob in the side of your Visual Studio editor kind.

The simplest breakpoint in the simplest application

Breakpoints are places in the code where the debugger stops execution so you can poke around at the state of the code and see what values variables are taking on. It is super useful to be able to see the state of execution when figuring out how code works or why it doesn’t work.

What about if the problem you’re trying to solve is deep inside a loop? Setting the breakpoint inside the loop will get you there but if the problem you’re solving is 500 items into a 10000 element collection you’re going to wear your fingers down pressing F5. Fortunately, conditional breakpoints make that easier.

Conditional Breakpoints

Conditional breakpoints allow setting conditions which dictate when the breakpoint will be hit and when it will be skipped over by the debugger.

A conditional breakpoint

Setting a condition can take on a number of forms

  • Breaking when a variable takes on a value
  • Breaking when a variable changes
  • Breaking when a line of code has been hit a certain number of times
  • Breaking when every n times through the loop

Let’s play with some conditional breakpoints.

Looping over data

First thing we need is some data to loop over. For random collections of data there is no better source than government-provided open data. I picked a random dataset from where I live in Canada. “Hexabromocyclododecane in Canadian Municipal Wastewater Treatment Systems” provides readings of whatever hexabromocyclododecane happens to be in the water system. I’m not sure exactly what hexabromocyclododecane is or if I want it but I do know that next time I play scrabble I’m going to use it and I’m going to win.

We’ll make use of the handy CsvHelper library to parse the CSV file (which I cleaned up a little bit manually before parsing).

using CsvHelper;
using System;
using System.IO;
using CsvHelper;
using System;
using System.IO;
namespace LargeDataLoop
{
    class Program
    {
        static void Main(string[] args)
        {
            using(var reader = new               StreamReader(@"HBCDBiosolids_EN_FR.csv"))
            using (var csv = new CsvReader(reader))
            {
                var records = csv.GetRecords();
                foreach (var record in records)
                    Console.WriteLine(record.CONCENTRATION_FOUND);
            }
        }
    }
}

This code does a great job of loading the file, looping over the records and printing out the concentrations.

Let’s introduce a bug and see how we can find it. Those concentrations take on values between 0 and 135. Let’s introduce a few records which have insane values. Now we can add a conditional breakpoint in the code to catch these

Adding a conditional breakpoint for when values are out of the expected range

Sure enough on the next pass through the debugger caught the large value

Breakpoint on the large value

You might also notice, in that screenshot, that I’ve turned on the heads up display to show the value for the field inline.

OzCode also provides some handy shortcuts for setting conditional breakpoints. If we click through the magic wand icon then we get to a prepopulated conditional breakpoint dialog

Conditional breakpoint prepopulated with the current value for record.CONCENTRATION_FOUND

This magic wand trick works even if you click into a collection and search for a specific value.

Time Travel Debugging

Time travel debugging allows us to jump ahead and back in a collection and preview what the results will be. There is a similarly named feature in Visual Studio Enterprise but it works by recording a full session and letting you jump around in it. OzCode’s version let’s you debug without ever executing the code.

To demo this let’s update our code to do something a bit more exciting than printing out the concentrations of hexabromocyclododecane. We’d like to discard values which seem out to lunch. Statistics give us a pretty way to do that by calculating the standard deviation and discarding any values which exist outside of a specific z-score.

using (var reader = new StreamReader(@"HBCDBiosolids_EN_FR.csv"))
using (var csv = new CsvReader(reader))
{
     var records = csv.GetRecords<Record>().ToList();
     var mean = records.Sum(x => x.CONCENTRATION_FOUND) / records.Count();
     var standardDeviation = Math.Sqrt(records.Sum(x => Math.Pow(x.CONCENTRATION_FOUND - mean, 2) / records.Count()));
     var cleanRecords = new List();
     foreach (var record in records)
     {
         var zScore = Convert.ToDouble(record.CONCENTRATION_FOUND - mean) / standardDeviation;
         if (Math.Abs(zScore) > 2)
             Console.WriteLine($"{record.CONCENTRATION_FOUND} - {zScore}");
         else
             cleanRecords.Add(record);
     }
}

Here we calculate a z-score and discard anything outside of 2 deviations above and below the mean. Assuming normally distributed data that should mean that about 95% of the data is within this range.

To see the power of forward time travel debugging set a breakpoint on the loop line.

Forward time travel

You can now click through or scroll through the records and preview what the value will be for each of the entries in the collection. After choosing the iteration, the Heads up Display updates to show the value for the current iteration. You can hover over variables to see their values in the selected future iteration.

Need help finding just the right iteration to jump to? By clicking on Reveal (that’s the star) OzCode will surface that part of the object so you can see it easily in the element list.

The Reveal
Surfacing the CONCENTRATION_FOUND in the record list

OzCode provides some great tools for debugging large collections by allowing you to search for a particular problematic value and preview what’s going to happen. The collection debugging tools in OzCode are my personal favorite feature and I’m sure you’ll love it too!