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.
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.
If the sketch stops at a given breakpoint, a yellow arrow appears and indicates
the next line that will be executed:
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.
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
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.
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.
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
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.
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:
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:
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.
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)
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)