Arduino Extended GDB Debugging for Visual Studio 2017 and 2019 - In Brief

Here's an overview of what Visual Micro GDB Debug offers

Gdb Debugging can be used with any sketch and project without modification, also with all Arduino example projects, and with projects consisting of multiple .ino files.

Note IconNote:

If you have created Visual Micro projects using gdb debugging before (prior to 25th July 2016), then you have to adapt your existing .vcxproj project file as described in  this post on the Visual Micro website.

 

Setting breakpoints

Breakpoints can be set at runtime without rebuilding the sketch. Up to 3 breakpoints can be set at a time.

To set a breakpoint, click on the code line and press F9. A red dot next to the line indicates the breakpoint.

Breakpoint set


If the sketch stops at a given breakpoint, a yellow arrow appears and indicates the next line that will be executed:

Breakpoint with code halted

To continue execution, press F5. Instead of simply continuing the sketch, you can also use Stepping to execute the code step by step, see next section.

Note IconNote:

Setting more than 3 breakpoints will make the debugging session unstable. This is due to a limitation in the board's CPU, because every breakpoint requires a special breakpoint register to be used, of which 4 exist. One is required internally for single stepping, leaving 3 for breakpoints.

The Breakpoints Window

The Breakpoints window gives an overview over all breakpoints currently set. It helps in finding, deleting and disabling breakpoints that are distributed over the several source files of the project

BP Window

Stepping

One of the most common debugging procedures is stepping. Stepping is executing code one line at a time. You can start stepping if the code is halted at a breakpoint.
There are multiple ways of stepping:

"Step Over"
Keyboard: F11
If the line contains a function call, Step Over executes the called function entirely, then halts at the first line of code inside the calling function. Otherwise, Step Into executes the next statement.
"Step Into"
Keyboard: F10
If the line contains a function call, Step Into enters this function, then halts at the first line of code inside the function. Otherwise, Step Into executes the next statement.
"Step Out"
Keyboard: Shift+F11
Step Out resumes execution of your code until the function returns, then breaks at the return point in the calling function.
You can continue uninterrupted execution of your code by pressing F5.

Data Tips

Data tips are a convenient way of watching the current value of a variable. Simply hover the mouse over a variable and its current value will be displayed.

Data Tip


The pin symbol allows to keep the data tip open if you move the mouse away from the variable. The value shown by the data tip shows will be updated if code execution returns to this code line.

Pinned Data Tip


You can also use the data tip to change a variable's value on the board. Simply click into the value field and enter a new value.

Useful Debugging Windows

Visual Studio/Visual Micro offer a complete set of useful windows that help you with your debugging tasks

The Watch Window

Visual Studio lets you open up to four Watch Windows, which all work in the same way

Open Watch Windows with Debug > Windows > Watch > Watch 1 through Watch 4

Watch Window

The Watch Window is used to view variables of your choice continuously. After opening a Watch Window, enter a variable name or expression in the left column of the watch window and its value will be shown right beneath it. You can also change a variable's value by overtyping it in the watch window.
Variables that are not accessible at the current code location will remain in the watch window, but will not be displayed

The Locals Window

Open the Locals window with Debug > Windows > Locals

The Locals window is like an automatic Watch window that displays the local variables in your current function.

It can be used like a Watch window (see above).

The Autos Window

Open the Autos window with Debug > Windows > Autos

The Autos window is like an automatic Watch window that displays variables used in the current statement and a few statement before.

It can be used like a Watch window (see above).

The Immediate Window

Open the Immediate Window with Debug > Windows > Immediate

The Immediate Window is a command line window that lets you enter any kind of C++ expressions that will be evaluated if you press Enter, and its result will be displayed..

Such expressions can be simple variable names, as well as complex expressions. However, you cannot use function calls to your code in these expressions.

Immediate Window

The Call Stack Window

Open this Window with Debug > Windows > Call Stack

The Call Stack window shows you, the sequence of function calls that lead to the current code location.

Let's assume that Setup() called ProcessLights(), and , ProcessLights() called SetNewState(), where you have set a breakpoint.

Then the Call Stack will look like this:

Call Stack Window

If you use one of the other Windows, like Autos, Locals, Watch, or Disassembly, then you can double click on one of the calling functions in the Call Stack Window, and the other windows will get access to the variables in that function. However, the current program position will not change, indicated by the yellow arrow in the call stack.

The Command Window

Open this Window with View > Other Windows > Command Window

The Command window offers a command line interface that allows you to communicate directly with the gdb debugger that works in the background.

All commands must start with "Debug.MIDebugExec"

You can learn about gdb's commands by entering Debug.MIDebugExec help.

An example of how the Command window looks like:

Command Window

The Disassembly Window

The Disassembly Window shows the assembly language view of your code.

Open this Window with Debug > Windows > Disassembly.

This window is only useful for special cases where you want to go this deep into the inner workings of your program.

Disassembly Window

The Registers Window

Open this Window with Debug > Windows > Registers.

The Registers Window shows the current contents of the processor's registers. Values in red have changed since the last stepping or breakpoint stop.

This window is only useful for special cases where you want to go this deep into the inner workings of your program, probably in conjunction with a Disassembly window (see below)

Registers Window

Disabling Optimization

Before you start debugging your code, you should switch off code optimization.
Optimization is a build step that results in more compact and faster code, it deletes parts of the code, or rearranges its order in program memory.

This impedes debugging, and leads to undesired behavior: Single stepping may jump up and down, variables can't be watched and so on.

Therefore, you should switch off optimization, which you can do selectively for the source files you want to debug, or for different depths of all files.

All Code Files:

To disable optimization for all code files for debugging, the menu options can also be used on the Debugging Menu when Debug: Hardware is selected.

These are visible in the below picture, and should be used sparingly as the size of the compiled code may increase significantly if enabling for cores and libraries.


Per Source Code File:

You disable debugging by adding this statement to your source file:

    #if _VM_DEBUG_GDB
    #pragma GCC optimize ("O0")
    #endif
 
All code lines below this statement - and only in this source file - will be exempt from optimization.

 If you want to switch on optimization for later parts of your source file, then you can put the following lines instead:

     #if _VM_DEBUG_GDB
    #pragma GCC push_options
    #pragma GCC optimize ("O0")
    #endif
 
Then you are able to switch optimization back on further down in your source file with this statement:

    #if _VM_DEBUG_GDB
    #pragma GCC pop_options
    #endif

The lines above only affect the Debug configuration of your project. If you switch to Release - which is not intended for debugging -, then optimization is always on.
(Read more about configuration here)