I'm using GNU Arm Embedded Toolchain 10.3 to cross-compile for a Cortex-M0+ target, and using the accompanying GDB to debug.
I want to set a breakpoint on the following line of code:
if (mPendingMessageIndex == 0)
{
// Start a new pending message
--> mPendingMessage[0] = extracted;
// Check for running status first
if (isChannelMessage(getTypeFromStatusByte(mRunningStatus_RX)))
{
// Only these types allow Running Status
This is line 818 of the file, so in GDB, while stopped at a nearby line, I type:
b 818
And I get the response:
Breakpoint 2 at 0x2000cc1c: file lib/arduino_midi_library/src/MIDI.hpp, line 1090.
Now, this is an inlined template method, and I appreciate that inlining can complicate matters, even when compiling (as I am) with -Og -ggdb3
.
But line 1090 is in a separate method that's not invoked from here.
So... really?
Disassembly of the ELF file shows this:
if (mPendingMessageIndex == 0)
2000bed0: 6e63 ldr r3, [r4, #100] ; 0x64
2000bed2: 2b00 cmp r3, #0
2000bed4: d000 beq.n 2000bed8 <midi::MidiInterface<usbMidi::usbMidiTransport, midi::DefaultSettings, midi::DefaultPlatform>::parse()+0x3c>
2000bed6: e0b6 b.n 2000c046 <midi::MidiInterface<usbMidi::usbMidiTransport, midi::DefaultSettings, midi::DefaultPlatform>::parse()+0x1aa>
lib/arduino_midi_library/src/MIDI.hpp:818
mPendingMessage[0] = extracted;
--> 2000bed8: 335b adds r3, #91 ; 0x5b
2000beda: 54e1 strb r1, [r4, r3]
lib/arduino_midi_library/src/MIDI.hpp:821
if (isChannelMessage(getTypeFromStatusByte(mRunningStatus_RX)))
2000bedc: 3b02 subs r3, #2
2000bede: 5ce2 ldrb r2, [r4, r3]
Line 818 corresponds to address 0x2000bed8
so in GDB I type:
b *0x2000bed8
And get:
Breakpoint 3 at 0x2000bed8: file lib/arduino_midi_library/src/MIDI.hpp, line 818.
So GDB can map the address to the line, but not vice versa. It's a major pain in the arse, but I can work with it - so I do.
But curiosity gnaws at me - so after a lot more rooting around I eventually find in the GDB sources:
static int
find_line_common (struct linetable *l, int lineno,
int *exact_match, int start)
{
...
/* Ignore non-statements. */
if (!item->is_stmt)
continue;
So it looks like GDB will only set breakpoints on lines where the is_stmt
flag is set in the DWARF info.
And sure enough, readelf -wL
tells me that that line is not marked as is_stmt
(indicated by 'x'):
USB-MIDI.h 130 0x2000beca 1 x
USB-MIDI.h 130 0x2000beca 2
lib/arduino_midi_library/src/MIDI.hpp:
MIDI.hpp 812 0x2000beca 3
MIDI.hpp 815 0x2000bed0
MIDI.hpp 815 0x2000bed2
--> MIDI.hpp 818 0x2000bed8
MIDI.hpp 821 0x2000bedc
MIDI.hpp 1228 0x2000bee0 x
readelf
also tells me that no other line of code corresponds to address 0x2000bed8
, and no other address corresponds to line MIDI.hpp:818
.
So... I guess this boils down to two questions.
- Why would GCC not mark such an unambiguous location as
is_stmt
? - If GCC can be expected to produce such output, why does GDB so naively ignore lines not marked
is_stmt
?
And the bonus question is:
- Is there a way around this?
NB: I've experimented with compiler switches such as -gstatement-frontiers and -ginline-points, and they have no effect.
EDITED TO ADD:
readelf
further indicates that large swathes of templated code from this file lack is_stmt
flags. Literally hundreds of lines, corresponding to scores of statements, mostly consisting of switch statements and assignments, producing 250 line table records in the readelf
output - of which ten are marked is_stmt
, and precisely all of those correspond to inline expansions of helper functions.