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 intfind_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.