This is an archive of the discontinued LLVM Phabricator instance.

[RFC][lldb] Use PC from the deepest consecutive inlined frame
Needs ReviewPublic

Authored by ntesic on Apr 19 2022, 3:24 AM.

Details

Summary

Please, consider LLDB behaviour in the case where we have inlined functions, explained below.

(lldb) bt
* thread #1, name = 'inline', stop reason = signal SIGSEGV
  * frame #0: 0x00000000004005e4 inline`print_info(node=0x00007ffcd1425cd8) at inline.c:16:32
    frame #1: 0x0000000000400645 inline`main [inlined] func1 at inline.c:28:3
    frame #2: 0x000000000040060f inline`main [inlined] func2 at inline.c:35
    frame #3: 0x000000000040060f inline`main at inline.c:39
    frame #4: 0x00007fab30fdb7b3 libc.so.6`__libc_start_main + 243
    frame #5: 0x000000000040050e inline`_start + 46

Parents of the inlined frames (#2 and #3) have PC which points to the beginning of the inlined sequence (0x40060f)
regardless of where the current PC is in the same inlined sequence (backtrace above).
Because of the inlining, PC register value is the same for frames #1, #2 and #3.
However, if we use LLDB GetPC() interface, we would get the frame PC value as is in the backtrace,
which could be misleading.

GDB just shows PC value for the first inlined frame (output below).

(gdb) bt
#0  0x00000000004005e4 in print_info (node=0x7fff7f41bc18) at double.c:16
#1  0x0000000000400645 in func1 () at double.c:28
#2  func2 () at double.c:35
#3  main () at double.c:39

Is the explained LLDB behaviour (different from GDB) expected?

If not, please, consider the solution proposed by this patch.
When doing stack unwinding of inlined functions, we start from the inlined frame and iterate through parents,
recursively, until we find the non inlined frame. During that process, we create StackFrame instances
for each frame and we are using GetParentOfInlinedScope hook to get needed info.
This patch proposes using PC from the deepest inlined frame, for all consecutive inlined parents (recursively)
and the first non inlined (where the inlined code is located).

After this patch the backtrace from the example above has the following form

* thread #1, name = 'double', stop reason = signal SIGSEGV
  * frame #0: 0x00000000004005e4 double`print_info(node=0x00007fff7f41bc18) at double.c:16:32
    frame #1: 0x0000000000400645 double`main [inlined] func1 at double.c:28:3
    frame #2: 0x0000000000400645 double`main [inlined] func2 at double.c:35:3
    frame #3: 0x0000000000400645 double`main at double.c:39:1
    frame #4: 0x00007f03adf0a7b3 libc.so.6`__libc_start_main + 243
    frame #5: 0x000000000040050e double`_start + 46

Any feedback is welcome, thanks!

Diff Detail

Event Timeline

ntesic created this revision.Apr 19 2022, 3:24 AM
Herald added a project: Restricted Project. · View Herald TranscriptApr 19 2022, 3:24 AM
ntesic requested review of this revision.Apr 19 2022, 3:24 AM

Please, consider LLDB behaviour in the case where we have inlined functions, explained below.

(lldb) bt
 * thread #1, name = 'inline', stop reason = signal SIGSEGV
   * frame #0: 0x00000000004005e4 inline`print_info(node=0x00007ffcd1425cd8) at inline.c:16:32
     frame #1: 0x0000000000400645 inline`main [inlined] func1 at inline.c:28:3
     frame #2: 0x000000000040060f inline`main [inlined] func2 at inline.c:35
     frame #3: 0x000000000040060f inline`main at inline.c:39
     frame #4: 0x00007fab30fdb7b3 libc.so.6`__libc_start_main + 243
     frame #5: 0x000000000040050e inline`_start + 46

Parents of the inlined frames (#2 and #3) have PC which points to the beginning of the inlined sequence (0x40060f)
regardless of where the current PC is in the same inlined sequence (backtrace above).
Because of the inlining, PC register value is the same for frames #1, #2 and #3.
However, if we use LLDB GetPC() interface, we would get the frame PC value as is in the backtrace,
which could be misleading.

This is expected behavior. We backup the PC to match the start address of the inlined function's start address in each case. So the PC is 0x0000000000400645 for frame #1, but the location in the code that falls through to "func1" is actually 0x000000000040060f. So this is actually more accurate from a code perspective. We get this information because we know the address ranges of all inline functions and we always correctly calculate the PC values for the last instruction of the current function ("func2" for frame #2 for example). I am not sure if GDB tracks the information the same way so GDB might not be able to represent this correctly. Many inline functions calls when chained together can end up with the same PC value, so for frames #2 and #3, there were no instructions between the fall through from "main" to "func2" so the PC doesn't actually have to change.

So I would vote to not change anything as I think what LLDB shows is more correct.