20.3.3 Debugging Strategies for Optimized Code
The optimizer can introduce challenges for debugging code which increase with higher levels of optimization. For the best debugging experience, make sure that the ELF/DWARF object file format is selected (as opposed to COFF) whenever possible. The output file format is selected in MPLAB X IDE under Project Properties>XC16 (Global Options) (see figure below).
The DWARF symbol language has advanced features that allow the compiler
to provide more information when optimized. The compiler will be able to describe how
object values flow in and out of registers, even if the register changes. For this
reason, ELF/DWARF at -O1
will provide a reasonably smooth debugging
experience with some optimizations.
Earlier (Using Optimizations) we mentioned some of the effects of optimizing code. Some of these effects will prevent the debugger from displaying a value (the variable is not needed and has been optimized away) or placing a breakpoint (the line of code does not exist).
Sometimes it is more effective to debug in a mixed C-assembly display, or to follow the C code along with the Program Memory view.
Additionally, MPLAB XC C compilers provide a couple of tools that can be helpful.
- A variant of the standard C assertion mechanism can be used to
return to the debugger at certain execution points. The macro
__conditional_software_breakpoint(X)
is available inassert.h
and can be used to halt the debugger. - The optimization level can be set on a function-by-function basis.
For example, to make debugging of a particular function easier while still
optimizing the rest of the application, define the function like
this:
Tau __attribute__((optimize(1))) fn(...){}
A declaration of this form will override the current global optimization setting on a function-by-function basis.
- The MPLAB X IDE defines the pre-processor symbol
__DEBUG
when a debug build is being produced. This can be useful for enabling code changes to support debugging only when actually debugging. For example, conditionally changing the optimization level for a given function can be implemented with a simple macro:#ifdef __DEBUG #define DBG_OPTIMIZE(X) __attribute__((optimize(X))) #else #define DBG_OPTIMIZE(X) /* not debugging */ #endif Tau DBG_OPTIMIZE(1) fn(...) { }
Multiple attributes can be combined. This is valid:
void __attribute__((interrupt)) DBG_OPTIMIZE(1) _T1Interrupt(void) { }