Hey everyone,
For my first post I want to write about the Visual Studio debugger. Debugging tends to be a lone wolf activity, and most of the things I learned about the Visual Studio debugger came from random glances at a co-workers screen, doing a double take, and shouting “You can do that???” to the annoyance of everyone else in the room. Hence, I want to introduce some techniques which I picked up the same way, in the hope that I can shave off some time off your Edit-Compile-Debug cycle. The explanations are terse, as these things are time-savers and I want to do the same. They’re also aimed at people who have some basic knowledge, for example watching a value or stepping through code.
Changing a value in the watch window
Sometimes you want to test your code to see if it handles certain values. The slow way is to write a test and compile/run it. The fast way is to run it and just change whatever value is already going in.
To do this, load up the value in the watch window, and double click on the entry in the “value” column. You should now be able to edit the value, ENTER to commit. The changed value should now be displayed in red.
This works on numerical and pointer types.
Changing a type in the watch window
While the debugger is good at what it does, we still more than it does. For example, maybe we’re watching a value of type A, while we know we are dealing with inherited class B that derives from A. If we want to inspect B’s values in the debugger, we have two options. The slow way is to edit the code to downcast from A to B, compile, run and load this in the watch window. The fast way is to edit the declaration directly in the watch window.
To do this, load up the variable declared as type A in the watch window. Double-click on the name column, and again you will be faced with an input field. The following are allowed as far as I know:
- Casting using ()
- taking an address using &
- expression building
For example, here’s what we could do with a plain old void pointer:
Inspecting an array in the watch window
Ever been annoyed VS will only show you the first value of an array? It’s not really it’s fault, because it can’t trivially know the actual size it’s playing safe. Luckily we often do know, and we can tell him! Just add a “, $elementcount” to the declaration in the name column, and you can view all elements. Combined with the expressions above, we can easily inspect a particular subset of an array, for example every element from the second to the end.
Re-running or skipping code by dragging the instruction pointer
Intermittent bugs are annoying. It can take a lot of time to catch one in the debugger, and it’s easy to to miss some critical information before you’ve stepped over the critical code and you’re stuck with trying to reproduce again. Sometimes, you wish you could just drag that giant yellow arrow around. And, guess what? You can!
This is what VS tells us when we hover over the instruction pointer:
Try it out. You can skip instructions, which can be useful if you know they’re going to crash, or take a lot of time. You can also drag it back to re-run a part of an algorithm if you’re working back from a result. BUT BEWARE. Functions can have side-effects, and your results will of course be different than an ordinary run. I shot myself in the foot once or twice with this, but it’s amazingly useful. You can only do this within the scope of a function body, of course.
Changing code at runtime with edit and continue
Compile times are annoying, and even runtime can add time overhead while debugging because you still have to reproduce before you can test a solution. Sometimes, you wish you could just change the code while you’re paused in the debugger, hit F5, and see if it works. And sometimes, it does! For this to work, you need to compile with the /ZI flag enabled (IDE : C/C++ -> General -> Debug Information Format). Not only can you change a function body, you can also have limited success adding functions and classes.
But it’s also a bit flaky, and I only recommend it if your project takes a long time to link, or it’s difficult to reproduce. In any case, make sure to do a full compile and recheck that your changes do what they were doing with edit and compile.
This technique hits the sweet spot whenever you find yourself changing a value often in the watch window (eg every frame), but cope with that annoyance to avoid a compile.
Conclusion
Having a quick Edit-Compile-Debug cycle is essential for productivity, and knowing your debugger can sometimes eliminate Compile and even parts of Debug. An added advantage is that you’ll be sprinkling less debug code around. However, with techniques that are a bit unorthodox, your results can be *flaky* (especially with big codebases with a lot of dependencies), so you’re even more responsible for double checking your fixes fix.
If anyone has another cool technique, make sure to leave it in the comments!