Created attachment 48190 [details] Bug reproducer. Download the attached file, then: tar -xf gcc-ranges-bugs.tar.xz cd gcc-ranges-bugs make The problem is described in the included README file, the contents of which I will include below. -- * Overview + The issues are present in all compilations, but v3 uses what I think might be the "best" debug flags. + Tested on GCC 9.2.0 and GCC from master branch (2020-02-05). + These bugs were encountered when trying to improve is-stmt support within GDB. + This bug report has a bit of history. Originally there was a GCC patch here: https://gcc.gnu.org/ml/gcc-patches/2019-10/msg01459.html and a reply here: https://gcc.gnu.org/ml/gcc-patches/2019-10/msg01459.html This led to a GDB patch here: https://gcc.gnu.org/ml/gcc-patches/2019-10/msg01459.html which in turn led to a different GDB patch (adding is-stmt support), here: https://gcc.gnu.org/ml/gcc-patches/2019-10/msg01459.html + As suggested in the 2019-10 GCC follow up, I have tried compiling with =-gno-statement-frontiers -gno-variable-location-views=, and currently the debug experience is better, but if I hack GDB to "fix" the bugs below while reading in the DWARF then I get just as good a debug experience with GCC's default flags. + In the included Makefile I build the test 4 times, version 3 is possibly the most interesting, as this explicitly switches on what I think are the "best" features for location tracking. However, the same bugs are present in v1 and v2 (see Makefile for flag differences). Version 4 compiles with the =-gno-*= flags above, and is included for reference. As I've already said, these bugs are not present there. + All of the analysis below references the output files =test-v3=, =dissas-v3=, =dwarf-v3=, and =lines-v3=. * Issue 1 + The function =tree_check= is inlined 3 times. + The first =DW_TAG_inlined_subroutine= looks like this: #+BEGIN_EXAMPLE <2><8db>: Abbrev Number: 38 (DW_TAG_inlined_subroutine) <8dc> DW_AT_abstract_origin: <0x9cc> <8e0> DW_AT_entry_pc : 0x400545 <8e8> Unknown AT value: 2138: 3 <8e9> DW_AT_ranges : 0x30 <8ed> DW_AT_call_file : 1 <8ee> DW_AT_call_line : 52 <8ef> DW_AT_call_column : 10 <8f0> DW_AT_sibling : <0x92f> #+END_EXAMPLE This references some =.debug_ranges= data that looks like this: #+BEGIN_EXAMPLE 00000030 0000000000400545 0000000000400545 (start == end) 00000030 0000000000400549 0000000000400553 00000030 0000000000400430 0000000000400435 00000030 <End of list> #+END_EXAMPLE + Notice that =0x400545= is /not/ part of the range for the inlined function as the range ==0x400545= to =0x400545= is an empty range, containing no bytes (see DWARF-4, 2.17.3: Non-Contiguous Address Ranges). + The =DW_AT_entry_pc= points to =0x400545=. + DWARF-4, 2.18: Entry Address, says: #+BEGIN_QUOTE Any debugging information entry describing an entity that has a range of code addresses, which includes compilation units, module initialization, subroutines, ordinary blocks, try/catch blocks, and the like, may have a DW_AT_entry_pc attribute to indicate the first executable instruction within that range of addresses. #+END_QUOTE I believe this means that the =DW_AT_entry_pc= should reference an address that is within the range of addresses associated with the inlined subroutine. + The particular problem this causes for GDB is that the decoded line table (see =lines-v3=) includes: #+BEGIN_EXAMPLE ./step-and-next-inline.h:[++] step-and-next-inline.h 32 0x400545 3 x step-and-next-inline.h 34 0x400545 4 x #+END_EXAMPLE GDB then stops for these lines (that are part of the inlined function, at an address that is not part of the inlined functions address range. + An idea solution for GDB would be to include the address =0x400545= within the address range of the inlined function, and when I earlier said I hacked GDB to "fix" this bug, this is what I did, extending the range entry starting at =0x400549= to instead start at =0x400545=. * Issue 2 + Looking at the other end of the range now, there's a range =0x400549= to =0x400553=, again according to 'DWARF-4, 2.17.3: Non-Contiguous Address Ranges' anything at =0x400553= is not within this range. + Looking at the decoded line table (see =lines-v3=) we see this: #+BEGIN_EXAMPLE ./step-and-next-inline.h:[++] step-and-next-inline.h 32 0x400545 3 x step-and-next-inline.h 34 0x400545 4 x ./step-and-next-inline.cc:[++] step-and-next-inline.cc 50 0x400545 5 ./step-and-next-inline.h:[++] step-and-next-inline.h 34 0x400549 6 step-and-next-inline.h 34 0x40054b 7 step-and-next-inline.h 36 0x400553 8 x step-and-next-inline.h 37 0x400553 9 x step-and-next-inline.h 37 0x400553 10 ./step-and-next-inline.cc:[++] step-and-next-inline.cc 52 0x400553 11 step-and-next-inline.cc 52 0x400556 12 #+END_EXAMPLE + Notice that lines 36 and 37 of step-and-next-inline.h are listed (with is-stmt = true) as being at =0x400553=. This is upsets GDB quite a bit as stopping at these addresses reports a line from the inilned function, but, as this is outside of the range associated with the inlined function, GDB doesn't understand that we are in the inlined frame. + This miss-placing of lines 36 and 37 is repeated for all three inlined instances. + I think the ideal solution for GDB would be to include that address within the range, or if that's not possible, or not appropriate, don't have those lines marked as 'is-stmt = true', then at least GDB would not try to stop their. When I said earlier I hacked GDB to "fix" this issue, this is what I did, I extended the upper bound for all three inline functions by 3 bytes (to cover one more instruction).
Hi, I use a newer binutils versions FWIW, and buit GCC-10 from a few days ago using those binutils. $ readelf -version GNU readelf (GNU Binutils) 2.32 Copyright (C) 2019 Free Software Foundation, Inc. This program is free software; you may redistribute it under the terms of the GNU General Public License version 3 or (at your option) any later version. This program has absolutely no warranty. instead of #+BEGIN_EXAMPLE <2><8db>: Abbrev Number: 38 (DW_TAG_inlined_subroutine) <8dc> DW_AT_abstract_origin: <0x9cc> <8e0> DW_AT_entry_pc : 0x400545 <8e8> Unknown AT value: 2138: 3 <8e9> DW_AT_ranges : 0x30 <8ed> DW_AT_call_file : 1 <8ee> DW_AT_call_line : 52 <8ef> DW_AT_call_column : 10 <8f0> DW_AT_sibling : <0x92f> #+END_EXAMPLE I see: <2><8b3>: Abbrev Number: 38 (DW_TAG_inlined_subroutine) <8b4> DW_AT_abstract_origin: <0x9a4> <8b8> DW_AT_entry_pc : 0x401165 <8c0> DW_AT_GNU_entry_view: 0 <8c1> DW_AT_ranges : 0x30 <8c5> DW_AT_call_file : 1 <8c6> DW_AT_call_line : 52 <8c7> DW_AT_call_column : 10 <8c8> DW_AT_sibling : <0x907> But as you can see there is a view number where the range begins, but there is no view number where the range ends. I am not sure if there are any view numbers when the inline has multiple ranges, (I dont remember) Therefore I try to chamge gdb to ignore is-stmt breakpoints which are at the end of the inline block. I am not sure if an entirely new DWARF version is necessary, or just a new AT value: like 2139 or so, to give us an idea if the is-stmt is per its view in the subroutine or in the calling program. Bernd.
Sorry for including the wrong DWARF dump output in the bug report. I too had seen the DW_AT_GNU_entry_view using a more recent binutils. When you pose the question: I am not sure if there are any view numbers when the inline has multiple ranges, (I dont remember) I think you're asking do we get DW_AT_GNU_entry_view when we have multiple ranges. And the answer is yes, this case already has multiple ranges (it has a DW_AT_ranges, which contains multiple ranges), and this is super confusing, because each range could, I guess, could have a different view number for its start, right? It seems that, when a debug entity has a single range defined within the entity then we need a start and end view number. When a debug entity makes use of separate range information, each range will need its own start and end view number. If the above requires new DWARF extensions, then GCC might just decide to try and make the best with the version of DWARF it currently has available.
(In reply to Andrew Burgess from comment #2) > Sorry for including the wrong DWARF dump output in the bug report. I too > had seen the DW_AT_GNU_entry_view using a more recent binutils. > NP. > When you pose the question: > > I am not sure if there are any view numbers when the inline has multiple > ranges, (I dont remember) > > I think you're asking do we get DW_AT_GNU_entry_view when we have multiple > ranges. And the answer is yes, this case already has multiple ranges (it > has a DW_AT_ranges, which contains multiple ranges), and this is super > confusing, because each range could, I guess, could have a different view > number for its start, right? > I am talking about the end of the range each of the subranges They should have view numbers, gdb should note the view number when parsing the line program. I think we need an DW_AT_GNU_exit_view, you know, young jedi :-) Bernd.
Can you please approve my patch now? https://sourceware.org/pipermail/gdb-patches/2020-April/167385.html Thanks Bernd.
Bernd, Wouldn't DW_AT_GNU_exit_view be attached to the DW_TAG_inlined_subroutine though? So, as in this case, one subroutine has 2 ranges, but would then have one end view number. I don't understand why each range wouldn't need its own view number?
Right, #+BEGIN_EXAMPLE 00000030 0000000000400545 0000000000400545 (start == end) 00000030 0000000000400549 0000000000400553 00000030 0000000000400430 0000000000400435 00000030 <End of list> #+END_EXAMPLE I dont see any view numbers here, but I would need them.
> I don't understand why each range wouldn't need its own view number? Each of the sub ranges end PC can be an exit point. At least how I see it. Please have a look at my patch. It adds each of the ranges end address to the end address list, and there are two cases, one where only one range is there, and one where multiple ranges are there. then the end PC addresses are used to modify the is-stmt bits of the corresponding line entries if there are any. Those cannot be used for break points per line number, possibly. But I dont care. Thanks Bernd.
Bernd, Please could you keep discussion of GDB patches to the GDB mailing list unless it is required to move this bug forward. In this bug I make the claim that the DWARF GCC produces is not correct. So far I don't think you are disagreeing with that. You are, I think, proposing that we should add range end view numbers to the range table. I'm not disagreeing that this would solve the problem. However, Right now I don't think such a feature is currently in the DWARF standard, maybe it exists as part of a DWARF proposal? Or maybe as a GCC extension? I don't know. The question I'm asking to the wider GCC community would be, given the (I claim) current incorrect DWARF, should GCC be changed to produce something better using existing standard DWARF? Should a new GCC DWARF extension be invented? I certainly don't claim to have the answer here, but hopefully someone else might have a concrete proposal.
Andrew, please update the reproducer, and explain in more detail what you would like to be changed. I still do not understand your idea. But I try hard to do so. Please be patient with me. Bernd.
Bernd, Not a problem, always happy to expand on things. This might get a little long, but hopefully it should give you an idea what I think is wrong. I have not updated the reproducer, I don't know why I would need to do that, as the existing example demonstrates the issues. My original comment did use an older version of binutils though, which is why I saw =<8e8> Unknown AT value: 2138: 3=, which was unfortunate. I'm now using binutils 2.34. To reduce the length of this post I'm only looking at the first inlined subroutine, this is the worst offender, the issues with the other two are just repeats of this one. From the reproducer I'm only looking at the *-v3 output files. Here's the DWARF for the first inlined subroutine: #+BEGIN_EXAMPLE <2><8dd>: Abbrev Number: 38 (DW_TAG_inlined_subroutine) <8de> DW_AT_abstract_origin: <0x9ce> <8e2> DW_AT_entry_pc : 0x401165 <8ea> DW_AT_GNU_entry_view: 0 <8eb> DW_AT_ranges : 0x30 <8ef> DW_AT_call_file : 1 <8f0> DW_AT_call_line : 52 <8f1> DW_AT_call_column : 10 <8f2> DW_AT_sibling : <0x931> #+END_EXAMPLE It references the range information at offset 0x30, which can be seen here, the duplication is just an objdump oddity, the ranges we care about are the first four lines starting with =00000030=: #+BEGIN_EXAMPLE Contents of the .debug_ranges section: Offset Begin End 00000000 0000000000401160 00000000004011ca 00000000 0000000000401040 0000000000401045 00000000 <End of list> 00000030 0000000000401165 0000000000401165 (start == end) 00000030 0000000000401169 0000000000401173 00000030 0000000000401040 0000000000401045 00000030 <End of list> 00000030 0000000000401165 0000000000401165 (start == end) 00000030 0000000000401169 0000000000401173 00000030 0000000000401040 0000000000401045 00000030 <End of list> 00000070 0000000000401160 00000000004011ca 00000070 0000000000401040 0000000000401045 00000070 0000000000401050 0000000000401065 00000070 <End of list> #+END_EXAMPLE Next, here's a snippet of disassembler, this covers two of the ranges from the above, the empty one =0x401165= to =0x401165=, and then =0x401169= to =0x401173=. The range =0x401040= to =0x401045= I'm ignoring for now, it's just some out of line cold code and not important for this discussion: #+BEGIN_EXAMPLE 0000000000401160 <_Z13get_alias_setP4tree>: 401160: 48 85 ff test %rdi,%rdi 401163: 74 4b je 4011b0 <_Z13get_alias_setP4tree+0x50> 401165: 48 83 ec 08 sub $0x8,%rsp 401169: 8b 07 mov (%rdi),%eax 40116b: 85 c0 test %eax,%eax 40116d: 0f 85 cd fe ff ff jne 401040 <_Z13get_alias_setP4tree.cold> 401173: 8b 47 04 mov 0x4(%rdi),%eax 401176: 83 f8 01 cmp $0x1,%eax 401179: 74 28 je 4011a3 <_Z13get_alias_setP4tree+0x43> 40117b: 8b 07 mov (%rdi),%eax 40117d: 85 c0 test %eax,%eax #+END_EXAMPLE Finally, a snippet of the line table covering the same disassembler region as above: #+BEGIN_EXAMPLE test-v3: file format elf64-x86-64 Contents of the .debug_line section: CU: ./step-and-next-inline.cc: File name Line number Starting address View Stmt step-and-next-inline.cc 50 0x401160 x step-and-next-inline.cc 51 0x401160 1 x step-and-next-inline.cc 54 0x401160 2 ./step-and-next-inline.h:[++] step-and-next-inline.h 32 0x401165 x step-and-next-inline.h 34 0x401165 1 x ./step-and-next-inline.cc:[++] step-and-next-inline.cc 50 0x401165 2 ./step-and-next-inline.h:[++] step-and-next-inline.h 34 0x401169 step-and-next-inline.h 34 0x40116b step-and-next-inline.h 36 0x401173 x step-and-next-inline.h 37 0x401173 1 x step-and-next-inline.h 37 0x401173 2 ./step-and-next-inline.cc:[++] step-and-next-inline.cc 52 0x401173 3 step-and-next-inline.cc 52 0x401176 #+END_EXAMPLE Here is what I think GCC could have produced for the range information instead: #+BEGIN_EXAMPLE 00000030 0000000000401165 0000000000401176 00000030 0000000000401040 0000000000401045 00000030 <End of list> #+END_EXAMPLE The changes are: (1) The empty range at =0x401165= is now folded into the following range, which coincidentally started at the very next instruction anyway. (2) The range previously ending at =0x401173= now ends at =0x401176=, extending by one instruction. The reasoning is that: (1) The DW_AT_entry_pc is now within a range attributed to the inlined subroutine. (2) All lines marked as is-stmt for the subroutine, are within a range attributed to the subroutine. A debugger stopping at one of those lines will now understand that: (a) It is at a particular line, and (b) It is within a particular subroutine. Notice that at =0x401173= we have lines from both the inlined subroutine, and from the enclosing calller function. However, it is the inlined callee that is marked is-stmt for this address, I believe this should mean this address is included in the range for the inlined subroutine. While I'm writing this I want to take a moment to address views. I'm still reading and learning about views, but I think they are relevant here. Non of the proposed view specifications I have yet seen go far enough to address applying the views technique to ranges. Each range could potentially be improved by having an entry and exit view. When a DW_TAG_inlined_subroutine references an external ranges list, then each sub-range in that list needs to be able to have its own entry and exit view. Currently, in the example above we have one DW_AT_GNU_entry_view for 3 ranges (though one is empty and can be ignored). Without some really clever engineering, one entry view is never going to be correct for multiple sub-ranges. Further, I've seen no mention of exit views anywhere, and I think they would also be needed. With the addition of these two features we would be able to support (I believe) fully merged callers and callees, with lines marked as is-stmt from both the caller and callee appearing at the same address. For now, without this, I think GCC needs to restrict itself when inlining. When an address represents a line from both the caller and callee, then that address should only be is-stmt true for EITHER lines from the caller, or lines from the callee. If the address is is-stmt true for a line from the caller, then the address should NOT be within the callee's range(s), and if the address is is-stmt true for a line from the callee, then the address MUST be within the callee's range(s). On a final note. These are just my personal thoughts from the perspective of a debug consumer, though I use words like "must" or "should" above this reflects my thoughts on how I believe the debug should appear, and is not an attempt to prescribe how GCC should be. I know there are limitations to what GCC can achieve, and also, I could be totally wrong in my understanding of DWARF. I'm always happy to be corrected!
Andrew, (In reply to Andrew Burgess from comment #10) > Further, I've seen no mention of exit views anywhere, and I think they > would also be needed. > Yes, that is also my idea, when I say the dwarf2 spec needs to be fixed. > With the addition of these two features we would be able to support (I > believe) fully merged callers and callees, with lines marked as > is-stmt from both the caller and callee appearing at the same address. > > For now, without this, I think GCC needs to restrict itself when > inlining. When an address represents a line from both the caller and > callee, then that address should only be is-stmt true for EITHER lines > from the caller, or lines from the callee. If the address is is-stmt > true for a line from the caller, then the address should NOT be within > the callee's range(s), and if the address is is-stmt true for a line > from the callee, then the address MUST be within the callee's > range(s). > I tried to do something similar, in my original gcc-patch, which was unfortunately not accepted. But what I learned from writing the patch is that gcc cannot easily tell if a range will be empty or not. That is because the assembler does emit the line info and the views, and the assembler decides how many bytes if any a certain construct will take in the binary representation. That is why this empty range appears, because that was supposed to be the start of the inline function, but instructions from the function prologue were moved in there. What causes the problem is the is-stmt line info which is the only remaining thing from the original plan. But on the other hand, it is not a big issue for gdb to ignore those artefacts, when they rarely occur. My gdb-patch does this, by changing the is-stmt bit of this line info. > On a final note. These are just my personal thoughts from the > perspective of a debug consumer, though I use words like "must" or > "should" above this reflects my thoughts on how I believe the debug > should appear, and is not an attempt to prescribe how GCC should > be. I know there are limitations to what GCC can achieve, and also, I > could be totally wrong in my understanding of DWARF. I'm always happy > to be corrected! Yeah, that is true for myself as well. Ping, Alexandre Oliva, are you still with us? I would be curious to know what you think, about this how should we proceed? Thanks Bernd.
> But what I learned from writing the patch is that gcc cannot > easily tell if a range will be empty or not. That is because > the assembler does emit the line info and the views, > and the assembler decides how many bytes if any a certain > construct will take in the binary representation. > > That is why this empty range appears, because that was > supposed to be the start of the inline function, but > instructions from the function prologue were moved in there. I think there's some miss-communication here. In the first paragraph you say the assembler is responsible for deciding the binary representation. I'd agree with that, in the GNU toolchain this is gas. Then in the second paragraph you say the assembler is performing instruction reordering. Now I don't know every gas optimisation, so I'm not saying gas doesn't do that, but it's not common, and it certainly isn't what's happening in this case. The instruction motion is entirely within GCC. I'll offer evidence for this below. > What causes the problem is the is-stmt line info which > is the only remaining thing from the original plan. And here in lies the GCC bug (or one of them). GCC has performed instruction motion, and GCC has left behind its marker for the is-stmt line. I make no claim about how easy this is to fix, or if (given GCC's code base) its even reasonably possible to consider fixing it, but that doesn't mean it isn't a GCC issue. > But on the other hand, it is not a big issue for gdb to > ignore those artefacts, when they rarely occur. GDB already ignores empty ranges. The DWARF standard specifically allows empty ranges and states that they are to be ignored. But that isn't the artefact you're talking about, you mean the is-stmt markers. The problem is, the markers are placed at an address, given that every address eventually contains an instruction[1] eventually, how can GDB tell if one marker was left by accident or not? > My gdb-patch does this, by changing the is-stmt bit of > this line info. Yes, and I think with this discussion we are getting closer to understanding the work around you want to introduce to GDB, something along the lines of: - On versions XXX -> YYY of GCC, - An is-stmt true marker, - At the same address as an empty range, that is - Associated with an inline function instance, should - Be ignored. It's this kind of specific identification of the bug you're working around that (for me at least) is holding back your GDB patch. But, we shouldn't make a choice between work around the GCC bug in GDB _or_ fix the bug in GCC. The ideal solution (for me) would be, fix GCC _and_ add a work around into GDB targeting the known broken versions of GCC, then we get the best of both worlds. --- To back up my claim that this is a GCC issue, you can generate the assembler file test-v3.s from the reproducer. The .debug_ranges looks like this: .section .debug_ranges,"",@progbits .Ldebug_ranges0: .quad .LFB21 .quad .LHOTE0 .quad .LFSB21 .quad .LCOLDE0 .quad 0 .quad 0 .quad .LBB8 .quad .LBE8 .quad .LBB12 .quad .LBE12 .quad .LBB17 .quad .LBE17 .quad 0 .quad 0 The empty range is the first range in the second sequence, so this one: .quad .LBB8 .quad .LBE8 If we then find this in the assembler we see: .LBB8: .LBI8: .file 2 "step-and-next-inline.h" .loc 2 32 1 is_stmt 1 view .LVU3 .LBB9: .loc 2 34 3 view .LVU4 .LBE9: .LBE8: .loc 1 50 1 is_stmt 0 view .LVU5 subq $8, %rsp .cfi_def_cfa_offset 16 Notice, no assembler instructions (but there are assembler directives) between LBB8 and LBE8. I agree 100% with your diagnosis that this is likely due to instruction reordering, but I disagree with your analysis that this is done in the assembler and that therefore GCC has no visibility of this. --- [1] I know, not _literally_ every address, but you get the point.
Hi Andrew, You are right about the instruction re-ordering, that is done in a compiler pass, which simply re-orders RTL instruction lists. But I think when the code motion happens, we just have no easy access to the range markers. And it may be the case that this is-stmt location mentions a register value that is indeed the parameter of the inline function, so it may be no instruction but only a side note, to the debugger, that a certain value would be already here available in a certain register. Also that is only a vague guess, since although I did already a number of gcc patches, I learn new things each time :-) Bernd.
(In reply to Andrew Burgess from comment #0) > + This bug report has a bit of history. Originally there was a GCC > patch here: > https://gcc.gnu.org/ml/gcc-patches/2019-10/msg01459.html > and a reply here: > https://gcc.gnu.org/ml/gcc-patches/2019-10/msg01459.html > > This led to a GDB patch here: > https://gcc.gnu.org/ml/gcc-patches/2019-10/msg01459.html > which in turn led to a different GDB patch (adding is-stmt > support), here: > https://gcc.gnu.org/ml/gcc-patches/2019-10/msg01459.html > one correction here: the reply to the oct 2019 patch was here: https://gcc.gnu.org/legacy-ml/gcc-patches/2019-11/msg01771.html The follow up GDB patch probably this one is meant: https://sourceware.org/pipermail/gdb-patches/2019-November/162747.html The different patch adding is-stmt probably this one is meant: https://sourceware.org/pipermail/gdb-patches/2019-December/164224.html
While there are certainly empty subranges that could be avoided, there are also completely empty subroutines, which cannot be avoided without losing the ability to inspect the procedure variable at this location. I have one example of that class here: $ cat empty-inline.cc /* This testcase is part of GDB, the GNU debugger. Copyright 2021 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* PR 25987 */ struct MyClass; struct ptr { MyClass* get() { return t; } /* line 21 */ MyClass* t; }; struct MyClass { void call(); }; void MyClass::call() { *(volatile char*)(nullptr) = 1; /* line 26 */ } static void intermediate(ptr p) { p.get()->call(); /* line 29 */ } int main() { intermediate(ptr{new MyClass}); } /* EOF */ $ gcc -g -Og empty-inline.cc The empty inline I mean is ptr::get(): In the debug info this function looks like this: <2><11b>: Abbrev Number: 17 (DW_TAG_inlined_subroutine) <11c> DW_AT_abstract_origin: <0x189> <120> DW_AT_entry_pc : 0x40113f <128> DW_AT_GNU_entry_view: 2 <129> DW_AT_low_pc : 0x40113f <131> DW_AT_high_pc : 0x0 <139> DW_AT_call_file : 1 <13a> DW_AT_call_line : 29 <13b> DW_AT_call_column : 18 <13c> DW_AT_sibling : <0x14e> <3><140>: Abbrev Number: 18 (DW_TAG_formal_parameter) <141> DW_AT_abstract_origin: <0x197> <145> DW_AT_location : 0x16 (location list) <149> DW_AT_GNU_locviews: 0x14 [...] <2><197>: Abbrev Number: 22 (DW_TAG_formal_parameter) <198> DW_AT_name : (indirect string, offset: 0x2e): this <19c> DW_AT_type : <0x98> <1a0> DW_AT_artificial : 1 [...] Contents of the .debug_loclists section: [...] 00000014 v000000000000002 v000000000000004 location view pair 00000016 v000000000000002 v000000000000004 views at 00000014 for: 000000000040113f 000000000040113f (DW_OP_implicit_pointer: <0x109> 0) 00000020 <End of list> when stepping through the empty subroutine the "this" pointer and in particular the value of the symbol "t" would be accessible, like this: (gdb) s intermediate (p=...) at empty-inline.cc:29 29 p.get()->call(); /* line 29 */ (gdb) s ptr::get (this=<synthetic pointer>) at empty-inline.cc:21 21 MyClass* get() { return t; } /* line 21 */ (gdb) p t $1 = (MyClass *) 0x416c20 This loclist is of course only accessible when we accept an empty range as something which may exist at all, thus with my latest GDB-patch applied: [0/4] https://sourceware.org/pipermail/gdb-patches/2021-May/179367.html [1/4] https://sourceware.org/pipermail/gdb-patches/2021-May/179368.html [2/4] https://sourceware.org/pipermail/gdb-patches/2021-May/179370.html [3/4] https://sourceware.org/pipermail/gdb-patches/2021-May/179369.html Currently gdb ignores these subroutine as a kind of nonsense.