-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[lldb] Improve unwinding for discontinuous functions #111409
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
256 changes: 256 additions & 0 deletions
256
lldb/test/Shell/Unwind/Inputs/basic-block-sections-with-dwarf.s
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
# An example of a function which has been split into two parts. Roughly | ||
# corresponds to this C code. | ||
# int baz() { return 47; } | ||
# int bar() { return foo(0); } | ||
# int foo(int flag) { return flag ? bar() : baz(); } | ||
# int main() { return foo(1); } | ||
# The function bar has been placed "in the middle" of foo. | ||
|
||
.text | ||
|
||
.type baz,@function | ||
baz: | ||
.cfi_startproc | ||
movl $47, %eax | ||
retq | ||
.cfi_endproc | ||
.Lbaz_end: | ||
.size baz, .Lbaz_end-baz | ||
|
||
.type foo,@function | ||
foo: | ||
.cfi_startproc | ||
pushq %rbp | ||
.cfi_def_cfa_offset 16 | ||
.cfi_offset %rbp, -16 | ||
movq %rsp, %rbp | ||
.cfi_def_cfa_register %rbp | ||
subq $16, %rsp | ||
movl %edi, -8(%rbp) | ||
cmpl $0, -8(%rbp) | ||
je foo.__part.2 | ||
jmp foo.__part.1 | ||
.cfi_endproc | ||
.Lfoo_end: | ||
.size foo, .Lfoo_end-foo | ||
|
||
foo.__part.1: | ||
.cfi_startproc | ||
.cfi_def_cfa %rbp, 16 | ||
.cfi_offset %rbp, -16 | ||
callq bar | ||
movl %eax, -4(%rbp) | ||
jmp foo.__part.3 | ||
.Lfoo.__part.1_end: | ||
.size foo.__part.1, .Lfoo.__part.1_end-foo.__part.1 | ||
.cfi_endproc | ||
|
||
bar: | ||
.cfi_startproc | ||
# NB: Decrease the stack pointer to make the unwind info for this function | ||
# different from the surrounding foo function. | ||
subq $24, %rsp | ||
.cfi_def_cfa_offset 32 | ||
xorl %edi, %edi | ||
callq foo | ||
addq $24, %rsp | ||
.cfi_def_cfa %rsp, 8 | ||
retq | ||
.cfi_endproc | ||
.Lbar_end: | ||
.size bar, .Lbar_end-bar | ||
|
||
foo.__part.2: | ||
.cfi_startproc | ||
.cfi_def_cfa %rbp, 16 | ||
.cfi_offset %rbp, -16 | ||
callq baz | ||
movl %eax, -4(%rbp) | ||
jmp foo.__part.3 | ||
.Lfoo.__part.2_end: | ||
.size foo.__part.2, .Lfoo.__part.2_end-foo.__part.2 | ||
.cfi_endproc | ||
|
||
foo.__part.3: | ||
.cfi_startproc | ||
.cfi_def_cfa %rbp, 16 | ||
.cfi_offset %rbp, -16 | ||
movl -4(%rbp), %eax | ||
addq $16, %rsp | ||
popq %rbp | ||
.cfi_def_cfa %rsp, 8 | ||
retq | ||
.Lfoo.__part.3_end: | ||
.size foo.__part.3, .Lfoo.__part.3_end-foo.__part.3 | ||
.cfi_endproc | ||
|
||
|
||
.globl main | ||
.type main,@function | ||
main: | ||
.cfi_startproc | ||
movl $1, %edi | ||
callq foo | ||
retq | ||
.cfi_endproc | ||
.Lmain_end: | ||
.size main, .Lmain_end-main | ||
|
||
.section .debug_abbrev,"",@progbits | ||
.byte 1 # Abbreviation Code | ||
.byte 17 # DW_TAG_compile_unit | ||
.byte 1 # DW_CHILDREN_yes | ||
.byte 37 # DW_AT_producer | ||
.byte 8 # DW_FORM_string | ||
.byte 19 # DW_AT_language | ||
.byte 5 # DW_FORM_data2 | ||
.byte 17 # DW_AT_low_pc | ||
.byte 1 # DW_FORM_addr | ||
.byte 85 # DW_AT_ranges | ||
.byte 35 # DW_FORM_rnglistx | ||
.byte 116 # DW_AT_rnglists_base | ||
.byte 23 # DW_FORM_sec_offset | ||
.byte 0 # EOM(1) | ||
.byte 0 # EOM(2) | ||
.byte 2 # Abbreviation Code | ||
.byte 46 # DW_TAG_subprogram | ||
.byte 0 # DW_CHILDREN_no | ||
.byte 17 # DW_AT_low_pc | ||
.byte 1 # DW_FORM_addr | ||
.byte 18 # DW_AT_high_pc | ||
.byte 1 # DW_FORM_addr | ||
.byte 3 # DW_AT_name | ||
.byte 8 # DW_FORM_string | ||
.byte 0 # EOM(1) | ||
.byte 0 # EOM(2) | ||
.byte 3 # Abbreviation Code | ||
.byte 46 # DW_TAG_subprogram | ||
.byte 1 # DW_CHILDREN_yes | ||
.byte 85 # DW_AT_ranges | ||
.byte 35 # DW_FORM_rnglistx | ||
.byte 64 # DW_AT_frame_base | ||
.byte 24 # DW_FORM_exprloc | ||
.byte 3 # DW_AT_name | ||
.byte 8 # DW_FORM_string | ||
.byte 0 # EOM(1) | ||
.byte 0 # EOM(2) | ||
.byte 4 # Abbreviation Code | ||
.byte 5 # DW_TAG_formal_parameter | ||
.byte 0 # DW_CHILDREN_no | ||
.byte 2 # DW_AT_location | ||
.byte 24 # DW_FORM_exprloc | ||
.byte 3 # DW_AT_name | ||
.byte 8 # DW_FORM_string | ||
.byte 73 # DW_AT_type | ||
.byte 19 # DW_FORM_ref4 | ||
.byte 0 # EOM(1) | ||
.byte 0 # EOM(2) | ||
.byte 5 # Abbreviation Code | ||
.byte 36 # DW_TAG_base_type | ||
.byte 0 # DW_CHILDREN_no | ||
.byte 3 # DW_AT_name | ||
.byte 8 # DW_FORM_string | ||
.byte 62 # DW_AT_encoding | ||
.byte 11 # DW_FORM_data1 | ||
.byte 11 # DW_AT_byte_size | ||
.byte 11 # DW_FORM_data1 | ||
.byte 0 # EOM(1) | ||
.byte 0 # EOM(2) | ||
.byte 0 # EOM(3) | ||
|
||
.section .debug_info,"",@progbits | ||
.Lcu_begin0: | ||
.long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit | ||
.Ldebug_info_start0: | ||
.short 5 # DWARF version number | ||
.byte 1 # DWARF Unit Type | ||
.byte 8 # Address Size (in bytes) | ||
.long .debug_abbrev # Offset Into Abbrev. Section | ||
.byte 1 # Abbrev [1] DW_TAG_compile_unit | ||
.asciz "Hand-written DWARF" # DW_AT_producer | ||
.short 29 # DW_AT_language | ||
.quad 0 # DW_AT_low_pc | ||
.byte 1 # DW_AT_ranges | ||
.long .Lrnglists_table_base0 # DW_AT_rnglists_base | ||
.byte 2 # Abbrev [2] DW_TAG_subprogram | ||
.quad baz # DW_AT_low_pc | ||
.quad .Lbaz_end # DW_AT_high_pc | ||
.asciz "baz" # DW_AT_name | ||
.byte 2 # Abbrev [2] DW_TAG_subprogram | ||
.quad bar # DW_AT_low_pc | ||
.quad .Lbar_end # DW_AT_high_pc | ||
.asciz "bar" # DW_AT_name | ||
.byte 3 # Abbrev [3] DW_TAG_subprogram | ||
.byte 0 # DW_AT_ranges | ||
.byte 1 # DW_AT_frame_base | ||
.byte 86 | ||
.asciz "foo" # DW_AT_name | ||
.byte 4 # Abbrev [4] DW_TAG_formal_parameter | ||
.byte 2 # DW_AT_location | ||
.byte 145 | ||
.byte 120 | ||
.asciz "flag" # DW_AT_name | ||
.long .Lint-.Lcu_begin0 # DW_AT_type | ||
.byte 0 # End Of Children Mark | ||
.byte 2 # Abbrev [2] DW_TAG_subprogram | ||
.quad main # DW_AT_low_pc | ||
.quad .Lmain_end # DW_AT_high_pc | ||
.asciz "main" # DW_AT_name | ||
.Lint: | ||
.byte 5 # Abbrev [5] DW_TAG_base_type | ||
.asciz "int" # DW_AT_name | ||
.byte 5 # DW_AT_encoding | ||
.byte 4 # DW_AT_byte_size | ||
.byte 0 # End Of Children Mark | ||
.Ldebug_info_end0: | ||
|
||
.section .debug_rnglists,"",@progbits | ||
.long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length | ||
.Ldebug_list_header_start0: | ||
.short 5 # Version | ||
.byte 8 # Address size | ||
.byte 0 # Segment selector size | ||
.long 2 # Offset entry count | ||
.Lrnglists_table_base0: | ||
.long .Ldebug_ranges0-.Lrnglists_table_base0 | ||
.long .Ldebug_ranges1-.Lrnglists_table_base0 | ||
.Ldebug_ranges0: | ||
.byte 6 # DW_RLE_start_end | ||
.quad foo | ||
.quad .Lfoo_end | ||
.byte 6 # DW_RLE_start_end | ||
.quad foo.__part.1 | ||
.quad .Lfoo.__part.1_end | ||
.byte 6 # DW_RLE_start_end | ||
.quad foo.__part.2 | ||
.quad .Lfoo.__part.2_end | ||
.byte 6 # DW_RLE_start_end | ||
.quad foo.__part.3 | ||
.quad .Lfoo.__part.3_end | ||
.byte 0 # DW_RLE_end_of_list | ||
.Ldebug_ranges1: | ||
.byte 6 # DW_RLE_start_end | ||
.quad baz | ||
.quad .Lbaz_end | ||
.byte 6 # DW_RLE_start_end | ||
.quad bar | ||
.quad .Lbar_end | ||
.byte 6 # DW_RLE_start_end | ||
.quad foo.__part.1 | ||
.quad .Lfoo.__part.1_end | ||
.byte 6 # DW_RLE_start_end | ||
.quad foo.__part.2 | ||
.quad .Lfoo.__part.2_end | ||
.byte 6 # DW_RLE_start_end | ||
.quad foo.__part.3 | ||
.quad .Lfoo.__part.3_end | ||
.byte 6 # DW_RLE_start_end | ||
.quad foo | ||
.quad .Lfoo_end | ||
.byte 6 # DW_RLE_start_end | ||
.quad main | ||
.quad .Lmain_end | ||
.byte 0 # DW_RLE_end_of_list | ||
.Ldebug_list_header_end0: | ||
|
||
.section ".note.GNU-stack","",@progbits |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
--- !minidump | ||
Streams: | ||
- Type: ThreadList | ||
Threads: | ||
- Thread Id: 0x000074DD | ||
Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0010000000000033000000000000000000000002020100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040109600000000000100000000000000000000000000000068E7D0C8FF7F000068E7D0C8FF7F000097E6D0C8FF7F000010109600000000000000000000000000020000000000000088E4D0C8FF7F0000603FFF85C77F0000F00340000000000080E7D0C8FF7F000000000000000000000000000000000000E0034000000000007F0300000000000000000000000000000000000000000000801F0000FFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF252525252525252525252525252525250000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | ||
Stack: | ||
Start of Memory Range: 0x00007FFFC8D0E000 | ||
Content: DEADBEEFBAADF00D | ||
- Type: Exception | ||
Thread ID: 0x000074DD | ||
Exception Record: | ||
Exception Code: 0x0000000B | ||
Thread Context: 00000000 | ||
- Type: SystemInfo | ||
Processor Arch: AMD64 | ||
Processor Level: 6 | ||
Processor Revision: 15876 | ||
Number of Processors: 40 | ||
Platform ID: Linux | ||
CSD Version: 'Linux 3.13.0-91-generic' | ||
CPU: | ||
Vendor ID: GenuineIntel | ||
Version Info: 0x00000000 | ||
Feature Info: 0x00000000 | ||
... |
65 changes: 65 additions & 0 deletions
65
lldb/test/Shell/Unwind/basic-block-sections-with-dwarf-static.test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Test unwind info for functions which have been split into two or more parts. | ||
# In particular, check that the address ranges of these plans are correct, as | ||
# overly large ranges have caused a bug in the past. | ||
|
||
# REQUIRES: lld, target-x86_64 | ||
|
||
# RUN: llvm-mc -triple=x86_64-pc-linux -filetype=obj \ | ||
# RUN: %S/Inputs/basic-block-sections-with-dwarf.s > %t.o | ||
# RUN: ld.lld %t.o -o %t | ||
## NB: This minidump exists only as a receptacle for the object file built | ||
## above. This is a workaround for the fact that "image show-unwind" does not | ||
## work without a Process object. | ||
# RUN: yaml2obj %S/Inputs/linux-x86_64.yaml > %t.core | ||
# RUN: %lldb -c %t.core %t -o "image load --file %t --slide 0" -s %s -o exit | \ | ||
# RUN: FileCheck --implicit-check-not="UNWIND PLANS" %s | ||
|
||
image show-unwind -n foo | ||
# CHECK: UNWIND PLANS for {{.*}}`foo | ||
# | ||
# CHECK: Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI' | ||
# CHECK-NEXT: Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI' | ||
|
||
# CHECK: Assembly language inspection UnwindPlan: | ||
# CHECK-NEXT: This UnwindPlan originally sourced from assembly insn profiling | ||
# CHECK-NEXT: This UnwindPlan is sourced from the compiler: no. | ||
# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: yes. | ||
# CHECK-NEXT: This UnwindPlan is for a trap handler function: no. | ||
# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 6-0x0000000000000019) | ||
|
||
# CHECK: eh_frame UnwindPlan: | ||
# CHECK-NEXT: This UnwindPlan originally sourced from eh_frame CFI | ||
# CHECK-NEXT: This UnwindPlan is sourced from the compiler: yes. | ||
# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: no. | ||
# CHECK-NEXT: This UnwindPlan is for a trap handler function: no. | ||
# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 6-0x0000000000000019) | ||
# CHECK-NEXT: row[0]: 0: CFA=rsp +8 => rip=[CFA-8] | ||
# CHECK-NEXT: row[1]: 1: CFA=rsp+16 => rbp=[CFA-16] rip=[CFA-8] | ||
# CHECK-NEXT: row[2]: 4: CFA=rbp+16 => rbp=[CFA-16] rip=[CFA-8] | ||
# CHECK-EMPTY: | ||
|
||
image show-unwind -n foo.__part.1 | ||
# CHECK: UNWIND PLANS for {{.*}}`foo.__part.1 | ||
|
||
## As of this writing (Oct 2024), the async unwind plan is "assembly insn | ||
## profiling", which isn't ideal, because this "function" does not follow the | ||
## standard ABI. We end up choosing this plan because the eh_frame unwind plan | ||
## looks like the unwind plan for a regular function without the prologue | ||
## information. | ||
# CHECK: Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI' | ||
|
||
# CHECK: Assembly language inspection UnwindPlan: | ||
# CHECK-NEXT: This UnwindPlan originally sourced from assembly insn profiling | ||
# CHECK-NEXT: This UnwindPlan is sourced from the compiler: no. | ||
# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: yes. | ||
# CHECK-NEXT: This UnwindPlan is for a trap handler function: no. | ||
# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 25-0x0000000000000023) | ||
|
||
# CHECK: eh_frame UnwindPlan: | ||
# CHECK-NEXT: This UnwindPlan originally sourced from eh_frame CFI | ||
# CHECK-NEXT: This UnwindPlan is sourced from the compiler: yes. | ||
# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: no. | ||
# CHECK-NEXT: This UnwindPlan is for a trap handler function: no. | ||
# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 25-0x0000000000000023) | ||
# CHECK-NEXT: row[0]: 0: CFA=rbp+16 => rbp=[CFA-16] rip=[CFA-8] | ||
# CHECK-EMPTY: |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is because
image show-unwind
wouldn't use the eh_frame address range without it (it requires a resolved address).BTW. This would have been easier to investigate (and test) if this command used the actual cached unwind plans instead of creating them from scratch. This way I was left puzzled as to unwind went off track even though the unwind plans looked (mostly) reasonable. What would you say to:
a) adding an option to use/print the cached plans
b) making this option the default
c) deleting the non-cached option entirely?