diff options
author | gulfemsavrun <gulfem@google.com> | 2025-07-21 12:51:27 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-21 12:51:27 +0300 |
commit | e94bc16b8e12a64ff28aedc58ee6e95e1f9d6f4b (patch) | |
tree | bd191ea217fe40e652e01c144e0c9a9f7c81ccc4 | |
parent | 9311f3814bf139aee08014dbeeaa4c59ac2ae6f8 (diff) | |
download | llvm-e94bc16b8e12a64ff28aedc58ee6e95e1f9d6f4b.zip llvm-e94bc16b8e12a64ff28aedc58ee6e95e1f9d6f4b.tar.gz llvm-e94bc16b8e12a64ff28aedc58ee6e95e1f9d6f4b.tar.bz2 |
[llvm-objdump] Add inlined function display support (#142246)
This patch adds the support for displaying inlined functions into
llvm-objdump.
1) It extends the source variable display
support for inlined functions both for ascii and unicode formats.
2) It also introduces a new format called limits-only that only prints a
line for the start and end of an inlined function without line-drawing
characters.
-rw-r--r-- | llvm/docs/CommandGuide/llvm-objdump.rst | 26 | ||||
-rw-r--r-- | llvm/docs/ReleaseNotes.md | 3 | ||||
-rw-r--r-- | llvm/test/tools/llvm-objdump/ELF/ARM/debug-vars-dwarf4.s | 4 | ||||
-rw-r--r-- | llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc | 10 | ||||
-rw-r--r-- | llvm/test/tools/llvm-objdump/X86/debug-inlined-functions.s | 643 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/ObjdumpOpts.td | 16 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/SourcePrinter.cpp | 238 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/SourcePrinter.h | 102 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/llvm-objdump.cpp | 144 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/llvm-objdump.h | 7 |
10 files changed, 1026 insertions, 167 deletions
diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst index c9f0379..aaf38f8 100644 --- a/llvm/docs/CommandGuide/llvm-objdump.rst +++ b/llvm/docs/CommandGuide/llvm-objdump.rst @@ -140,23 +140,29 @@ OPTIONS debug information for stripped binaries. Multiple instances of this argument are searched in the order given. -.. option:: --debuginfod, --no-debuginfod +.. option:: --debug-indent=<width> - Whether or not to try debuginfod lookups for debug binaries. Unless specified, - debuginfod is only enabled if libcurl was compiled in (``LLVM_ENABLE_CURL``) - and at least one server URL was provided by the environment variable - ``DEBUGINFOD_URLS``. + Distance to indent the source-level variable or inlined function display, + relative to the start of the disassembly. Defaults to 52 characters. + +.. option:: --debug-inlined-funcs[=<format>] -.. option:: --debug-vars=<format> + Print the locations of inlined functions alongside disassembly. + ``format`` may be ``ascii``, ``limits-only``, or ``unicode``, defaulting to + ``unicode`` if omitted. + +.. option:: --debug-vars[=<format>] Print the locations (in registers or memory) of source-level variables - alongside disassembly. ``format`` may be ``unicode`` or ``ascii``, defaulting + alongside disassembly. ``format`` may be ``ascii`` or ``unicode``, defaulting to ``unicode`` if omitted. -.. option:: --debug-vars-indent=<width> +.. option:: --debuginfod, --no-debuginfod - Distance to indent the source-level variable display, relative to the start - of the disassembly. Defaults to 52 characters. + Whether or not to try debuginfod lookups for debug binaries. Unless specified, + debuginfod is only enabled if libcurl was compiled in (``LLVM_ENABLE_CURL``) + and at least one server URL was provided by the environment variable + ``DEBUGINFOD_URLS``. .. option:: -j, --section=<section1[,section2,...]> diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index 5591ac6..bb1f88e 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -282,6 +282,9 @@ Changes to the LLVM tools ([#47468](https://github.com/llvm/llvm-project/issues/47468)) * llvm-addr2line now supports a `+` prefix when specifying an address. * Support for `SHT_LLVM_BB_ADDR_MAP` versions 0 and 1 has been dropped. +* llvm-objdump now supports the `--debug-inlined-funcs` flag, which prints the + locations of inlined functions alongside disassembly. The + `--debug-vars-indent` flag has also been renamed to `--debug-indent`. Changes to LLDB --------------------------------- diff --git a/llvm/test/tools/llvm-objdump/ELF/ARM/debug-vars-dwarf4.s b/llvm/test/tools/llvm-objdump/ELF/ARM/debug-vars-dwarf4.s index 69b7489..085f258 100644 --- a/llvm/test/tools/llvm-objdump/ELF/ARM/debug-vars-dwarf4.s +++ b/llvm/test/tools/llvm-objdump/ELF/ARM/debug-vars-dwarf4.s @@ -15,10 +15,10 @@ ## Check that passing the default value for --debug-vars-indent (52) makes no ## change to the output. -# RUN: llvm-objdump %t.o -d --debug-vars --debug-vars-indent=52 | \ +# RUN: llvm-objdump %t.o -d --debug-vars --debug-indent=52 | \ # RUN: FileCheck %s --check-prefix=RAW --strict-whitespace -# RUN: llvm-objdump %t.o -d --debug-vars --debug-vars-indent=30 | \ +# RUN: llvm-objdump %t.o -d --debug-vars --debug-indent=30 | \ # RUN: FileCheck %s --check-prefix=INDENT --strict-whitespace # RUN: llvm-objdump %t.o -d --debug-vars --no-show-raw-insn | \ diff --git a/llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc b/llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc new file mode 100644 index 0000000..a708bc0 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc @@ -0,0 +1,10 @@ +int bar(int x, int y) { + int sum = x + y; + int mul = x * y; + return sum + mul; +} + +int foo(int a, int b) { + int result = bar(a, b); + return result; +} diff --git a/llvm/test/tools/llvm-objdump/X86/debug-inlined-functions.s b/llvm/test/tools/llvm-objdump/X86/debug-inlined-functions.s new file mode 100644 index 0000000..6ed3507 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/X86/debug-inlined-functions.s @@ -0,0 +1,643 @@ +## Generated with this compile command, with the source code in Inputs/debug-inlined-functions.cc: +## clang++ -g -c debug-inlined-functions.cc -O1 -S -o - + +# RUN: llvm-mc -triple=x86_64 %s -filetype=obj -o %t.o + +# RUN: llvm-objdump %t.o -d --debug-inlined-funcs=unicode | \ +# RUN: FileCheck %s --check-prefixes=UNICODE,UNICODE-MANGLED --strict-whitespace + +# RUN: llvm-objdump %t.o -d -C --debug-inlined-funcs | \ +# RUN: FileCheck %s --check-prefixes=UNICODE,UNICODE-DEMANGLED --strict-whitespace + +# RUN: llvm-objdump %t.o -d -C --debug-inlined-funcs=unicode | \ +# RUN: FileCheck %s --check-prefixes=UNICODE,UNICODE-DEMANGLED --strict-whitespace + +# RUN: llvm-objdump %t.o -d -C --debug-inlined-funcs=unicode --debug-indent=30 | \ +# RUN: FileCheck %s --check-prefix=UNICODE-DEMANGLED-INDENT --strict-whitespace + +# RUN: llvm-objdump %t.o -d -C --debug-inlined-funcs=ascii | \ +# RUN: FileCheck %s --check-prefix=ASCII-DEMANGLED --strict-whitespace + +# RUN: llvm-objdump %t.o -d -C --debug-inlined-funcs=limits-only | \ +# RUN: FileCheck %s --check-prefix=LIMITS-ONLY-DEMANGLED + +# RUN: llvm-objdump %t.o -d -C --debug-inlined-funcs=unicode --debug-vars=unicode | \ +# RUN: FileCheck %s --check-prefix=DEBUG-DEMANGLED-ALL --strict-whitespace + +# UNICODE-MANGLED: 0000000000000000 <_Z3barii>: +# UNICODE-DEMANGLED: 0000000000000000 <bar(int, int)>: +# UNICODE-NEXT: 0: 8d 04 3e leal (%rsi,%rdi), %eax +# UNICODE-NEXT: 3: 0f af f7 imull %edi, %esi +# UNICODE-NEXT: 6: 01 f0 addl %esi, %eax +# UNICODE-NEXT: 8: c3 retq +# UNICODE-NEXT: 9: 0f 1f 80 00 00 00 00 nopl (%rax) +# UNICODE-EMPTY: +# UNICODE-MANGLED-NEXT: 0000000000000010 <_Z3fooii>: +# UNICODE-DEMANGLED-NEXT: 0000000000000010 <foo(int, int)>: +# UNICODE-MANGLED-NEXT: ┠─ _Z3barii = inlined into _Z3fooii +# UNICODE-DEMANGLED-NEXT: ┠─ bar(int, int) = inlined into foo(int, int) +# UNICODE-NEXT: 10: 8d 04 3e leal (%rsi,%rdi), %eax ┃ +# UNICODE-NEXT: 13: 0f af f7 imull %edi, %esi ┃ +# UNICODE-NEXT: 16: 01 f0 addl %esi, %eax ┻ +# UNICODE-NEXT: 18: c3 retq + +# UNICODE-DEMANGLED-INDENT: 0000000000000010 <foo(int, int)>: +# UNICODE-DEMANGLED-INDENT-NEXT: ┠─ bar(int, int) = inlined into foo(int, int) +# UNICODE-DEMANGLED-INDENT-NEXT: 10: 8d 04 3e leal (%rsi,%rdi), %eax ┃ +# UNICODE-DEMANGLED-INDENT-NEXT: 13: 0f af f7 imull %edi, %esi ┃ +# UNICODE-DEMANGLED-INDENT-NEXT: 16: 01 f0 addl %esi, %eax ┻ +# UNICODE-DEMANGLED-INDENT-NEXT: 18: c3 retq + +# ASCII-DEMANGLED: 0000000000000010 <foo(int, int)>: +# ASCII-DEMANGLED-NEXT: |- bar(int, int) = inlined into foo(int, int) +# ASCII-DEMANGLED-NEXT: 10: 8d 04 3e leal (%rsi,%rdi), %eax | +# ASCII-DEMANGLED-NEXT: 13: 0f af f7 imull %edi, %esi | +# ASCII-DEMANGLED-NEXT: 16: 01 f0 addl %esi, %eax v +# ASCII-DEMANGLED-NEXT: 18: c3 retq + +# LIMITS-ONLY-DEMANGLED: 0000000000000010 <foo(int, int)>: +# LIMITS-ONLY-DEMANGLED-NEXT: debug-inlined-functions.cc:8:16: bar(int, int) inlined into foo(int, int) +# LIMITS-ONLY-DEMANGLED-NEXT: 10: 8d 04 3e leal (%rsi,%rdi), %eax +# LIMITS-ONLY-DEMANGLED-NEXT: 13: 0f af f7 imull %edi, %esi +# LIMITS-ONLY-DEMANGLED-NEXT: 16: 01 f0 addl %esi, %eax +# LIMITS-ONLY-DEMANGLED-NEXT: debug-inlined-functions.cc:8:16: end of bar(int, int) inlined into foo(int, int) +# LIMITS-ONLY-DEMANGLED-NEXT: 18: c3 retq + +# DEBUG-DEMANGLED-ALL: 0000000000000010 <foo(int, int)>: +# DEBUG-DEMANGLED-ALL-NEXT: ┠─ a = RDI +# DEBUG-DEMANGLED-ALL-NEXT: ┃ ┠─ b = RSI +# DEBUG-DEMANGLED-ALL-NEXT: ┃ ┃ ┠─ bar(int, int) = inlined into foo(int, int) +# DEBUG-DEMANGLED-ALL-NEXT: ┃ ┃ ┃ ┠─ x = RDI +# DEBUG-DEMANGLED-ALL-NEXT: ┃ ┃ ┃ ┃ ┠─ y = RSI +# DEBUG-DEMANGLED-ALL-NEXT: ┃ ┃ ┃ ┃ ┃ ┌─ sum = RAX +# DEBUG-DEMANGLED-ALL-NEXT: 10: 8d 04 3e leal (%rsi,%rdi), %eax ┃ ┃ ┃ ┃ ┃ ╈ +# DEBUG-DEMANGLED-ALL-NEXT: ┃ ┃ ┃ ┃ ┃ ┃ ┌─ b = entry(RSI) +# DEBUG-DEMANGLED-ALL-NEXT: ┃ ┃ ┃ ┃ ┃ ┃ │ ┌─ mul = RSI +# DEBUG-DEMANGLED-ALL-NEXT: 13: 0f af f7 imull %edi, %esi ┃ ┻ ┃ ┃ ┻ ┃ ╈ ╈ +# DEBUG-DEMANGLED-ALL-NEXT: ┃ ┌─ result = RAX +# DEBUG-DEMANGLED-ALL-NEXT: 16: 01 f0 addl %esi, %eax ┃ ╈ ┻ ┻ ┻ ┃ ┃ +# DEBUG-DEMANGLED-ALL-NEXT: 18: c3 retq ┻ ┻ ┻ ┻ + + .file "debug-inlined-functions.cc" + .text + .globl _Z3barii # -- Begin function _Z3barii + .p2align 4 + .type _Z3barii,@function +_Z3barii: # @_Z3barii +.Lfunc_begin0: + .file 0 "debug-inlined-functions.cc" md5 0xf07b869ec4d0996589aa6856ae4e6c83 + .cfi_startproc +# %bb.0: # %entry + #DEBUG_VALUE: bar:x <- $edi + #DEBUG_VALUE: bar:y <- $esi + # kill: def $esi killed $esi def $rsi + # kill: def $edi killed $edi def $rdi + .loc 0 2 15 prologue_end # llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:2:15 + leal (%rsi,%rdi), %eax +.Ltmp0: + #DEBUG_VALUE: bar:sum <- $eax + .loc 0 3 15 # llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:3:15 + imull %edi, %esi +.Ltmp1: + #DEBUG_VALUE: bar:y <- [DW_OP_LLVM_entry_value 1] $esi + #DEBUG_VALUE: bar:mul <- $esi + .loc 0 4 14 # llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:4:14 + addl %esi, %eax +.Ltmp2: + .loc 0 4 3 is_stmt 0 # llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:4:3 + retq +.Ltmp3: +.Lfunc_end0: + .size _Z3barii, .Lfunc_end0-_Z3barii + .cfi_endproc + # -- End function + .globl _Z3fooii # -- Begin function _Z3fooii + .p2align 4 + .type _Z3fooii,@function +_Z3fooii: # @_Z3fooii +.Lfunc_begin1: + .cfi_startproc +# %bb.0: # %entry + #DEBUG_VALUE: foo:a <- $edi + #DEBUG_VALUE: foo:b <- $esi + #DEBUG_VALUE: bar:x <- $edi + #DEBUG_VALUE: bar:y <- $esi + # kill: def $esi killed $esi def $rsi + # kill: def $edi killed $edi def $rdi + .loc 0 2 15 prologue_end is_stmt 1 # llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:2:15 @[ llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:8:16 ] + leal (%rsi,%rdi), %eax +.Ltmp4: + #DEBUG_VALUE: bar:sum <- $eax + .loc 0 3 15 # llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:3:15 @[ llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:8:16 ] + imull %edi, %esi +.Ltmp5: + #DEBUG_VALUE: foo:b <- [DW_OP_LLVM_entry_value 1] $esi + #DEBUG_VALUE: bar:mul <- $esi + .loc 0 4 14 # llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:4:14 @[ llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:8:16 ] + addl %esi, %eax +.Ltmp6: + #DEBUG_VALUE: foo:result <- $eax + .loc 0 9 3 # llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc:9:3 + retq +.Ltmp7: +.Lfunc_end1: + .size _Z3fooii, .Lfunc_end1-_Z3fooii + .cfi_endproc + # -- End function + .section .debug_loclists,"",@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 8 # Offset entry count +.Lloclists_table_base0: + .long .Ldebug_loc0-.Lloclists_table_base0 + .long .Ldebug_loc1-.Lloclists_table_base0 + .long .Ldebug_loc2-.Lloclists_table_base0 + .long .Ldebug_loc3-.Lloclists_table_base0 + .long .Ldebug_loc4-.Lloclists_table_base0 + .long .Ldebug_loc5-.Lloclists_table_base0 + .long .Ldebug_loc6-.Lloclists_table_base0 + .long .Ldebug_loc7-.Lloclists_table_base0 +.Ldebug_loc0: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset + .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 84 # super-register DW_OP_reg4 + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset + .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset + .byte 4 # Loc expr size + .byte 163 # DW_OP_entry_value + .byte 1 # 1 + .byte 84 # super-register DW_OP_reg4 + .byte 159 # DW_OP_stack_value + .byte 0 # DW_LLE_end_of_list +.Ldebug_loc1: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp0-.Lfunc_begin0 # starting offset + .uleb128 .Ltmp2-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 80 # super-register DW_OP_reg0 + .byte 0 # DW_LLE_end_of_list +.Ldebug_loc2: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset + .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 84 # super-register DW_OP_reg4 + .byte 0 # DW_LLE_end_of_list +.Ldebug_loc3: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Lfunc_begin1-.Lfunc_begin0 # starting offset + .uleb128 .Ltmp5-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 84 # super-register DW_OP_reg4 + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp5-.Lfunc_begin0 # starting offset + .uleb128 .Lfunc_end1-.Lfunc_begin0 # ending offset + .byte 4 # Loc expr size + .byte 163 # DW_OP_entry_value + .byte 1 # 1 + .byte 84 # super-register DW_OP_reg4 + .byte 159 # DW_OP_stack_value + .byte 0 # DW_LLE_end_of_list +.Ldebug_loc4: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Lfunc_begin1-.Lfunc_begin0 # starting offset + .uleb128 .Ltmp5-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 84 # super-register DW_OP_reg4 + .byte 0 # DW_LLE_end_of_list +.Ldebug_loc5: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset + .uleb128 .Ltmp6-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 80 # super-register DW_OP_reg0 + .byte 0 # DW_LLE_end_of_list +.Ldebug_loc6: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp5-.Lfunc_begin0 # starting offset + .uleb128 .Lfunc_end1-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 84 # super-register DW_OP_reg4 + .byte 0 # DW_LLE_end_of_list +.Ldebug_loc7: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp6-.Lfunc_begin0 # starting offset + .uleb128 .Lfunc_end1-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 80 # super-register DW_OP_reg0 + .byte 0 # DW_LLE_end_of_list +.Ldebug_list_header_end0: + .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 37 # DW_FORM_strx1 + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 114 # DW_AT_str_offsets_base + .byte 23 # DW_FORM_sec_offset + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 37 # DW_FORM_strx1 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 115 # DW_AT_addr_base + .byte 23 # DW_FORM_sec_offset + .ascii "\214\001" # DW_AT_loclists_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 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 122 # DW_AT_call_all_calls + .byte 25 # DW_FORM_flag_present + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .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 34 # DW_FORM_loclistx + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 34 # DW_FORM_loclistx + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 110 # DW_AT_linkage_name + .byte 37 # DW_FORM_strx1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 32 # DW_AT_inline + .byte 33 # DW_FORM_implicit_const + .byte 1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .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 10 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 122 # DW_AT_call_all_calls + .byte 25 # DW_FORM_flag_present + .byte 110 # DW_AT_linkage_name + .byte 37 # DW_FORM_strx1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 11 # 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 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 12 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 34 # DW_FORM_loclistx + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 13 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 34 # DW_FORM_loclistx + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 14 # Abbreviation Code + .byte 29 # DW_TAG_inlined_subroutine + .byte 1 # DW_CHILDREN_yes + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 88 # DW_AT_call_file + .byte 11 # DW_FORM_data1 + .byte 89 # DW_AT_call_line + .byte 11 # DW_FORM_data1 + .byte 87 # DW_AT_call_column + .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] 0xc:0xc4 DW_TAG_compile_unit + .byte 0 # DW_AT_producer + .short 33 # DW_AT_language + .byte 1 # DW_AT_name + .long .Lstr_offsets_base0 # DW_AT_str_offsets_base + .long .Lline_table_start0 # DW_AT_stmt_list + .byte 2 # DW_AT_comp_dir + .byte 0 # DW_AT_low_pc + .long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc + .long .Laddr_table_base0 # DW_AT_addr_base + .long .Lloclists_table_base0 # DW_AT_loclists_base + .byte 2 # Abbrev [2] 0x27:0x26 DW_TAG_subprogram + .byte 0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + # DW_AT_call_all_calls + .long 77 # DW_AT_abstract_origin + .byte 3 # Abbrev [3] 0x33:0x7 DW_TAG_formal_parameter + .byte 1 # DW_AT_location + .byte 85 + .long 86 # DW_AT_abstract_origin + .byte 4 # Abbrev [4] 0x3a:0x6 DW_TAG_formal_parameter + .byte 0 # DW_AT_location + .long 94 # DW_AT_abstract_origin + .byte 5 # Abbrev [5] 0x40:0x6 DW_TAG_variable + .byte 1 # DW_AT_location + .long 102 # DW_AT_abstract_origin + .byte 5 # Abbrev [5] 0x46:0x6 DW_TAG_variable + .byte 2 # DW_AT_location + .long 110 # DW_AT_abstract_origin + .byte 0 # End Of Children Mark + .byte 6 # Abbrev [6] 0x4d:0x2a DW_TAG_subprogram + .byte 3 # DW_AT_linkage_name + .byte 4 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 119 # DW_AT_type + # DW_AT_external + # DW_AT_inline + .byte 7 # Abbrev [7] 0x56:0x8 DW_TAG_formal_parameter + .byte 6 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 119 # DW_AT_type + .byte 7 # Abbrev [7] 0x5e:0x8 DW_TAG_formal_parameter + .byte 7 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 119 # DW_AT_type + .byte 8 # Abbrev [8] 0x66:0x8 DW_TAG_variable + .byte 8 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .long 119 # DW_AT_type + .byte 8 # Abbrev [8] 0x6e:0x8 DW_TAG_variable + .byte 9 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .long 119 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 9 # Abbrev [9] 0x77:0x4 DW_TAG_base_type + .byte 5 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 10 # Abbrev [10] 0x7b:0x54 DW_TAG_subprogram + .byte 1 # DW_AT_low_pc + .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + # DW_AT_call_all_calls + .byte 10 # DW_AT_linkage_name + .byte 11 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 119 # DW_AT_type + # DW_AT_external + .byte 11 # Abbrev [11] 0x8b:0xa DW_TAG_formal_parameter + .byte 1 # DW_AT_location + .byte 85 + .byte 12 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 119 # DW_AT_type + .byte 12 # Abbrev [12] 0x95:0x9 DW_TAG_formal_parameter + .byte 3 # DW_AT_location + .byte 13 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 119 # DW_AT_type + .byte 13 # Abbrev [13] 0x9e:0x9 DW_TAG_variable + .byte 7 # DW_AT_location + .byte 14 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 8 # DW_AT_decl_line + .long 119 # DW_AT_type + .byte 14 # Abbrev [14] 0xa7:0x27 DW_TAG_inlined_subroutine + .long 77 # DW_AT_abstract_origin + .byte 1 # DW_AT_low_pc + .long .Ltmp6-.Lfunc_begin1 # DW_AT_high_pc + .byte 0 # DW_AT_call_file + .byte 8 # DW_AT_call_line + .byte 16 # DW_AT_call_column + .byte 3 # Abbrev [3] 0xb4:0x7 DW_TAG_formal_parameter + .byte 1 # DW_AT_location + .byte 85 + .long 86 # DW_AT_abstract_origin + .byte 4 # Abbrev [4] 0xbb:0x6 DW_TAG_formal_parameter + .byte 4 # DW_AT_location + .long 94 # DW_AT_abstract_origin + .byte 5 # Abbrev [5] 0xc1:0x6 DW_TAG_variable + .byte 5 # DW_AT_location + .long 102 # DW_AT_abstract_origin + .byte 5 # Abbrev [5] 0xc7:0x6 DW_TAG_variable + .byte 6 # DW_AT_location + .long 110 # DW_AT_abstract_origin + .byte 0 # End Of Children Mark + .byte 0 # End Of Children Mark + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .section .debug_str_offsets,"",@progbits + .long 64 # Length of String Offsets Set + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 21.0.0git (git@github.com:llvm/llvm-project.git eed98e1493414ae9c30596b1eeb8f4a9b260e42)" # string offset=0 +.Linfo_string1: + .asciz "llvm/test/tools/llvm-objdump/X86/Inputs/debug-inlined-functions.cc" # string offset=112 +.Linfo_string2: + .asciz "llvm-project" # string offset=179 +.Linfo_string3: + .asciz "_Z3barii" # string offset=229 +.Linfo_string4: + .asciz "bar" # string offset=238 +.Linfo_string5: + .asciz "int" # string offset=242 +.Linfo_string6: + .asciz "x" # string offset=246 +.Linfo_string7: + .asciz "y" # string offset=248 +.Linfo_string8: + .asciz "sum" # string offset=250 +.Linfo_string9: + .asciz "mul" # string offset=254 +.Linfo_string10: + .asciz "_Z3fooii" # string offset=258 +.Linfo_string11: + .asciz "foo" # string offset=267 +.Linfo_string12: + .asciz "a" # string offset=271 +.Linfo_string13: + .asciz "b" # string offset=273 +.Linfo_string14: + .asciz "result" # string offset=275 + .section .debug_str_offsets,"",@progbits + .long .Linfo_string0 + .long .Linfo_string1 + .long .Linfo_string2 + .long .Linfo_string3 + .long .Linfo_string4 + .long .Linfo_string5 + .long .Linfo_string6 + .long .Linfo_string7 + .long .Linfo_string8 + .long .Linfo_string9 + .long .Linfo_string10 + .long .Linfo_string11 + .long .Linfo_string12 + .long .Linfo_string13 + .long .Linfo_string14 + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad .Lfunc_begin0 + .quad .Lfunc_begin1 +.Ldebug_addr_end0: + .ident "clang version 21.0.0git (git@github.com:llvm/llvm-project.git eed98e1493414ae9c30596b1eeb8f4a9b260e42a)" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td index c3764c6..c97e06f 100644 --- a/llvm/tools/llvm-objdump/ObjdumpOpts.td +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -241,17 +241,23 @@ defm prefix_strip "paths. No effect without --prefix">, MetaVarName<"prefix">; +def debug_indent_EQ : Joined<["--"], "debug-indent=">, + HelpText<"Distance to indent the source-level variable and inlined function display, " + "relative to the start of the disassembly">; + +def debug_inlined_funcs_EQ : Joined<["--"], "debug-inlined-funcs=">, + HelpText<"Print the locations of inlined functions alongside disassembly. " + "Supported formats: ascii, limits-only, and unicode (default)">, + Values<"ascii,limits-only,unicode">; +def : Flag<["--"], "debug-inlined-funcs">, Alias<debug_inlined_funcs_EQ>, AliasArgs<["unicode"]>; + def debug_vars_EQ : Joined<["--"], "debug-vars=">, HelpText<"Print the locations (in registers or memory) of " "source-level variables alongside disassembly. " "Supported formats: ascii, unicode (default)">, - Values<"unicode,ascii">; + Values<"ascii,unicode">; def : Flag<["--"], "debug-vars">, Alias<debug_vars_EQ>, AliasArgs<["unicode"]>; -def debug_vars_indent_EQ : Joined<["--"], "debug-vars-indent=">, - HelpText<"Distance to indent the source-level variable display, " - "relative to the start of the disassembly">; - def x86_asm_syntax_att : Flag<["--"], "x86-asm-syntax=att">, HelpText<"Emit AT&T-style disassembly">; diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp index 3630502..b0ff89d 100644 --- a/llvm/tools/llvm-objdump/SourcePrinter.cpp +++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// // -// This file implements the LiveVariablePrinter and SourcePrinter classes to +// This file implements the LiveElementPrinter and SourcePrinter classes to // keep track of DWARF info as the current address is updated, and print out the -// source file line and variable liveness as needed. +// source file line and variable or inlined function liveness as needed. // //===----------------------------------------------------------------------===// @@ -17,6 +17,7 @@ #include "llvm/ADT/SmallSet.h" #include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Support/FormatVariadic.h" #define DEBUG_TYPE "objdump" @@ -24,7 +25,70 @@ namespace llvm { namespace objdump { -bool LiveVariable::liveAtAddress(object::SectionedAddress Addr) { +bool InlinedFunction::liveAtAddress(object::SectionedAddress Addr) const { + if (!Range.valid()) + return false; + + return Range.LowPC <= Addr.Address && Range.HighPC > Addr.Address; +} + +void InlinedFunction::print(raw_ostream &OS, const MCRegisterInfo &MRI) const { + const char *MangledCallerName = FuncDie.getName(DINameKind::LinkageName); + if (!MangledCallerName) + return; + + if (Demangle) + OS << "inlined into " << demangle(MangledCallerName); + else + OS << "inlined into " << MangledCallerName; +} + +void InlinedFunction::dump(raw_ostream &OS) const { + OS << Name << " @ " << Range << ": "; +} + +void InlinedFunction::printElementLine(raw_ostream &OS, + object::SectionedAddress Addr, + bool IsEnd) const { + bool LiveIn = !IsEnd && Range.LowPC == Addr.Address; + bool LiveOut = IsEnd && Range.HighPC == Addr.Address; + if (!(LiveIn || LiveOut)) + return; + + uint32_t CallFile, CallLine, CallColumn, CallDiscriminator; + InlinedFuncDie.getCallerFrame(CallFile, CallLine, CallColumn, + CallDiscriminator); + const DWARFDebugLine::LineTable *LineTable = + Unit->getContext().getLineTableForUnit(Unit); + std::string FileName; + if (!LineTable->hasFileAtIndex(CallFile)) + return; + if (!LineTable->getFileNameByIndex( + CallFile, Unit->getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName)) + return; + + if (FileName.empty()) + return; + + const char *MangledCallerName = FuncDie.getName(DINameKind::LinkageName); + if (!MangledCallerName) + return; + + std::string CallerName = MangledCallerName; + std::string CalleeName = Name; + if (Demangle) { + CallerName = demangle(MangledCallerName); + CalleeName = demangle(Name); + } + + OS << "; " << FileName << ":" << CallLine << ":" << CallColumn << ": "; + if (IsEnd) + OS << "end of "; + OS << CalleeName << " inlined into " << CallerName << "\n"; +} + +bool LiveVariable::liveAtAddress(object::SectionedAddress Addr) const { if (LocExpr.Range == std::nullopt) return false; return LocExpr.Range->SectionIndex == Addr.SectionIndex && @@ -49,7 +113,24 @@ void LiveVariable::print(raw_ostream &OS, const MCRegisterInfo &MRI) const { printDwarfExpressionCompact(&Expression, OS, GetRegName); } -void LiveVariablePrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) { +void LiveVariable::dump(raw_ostream &OS) const { + OS << Name << " @ " << LocExpr.Range << ": "; +} + +void LiveElementPrinter::addInlinedFunction(DWARFDie FuncDie, + DWARFDie InlinedFuncDie) { + uint64_t FuncLowPC, FuncHighPC, SectionIndex; + if (!InlinedFuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex)) + return; + + DWARFUnit *U = InlinedFuncDie.getDwarfUnit(); + const char *InlinedFuncName = InlinedFuncDie.getName(DINameKind::LinkageName); + DWARFAddressRange Range{FuncLowPC, FuncHighPC, SectionIndex}; + LiveElements.emplace_back(std::make_unique<InlinedFunction>( + InlinedFuncName, U, FuncDie, InlinedFuncDie, Range)); +} + +void LiveElementPrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) { uint64_t FuncLowPC, FuncHighPC, SectionIndex; FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex); const char *VarName = VarDie.getName(DINameKind::ShortName); @@ -67,7 +148,8 @@ void LiveVariablePrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) { for (const DWARFLocationExpression &LocExpr : *Locs) { if (LocExpr.Range) { - LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie); + LiveElements.emplace_back( + std::make_unique<LiveVariable>(LocExpr, VarName, U, FuncDie)); } else { // If the LocExpr does not have an associated range, it is valid for // the whole of the function. @@ -75,24 +157,30 @@ void LiveVariablePrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) { // LocExpr, does that happen in reality? DWARFLocationExpression WholeFuncExpr{ DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), LocExpr.Expr}; - LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie); + LiveElements.emplace_back( + std::make_unique<LiveVariable>(WholeFuncExpr, VarName, U, FuncDie)); } } } -void LiveVariablePrinter::addFunction(DWARFDie D) { +void LiveElementPrinter::addFunction(DWARFDie D) { for (const DWARFDie &Child : D.children()) { - if (Child.getTag() == dwarf::DW_TAG_variable || - Child.getTag() == dwarf::DW_TAG_formal_parameter) + if (DbgVariables != DFDisabled && + (Child.getTag() == dwarf::DW_TAG_variable || + Child.getTag() == dwarf::DW_TAG_formal_parameter)) { addVariable(D, Child); - else + } else if (DbgInlinedFunctions != DFDisabled && + Child.getTag() == dwarf::DW_TAG_inlined_subroutine) { + addInlinedFunction(D, Child); + addFunction(Child); + } else addFunction(Child); } } -// Get the column number (in characters) at which the first live variable +// Get the column number (in characters) at which the first live element // line should be printed. -unsigned LiveVariablePrinter::getIndentLevel() const { +unsigned LiveElementPrinter::getIndentLevel() const { return DbgIndent + getInstStartColumn(STI); } @@ -100,8 +188,8 @@ unsigned LiveVariablePrinter::getIndentLevel() const { // printed line, and return the index of that column. // TODO: formatted_raw_ostream uses "column" to mean a number of characters // since the last \n, and we use it to mean the number of slots in which we -// put live variable lines. Pick a less overloaded word. -unsigned LiveVariablePrinter::moveToFirstVarColumn(formatted_raw_ostream &OS) { +// put live element lines. Pick a less overloaded word. +unsigned LiveElementPrinter::moveToFirstVarColumn(formatted_raw_ostream &OS) { // Logical column number: column zero is the first column we print in, each // logical column is 2 physical columns wide. unsigned FirstUnprintedLogicalColumn = @@ -117,7 +205,7 @@ unsigned LiveVariablePrinter::moveToFirstVarColumn(formatted_raw_ostream &OS) { return FirstUnprintedLogicalColumn; } -unsigned LiveVariablePrinter::findFreeColumn() { +unsigned LiveElementPrinter::findFreeColumn() { for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx) if (!ActiveCols[ColIdx].isActive()) return ColIdx; @@ -127,15 +215,15 @@ unsigned LiveVariablePrinter::findFreeColumn() { return OldSize; } -void LiveVariablePrinter::dump() const { - for (const LiveVariable &LV : LiveVariables) { - dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": "; - LV.print(dbgs(), MRI); +void LiveElementPrinter::dump() const { + for (const std::unique_ptr<LiveElement> &LE : LiveElements) { + LE->dump(dbgs()); + LE->print(dbgs(), MRI); dbgs() << "\n"; } } -void LiveVariablePrinter::addCompileUnit(DWARFDie D) { +void LiveElementPrinter::addCompileUnit(DWARFDie D) { if (D.getTag() == dwarf::DW_TAG_subprogram) addFunction(D); else @@ -148,47 +236,57 @@ void LiveVariablePrinter::addCompileUnit(DWARFDie D) { /// live-in to the instruction, and any live range active at NextAddr is /// live-out of the instruction. If IncludeDefinedVars is false, then live /// ranges starting at NextAddr will be ignored. -void LiveVariablePrinter::update(object::SectionedAddress ThisAddr, - object::SectionedAddress NextAddr, - bool IncludeDefinedVars) { +void LiveElementPrinter::update(object::SectionedAddress ThisAddr, + object::SectionedAddress NextAddr, + bool IncludeDefinedVars) { + // Do not create live ranges when debug-inlined-funcs option is provided with + // line format option. + if (DbgInlinedFunctions == DFLimitsOnly) + return; + // First, check variables which have already been assigned a column, so // that we don't change their order. - SmallSet<unsigned, 8> CheckedVarIdxs; + SmallSet<unsigned, 8> CheckedElementIdxs; for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { if (!ActiveCols[ColIdx].isActive()) continue; - CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx); - LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx]; - ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr); - ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr); + + CheckedElementIdxs.insert(ActiveCols[ColIdx].ElementIdx); + const std::unique_ptr<LiveElement> &LE = + LiveElements[ActiveCols[ColIdx].ElementIdx]; + ActiveCols[ColIdx].LiveIn = LE->liveAtAddress(ThisAddr); + ActiveCols[ColIdx].LiveOut = LE->liveAtAddress(NextAddr); + std::string Name = Demangle ? demangle(LE->getName()) : LE->getName(); LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-" - << NextAddr.Address << ", " << LV.VarName << ", Col " - << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn + << NextAddr.Address << ", " << Name << ", Col " << ColIdx + << ": LiveIn=" << ActiveCols[ColIdx].LiveIn << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n"); if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) - ActiveCols[ColIdx].VarIdx = Column::NullVarIdx; + ActiveCols[ColIdx].ElementIdx = Column::NullElementIdx; } // Next, look for variables which don't already have a column, but which // are now live. if (IncludeDefinedVars) { - for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End; - ++VarIdx) { - if (CheckedVarIdxs.count(VarIdx)) + for (unsigned ElementIdx = 0, End = LiveElements.size(); ElementIdx < End; + ++ElementIdx) { + if (CheckedElementIdxs.count(ElementIdx)) continue; - LiveVariable &LV = LiveVariables[VarIdx]; - bool LiveIn = LV.liveAtAddress(ThisAddr); - bool LiveOut = LV.liveAtAddress(NextAddr); + + const std::unique_ptr<LiveElement> &LE = LiveElements[ElementIdx]; + bool LiveIn = LE->liveAtAddress(ThisAddr); + bool LiveOut = LE->liveAtAddress(NextAddr); if (!LiveIn && !LiveOut) continue; unsigned ColIdx = findFreeColumn(); + std::string Name = Demangle ? demangle(LE->getName()) : LE->getName(); LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-" - << NextAddr.Address << ", " << LV.VarName << ", Col " + << NextAddr.Address << ", " << Name << ", Col " << ColIdx << ": LiveIn=" << LiveIn << ", LiveOut=" << LiveOut << "\n"); - ActiveCols[ColIdx].VarIdx = VarIdx; + ActiveCols[ColIdx].ElementIdx = ElementIdx; ActiveCols[ColIdx].LiveIn = LiveIn; ActiveCols[ColIdx].LiveOut = LiveOut; ActiveCols[ColIdx].MustDrawLabel = true; @@ -205,8 +303,8 @@ enum class LineChar { LabelCornerActive, LabelHoriz, }; -const char *LiveVariablePrinter::getLineChar(LineChar C) const { - bool IsASCII = DbgVariables == DVASCII; +const char *LiveElementPrinter::getLineChar(LineChar C) const { + bool IsASCII = DbgVariables == DFASCII || DbgInlinedFunctions == DFASCII; switch (C) { case LineChar::RangeStart: return IsASCII ? "^" : (const char *)u8"\u2548"; @@ -231,8 +329,8 @@ const char *LiveVariablePrinter::getLineChar(LineChar C) const { /// we only need to print active ranges or empty columns. If AfterInst is /// true, this is being printed after the last instruction fed to update(), /// otherwise this is being printed before it. -void LiveVariablePrinter::printAfterOtherLine(formatted_raw_ostream &OS, - bool AfterInst) { +void LiveElementPrinter::printAfterOtherLine(formatted_raw_ostream &OS, + bool AfterInst) { if (ActiveCols.size()) { unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); @@ -252,15 +350,15 @@ void LiveVariablePrinter::printAfterOtherLine(formatted_raw_ostream &OS, OS << "\n"; } -/// Print any live variable range info needed to the right of a -/// non-instruction line of disassembly. This is where we print the variable +/// Print any live element range info needed to the right of a +/// non-instruction line of disassembly. This is where we print the element /// names and expressions, with thin line-drawing characters connecting them /// to the live range which starts at the next instruction. If MustPrint is /// true, we have to print at least one line (with the continuation of any /// already-active live ranges) because something has already been printed /// earlier on this line. -void LiveVariablePrinter::printBetweenInsts(formatted_raw_ostream &OS, - bool MustPrint) { +void LiveElementPrinter::printBetweenInsts(formatted_raw_ostream &OS, + bool MustPrint) { bool PrintedSomething = false; for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) { @@ -277,17 +375,20 @@ void LiveVariablePrinter::printBetweenInsts(formatted_raw_ostream &OS, OS << " "; } + const std::unique_ptr<LiveElement> &LE = + LiveElements[ActiveCols[ColIdx].ElementIdx]; // Then print the variable name and location of the new live range, // with box drawing characters joining it to the live range line. OS << getLineChar(ActiveCols[ColIdx].LiveIn ? LineChar::LabelCornerActive : LineChar::LabelCornerNew) << getLineChar(LineChar::LabelHoriz) << " "; - WithColor(OS, raw_ostream::GREEN) - << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName; + + std::string Name = Demangle ? demangle(LE->getName()) : LE->getName(); + WithColor(OS, raw_ostream::GREEN) << Name; OS << " = "; { WithColor ExprColor(OS, raw_ostream::CYAN); - LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI); + LE->print(OS, MRI); } // If there are any columns to the right of the expression we just @@ -317,8 +418,8 @@ void LiveVariablePrinter::printBetweenInsts(formatted_raw_ostream &OS, printAfterOtherLine(OS, false); } -/// Print the live variable ranges to the right of a disassembled instruction. -void LiveVariablePrinter::printAfterInst(formatted_raw_ostream &OS) { +/// Print the live element ranges to the right of a disassembled instruction. +void LiveElementPrinter::printAfterInst(formatted_raw_ostream &OS) { if (!ActiveCols.size()) return; unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); @@ -337,6 +438,24 @@ void LiveVariablePrinter::printAfterInst(formatted_raw_ostream &OS) { } } +void LiveElementPrinter::printStartLine(formatted_raw_ostream &OS, + object::SectionedAddress Addr) { + // Print a line to idenfity the start of an inlined function if line format + // is specified. + if (DbgInlinedFunctions == DFLimitsOnly) + for (const std::unique_ptr<LiveElement> &LE : LiveElements) + LE->printElementLine(OS, Addr, false); +} + +void LiveElementPrinter::printEndLine(formatted_raw_ostream &OS, + object::SectionedAddress Addr) { + // Print a line to idenfity the end of an inlined function if line format is + // specified. + if (DbgInlinedFunctions == DFLimitsOnly) + for (const std::unique_ptr<LiveElement> &LE : LiveElements) + LE->printElementLine(OS, Addr, true); +} + bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { std::unique_ptr<MemoryBuffer> Buffer; if (LineInfo.Source) { @@ -371,7 +490,7 @@ bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { void SourcePrinter::printSourceLine(formatted_raw_ostream &OS, object::SectionedAddress Address, StringRef ObjectFilename, - LiveVariablePrinter &LVP, + LiveElementPrinter &LEP, StringRef Delimiter) { if (!Symbolizer) return; @@ -419,15 +538,16 @@ void SourcePrinter::printSourceLine(formatted_raw_ostream &OS, } if (PrintLines) - printLines(OS, LineInfo, Delimiter, LVP); + printLines(OS, Address, LineInfo, Delimiter, LEP); if (PrintSource) - printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP); + printSources(OS, LineInfo, ObjectFilename, Delimiter, LEP); OldLineInfo = LineInfo; } void SourcePrinter::printLines(formatted_raw_ostream &OS, + object::SectionedAddress Address, const DILineInfo &LineInfo, StringRef Delimiter, - LiveVariablePrinter &LVP) { + LiveElementPrinter &LEP) { bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString && LineInfo.FunctionName != OldLineInfo.FunctionName; if (PrintFunctionName) { @@ -442,7 +562,7 @@ void SourcePrinter::printLines(formatted_raw_ostream &OS, (OldLineInfo.Line != LineInfo.Line || OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) { OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line; - LVP.printBetweenInsts(OS, true); + LEP.printBetweenInsts(OS, true); } } @@ -477,7 +597,7 @@ StringRef SourcePrinter::getLine(const DILineInfo &LineInfo, void SourcePrinter::printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, StringRef ObjectFilename, StringRef Delimiter, - LiveVariablePrinter &LVP) { + LiveElementPrinter &LEP) { if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 || (OldLineInfo.Line == LineInfo.Line && OldLineInfo.FileName == LineInfo.FileName)) @@ -486,7 +606,7 @@ void SourcePrinter::printSources(formatted_raw_ostream &OS, StringRef Line = getLine(LineInfo, ObjectFilename); if (!Line.empty()) { OS << Delimiter << Line; - LVP.printBetweenInsts(OS, true); + LEP.printBetweenInsts(OS, true); } } diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h index fc67fc6..5c131a0 100644 --- a/llvm/tools/llvm-objdump/SourcePrinter.h +++ b/llvm/tools/llvm-objdump/SourcePrinter.h @@ -22,40 +22,83 @@ namespace llvm { namespace objdump { +/// Base class for representing the location of a source-level variable or +/// an inlined function. +class LiveElement { +protected: + const char *Name; + DWARFUnit *Unit; + const DWARFDie FuncDie; + +public: + LiveElement(const char *Name, DWARFUnit *Unit, const DWARFDie FuncDie) + : Name(Name), Unit(Unit), FuncDie(FuncDie) {} + + virtual ~LiveElement() {}; + const char *getName() const { return Name; } + + virtual bool liveAtAddress(object::SectionedAddress Addr) const = 0; + virtual void print(raw_ostream &OS, const MCRegisterInfo &MRI) const = 0; + virtual void dump(raw_ostream &OS) const = 0; + virtual void printElementLine(raw_ostream &OS, + object::SectionedAddress Address, + bool IsEnd) const {} +}; + +class InlinedFunction : public LiveElement { +private: + DWARFDie InlinedFuncDie; + DWARFAddressRange Range; + +public: + InlinedFunction(const char *FunctionName, DWARFUnit *Unit, + const DWARFDie FuncDie, const DWARFDie InlinedFuncDie, + DWARFAddressRange &Range) + : LiveElement(FunctionName, Unit, FuncDie), + InlinedFuncDie(InlinedFuncDie), Range(Range) {} + + bool liveAtAddress(object::SectionedAddress Addr) const override; + void print(raw_ostream &OS, const MCRegisterInfo &MRI) const override; + void dump(raw_ostream &OS) const override; + void printElementLine(raw_ostream &OS, object::SectionedAddress Address, + bool IsEnd) const override; +}; + /// Stores a single expression representing the location of a source-level /// variable, along with the PC range for which that expression is valid. -struct LiveVariable { +class LiveVariable : public LiveElement { +private: DWARFLocationExpression LocExpr; - const char *VarName; - DWARFUnit *Unit; - const DWARFDie FuncDie; +public: LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName, DWARFUnit *Unit, const DWARFDie FuncDie) - : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {} + : LiveElement(VarName, Unit, FuncDie), LocExpr(LocExpr) {} - bool liveAtAddress(object::SectionedAddress Addr); - - void print(raw_ostream &OS, const MCRegisterInfo &MRI) const; + bool liveAtAddress(object::SectionedAddress Addr) const override; + void print(raw_ostream &OS, const MCRegisterInfo &MRI) const override; + void dump(raw_ostream &OS) const override; }; -/// Helper class for printing source variable locations alongside disassembly. -class LiveVariablePrinter { - // Information we want to track about one column in which we are printing a - // variable live range. +/// Helper class for printing source locations for variables and inlined +/// subroutines alongside disassembly. +class LiveElementPrinter { + // Information we want to track about one column in which we are printing an + // element live range. struct Column { - unsigned VarIdx = NullVarIdx; + unsigned ElementIdx = NullElementIdx; bool LiveIn = false; bool LiveOut = false; bool MustDrawLabel = false; - bool isActive() const { return VarIdx != NullVarIdx; } + bool isActive() const { return ElementIdx != NullElementIdx; } - static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max(); + static constexpr unsigned NullElementIdx = + std::numeric_limits<unsigned>::max(); }; - // All live variables we know about in the object/image file. - std::vector<LiveVariable> LiveVariables; + // All live elements we know about in the object/image file. + std::vector<std::unique_ptr<LiveElement>> LiveElements; // The columns we are currently drawing. IndexedMap<Column> ActiveCols; @@ -63,11 +106,12 @@ class LiveVariablePrinter { const MCRegisterInfo &MRI; const MCSubtargetInfo &STI; + void addInlinedFunction(DWARFDie FuncDie, DWARFDie InlinedFuncDie); void addVariable(DWARFDie FuncDie, DWARFDie VarDie); void addFunction(DWARFDie D); - // Get the column number (in characters) at which the first live variable + // Get the column number (in characters) at which the first live element // line should be printed. unsigned getIndentLevel() const; @@ -75,13 +119,13 @@ class LiveVariablePrinter { // printed line, and return the index of that column. // TODO: formatted_raw_ostream uses "column" to mean a number of characters // since the last \n, and we use it to mean the number of slots in which we - // put live variable lines. Pick a less overloaded word. + // put live element lines. Pick a less overloaded word. unsigned moveToFirstVarColumn(formatted_raw_ostream &OS); unsigned findFreeColumn(); public: - LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) + LiveElementPrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) : ActiveCols(Column()), MRI(MRI), STI(STI) {} void dump() const; @@ -114,7 +158,7 @@ public: /// otherwise this is being printed before it. void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst); - /// Print any live variable range info needed to the right of a + /// Print any live element range info needed to the right of a /// non-instruction line of disassembly. This is where we print the variable /// names and expressions, with thin line-drawing characters connecting them /// to the live range which starts at the next instruction. If MustPrint is @@ -123,8 +167,13 @@ public: /// earlier on this line. void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint); - /// Print the live variable ranges to the right of a disassembled instruction. + /// Print the live element ranges to the right of a disassembled instruction. void printAfterInst(formatted_raw_ostream &OS); + + /// Print a line to idenfity the start of a live element. + void printStartLine(formatted_raw_ostream &OS, object::SectionedAddress Addr); + /// Print a line to idenfity the end of a live element. + void printEndLine(formatted_raw_ostream &OS, object::SectionedAddress Addr); }; class SourcePrinter { @@ -144,12 +193,13 @@ protected: private: bool cacheSource(const DILineInfo &LineInfoFile); - void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo, - StringRef Delimiter, LiveVariablePrinter &LVP); + void printLines(formatted_raw_ostream &OS, object::SectionedAddress Address, + const DILineInfo &LineInfo, StringRef Delimiter, + LiveElementPrinter &LEP); void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, StringRef ObjectFilename, StringRef Delimiter, - LiveVariablePrinter &LVP); + LiveElementPrinter &LEP); // Returns line source code corresponding to `LineInfo`. // Returns empty string if source code cannot be found. @@ -162,7 +212,7 @@ public: virtual void printSourceLine(formatted_raw_ostream &OS, object::SectionedAddress Address, StringRef ObjectFilename, - LiveVariablePrinter &LVP, + LiveElementPrinter &LEP, StringRef Delimiter = "; "); }; diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index 74eb903..0316c4b 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -348,7 +348,8 @@ static bool Wide; std::string objdump::Prefix; uint32_t objdump::PrefixStrip; -DebugVarsFormat objdump::DbgVariables = DVDisabled; +DebugFormat objdump::DbgVariables = DFDisabled; +DebugFormat objdump::DbgInlinedFunctions = DFDisabled; int objdump::DbgIndent = 52; @@ -523,8 +524,8 @@ static const Target *getTarget(const ObjectFile *Obj) { // Get the target specific parser. std::string Error; - const Target *TheTarget = TargetRegistry::lookupTarget(ArchName, TheTriple, - Error); + const Target *TheTarget = + TargetRegistry::lookupTarget(ArchName, TheTriple, Error); if (!TheTarget) reportError(Obj->getFileName(), "can't find target: " + Error); @@ -633,7 +634,7 @@ static bool isCSKYElf(const ObjectFile &Obj) { } static bool hasMappingSymbols(const ObjectFile &Obj) { - return isArmElf(Obj) || isAArch64Elf(Obj) || isCSKYElf(Obj) ; + return isArmElf(Obj) || isAArch64Elf(Obj) || isCSKYElf(Obj); } static void printRelocation(formatted_raw_ostream &OS, StringRef FileName, @@ -653,7 +654,7 @@ static void printRelocation(formatted_raw_ostream &OS, StringRef FileName, static void printBTFRelocation(formatted_raw_ostream &FOS, llvm::BTFParser &BTF, object::SectionedAddress Address, - LiveVariablePrinter &LVP) { + LiveElementPrinter &LEP) { const llvm::BTF::BPFFieldReloc *Reloc = BTF.findFieldReloc(Address); if (!Reloc) return; @@ -664,7 +665,7 @@ static void printBTFRelocation(formatted_raw_ostream &FOS, llvm::BTFParser &BTF, if (LeadingAddr) FOS << format("%016" PRIx64 ": ", Address.Address + AdjustVMA); FOS << "CO-RE " << Val; - LVP.printAfterOtherLine(FOS, true); + LEP.printAfterOtherLine(FOS, true); } class PrettyPrinter { @@ -675,10 +676,11 @@ public: object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, StringRef ObjectFilename, std::vector<RelocationRef> *Rels, - LiveVariablePrinter &LVP) { + LiveElementPrinter &LEP) { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, LVP); - LVP.printBetweenInsts(OS, false); + SP->printSourceLine(OS, Address, ObjectFilename, LEP); + LEP.printStartLine(OS, Address); + LEP.printBetweenInsts(OS, false); printRawData(Bytes, Address.Address, OS, STI); @@ -698,7 +700,7 @@ public: const MCAsmInfo &MAI, const MCSubtargetInfo &STI, StringRef Comments, - LiveVariablePrinter &LVP) { + LiveElementPrinter &LEP) { do { if (!Comments.empty()) { // Emit a line of comments. @@ -712,7 +714,7 @@ public: FOS.PadToColumn(CommentColumn); FOS << MAI.getCommentString() << ' ' << Comment; } - LVP.printAfterInst(FOS); + LEP.printAfterInst(FOS); FOS << "\n"; } while (!Comments.empty()); FOS.flush(); @@ -757,10 +759,10 @@ public: void emitPostInstructionInfo(formatted_raw_ostream &FOS, const MCAsmInfo &MAI, const MCSubtargetInfo &STI, StringRef Comments, - LiveVariablePrinter &LVP) override { + LiveElementPrinter &LEP) override { // Hexagon does not write anything to the comment stream, so we can just // print the separator. - LVP.printAfterInst(FOS); + LEP.printAfterInst(FOS); FOS << getInstructionSeparator(); FOS.flush(); if (ShouldClosePacket) @@ -771,9 +773,9 @@ public: object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, StringRef ObjectFilename, std::vector<RelocationRef> *Rels, - LiveVariablePrinter &LVP) override { + LiveElementPrinter &LEP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, LVP, ""); + SP->printSourceLine(OS, Address, ObjectFilename, LEP, ""); if (!MI) { printLead(Bytes, Address.Address, OS); OS << " <unknown>"; @@ -784,7 +786,7 @@ public: StringRef Preamble = IsStartOfBundle ? " { " : " "; if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, LVP, ""); + SP->printSourceLine(OS, Address, ObjectFilename, LEP, ""); printLead(Bytes, Address.Address, OS); OS << Preamble; std::string Buf; @@ -845,9 +847,9 @@ public: object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, StringRef ObjectFilename, std::vector<RelocationRef> *Rels, - LiveVariablePrinter &LVP) override { + LiveElementPrinter &LEP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, LVP); + SP->printSourceLine(OS, Address, ObjectFilename, LEP); if (MI) { SmallString<40> InstStr; @@ -866,10 +868,10 @@ public: support::endian::read32<llvm::endianness::little>(Bytes.data())); OS.indent(42); } else { - OS << format("\t.byte 0x%02" PRIx8, Bytes[0]); - for (unsigned int i = 1; i < Bytes.size(); i++) - OS << format(", 0x%02" PRIx8, Bytes[i]); - OS.indent(55 - (6 * Bytes.size())); + OS << format("\t.byte 0x%02" PRIx8, Bytes[0]); + for (unsigned int i = 1; i < Bytes.size(); i++) + OS << format(", 0x%02" PRIx8, Bytes[i]); + OS.indent(55 - (6 * Bytes.size())); } } @@ -880,7 +882,7 @@ public: for (uint32_t D : ArrayRef(reinterpret_cast<const support::little32_t *>(Bytes.data()), Bytes.size() / 4)) - OS << format(" %08" PRIX32, D); + OS << format(" %08" PRIX32, D); } else { for (unsigned char B : Bytes) OS << format(" %02" PRIX8, B); @@ -898,9 +900,9 @@ public: object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, StringRef ObjectFilename, std::vector<RelocationRef> *Rels, - LiveVariablePrinter &LVP) override { + LiveElementPrinter &LEP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, LVP); + SP->printSourceLine(OS, Address, ObjectFilename, LEP); if (LeadingAddr) OS << format("%8" PRId64 ":", Address.Address / 8); if (ShowRawInsn) { @@ -921,10 +923,11 @@ public: object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, StringRef ObjectFilename, std::vector<RelocationRef> *Rels, - LiveVariablePrinter &LVP) override { + LiveElementPrinter &LEP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, LVP); - LVP.printBetweenInsts(OS, false); + SP->printSourceLine(OS, Address, ObjectFilename, LEP); + LEP.printStartLine(OS, Address); + LEP.printBetweenInsts(OS, false); size_t Start = OS.tell(); if (LeadingAddr) @@ -975,10 +978,11 @@ public: object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, StringRef ObjectFilename, std::vector<RelocationRef> *Rels, - LiveVariablePrinter &LVP) override { + LiveElementPrinter &LEP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, LVP); - LVP.printBetweenInsts(OS, false); + SP->printSourceLine(OS, Address, ObjectFilename, LEP); + LEP.printStartLine(OS, Address); + LEP.printBetweenInsts(OS, false); size_t Start = OS.tell(); if (LeadingAddr) @@ -1013,10 +1017,11 @@ public: object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, StringRef ObjectFilename, std::vector<RelocationRef> *Rels, - LiveVariablePrinter &LVP) override { + LiveElementPrinter &LEP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, LVP); - LVP.printBetweenInsts(OS, false); + SP->printSourceLine(OS, Address, ObjectFilename, LEP); + LEP.printStartLine(OS, Address); + LEP.printBetweenInsts(OS, false); size_t Start = OS.tell(); if (LeadingAddr) @@ -1057,7 +1062,7 @@ public: RISCVPrettyPrinter RISCVPrettyPrinterInst; PrettyPrinter &selectPrettyPrinter(Triple const &Triple) { - switch(Triple.getArch()) { + switch (Triple.getArch()) { default: return PrettyPrinterInst; case Triple::hexagon: @@ -1108,8 +1113,7 @@ private: DisassemblerTarget::DisassemblerTarget(const Target *TheTarget, ObjectFile &Obj, StringRef TripleName, StringRef MCPU, SubtargetFeatures &Features) - : TheTarget(TheTarget), - Printer(&selectPrettyPrinter(Triple(TripleName))), + : TheTarget(TheTarget), Printer(&selectPrettyPrinter(Triple(TripleName))), RegisterInfo(TheTarget->createMCRegInfo(TripleName)) { if (!RegisterInfo) reportError(Obj.getFileName(), "no register info for target " + TripleName); @@ -1388,7 +1392,6 @@ static bool shouldAdjustVA(const SectionRef &Section) { return false; } - typedef std::pair<uint64_t, char> MappingSymbolPair; static char getMappingSymbolKind(ArrayRef<MappingSymbolPair> MappingSymbols, uint64_t Address) { @@ -1416,8 +1419,7 @@ static uint64_t dumpARMELFData(uint64_t SectionAddr, uint64_t Index, dumpBytes(Bytes.slice(Index, 4), OS); AlignToInstStartColumn(Start, STI, OS); OS << "\t.word\t" - << format_hex(support::endian::read32(Bytes.data() + Index, Endian), - 10); + << format_hex(support::endian::read32(Bytes.data() + Index, Endian), 10); return 4; } if (Index + 2 <= End) { @@ -1791,9 +1793,9 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, // STAB symbol's section field refers to a valid section index. Otherwise // the symbol may error trying to load a section that does not exist. DataRefImpl SymDRI = Symbol.getRawDataRefImpl(); - uint8_t NType = (MachO->is64Bit() ? - MachO->getSymbol64TableEntry(SymDRI).n_type: - MachO->getSymbolTableEntry(SymDRI).n_type); + uint8_t NType = + (MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type + : MachO->getSymbolTableEntry(SymDRI).n_type); if (NType & MachO::N_STAB) continue; } @@ -1892,15 +1894,15 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, llvm::stable_sort(AbsoluteSymbols); std::unique_ptr<DWARFContext> DICtx; - LiveVariablePrinter LVP(*DT->Context->getRegisterInfo(), *DT->SubtargetInfo); + LiveElementPrinter LEP(*DT->Context->getRegisterInfo(), *DT->SubtargetInfo); - if (DbgVariables != DVDisabled) { + if (DbgVariables != DFDisabled || DbgInlinedFunctions != DFDisabled) { DICtx = DWARFContext::create(DbgObj); for (const std::unique_ptr<DWARFUnit> &CU : DICtx->compile_units()) - LVP.addCompileUnit(CU->getUnitDIE(false)); + LEP.addCompileUnit(CU->getUnitDIE(false)); } - LLVM_DEBUG(LVP.dump()); + LLVM_DEBUG(LEP.dump()); BBAddrMapInfo FullAddrMap; auto ReadBBAddrMap = [&](std::optional<unsigned> SectionIndex = @@ -2368,8 +2370,9 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, ThisBytes.size(), DT->DisAsm->suggestBytesToSkip(ThisBytes, ThisAddr)); - LVP.update({Index, Section.getIndex()}, - {Index + Size, Section.getIndex()}, Index + Size != End); + LEP.update({ThisAddr, Section.getIndex()}, + {ThisAddr + Size, Section.getIndex()}, + Index + Size != End); DT->InstPrinter->setCommentStream(CommentStream); @@ -2377,7 +2380,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, *DT->InstPrinter, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size), {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, FOS, - "", *DT->SubtargetInfo, &SP, Obj.getFileName(), &Rels, LVP); + "", *DT->SubtargetInfo, &SP, Obj.getFileName(), &Rels, LEP); DT->InstPrinter->setCommentStream(llvm::nulls()); @@ -2562,22 +2565,26 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, assert(DT->Context->getAsmInfo()); DT->Printer->emitPostInstructionInfo(FOS, *DT->Context->getAsmInfo(), *DT->SubtargetInfo, - CommentStream.str(), LVP); + CommentStream.str(), LEP); Comments.clear(); if (BTF) - printBTFRelocation(FOS, *BTF, {Index, Section.getIndex()}, LVP); + printBTFRelocation(FOS, *BTF, {Index, Section.getIndex()}, LEP); if (InlineRelocs) { while (findRel()) { // When --adjust-vma is used, update the address printed. printRelocation(FOS, Obj.getFileName(), *RelCur, SectionAddr + RelOffset + VMAAdjustment, Is64Bits); - LVP.printAfterOtherLine(FOS, true); + LEP.printAfterOtherLine(FOS, true); ++RelCur; } } + object::SectionedAddress NextAddr = { + SectionAddr + Index + VMAAdjustment + Size, Section.getIndex()}; + LEP.printEndLine(FOS, NextAddr); + Index += Size; } } @@ -2869,7 +2876,8 @@ void objdump::printSectionContents(const ObjectFile *Obj) { continue; } - StringRef Contents = unwrapOrError(Section.getContents(), Obj->getFileName()); + StringRef Contents = + unwrapOrError(Section.getContents(), Obj->getFileName()); // Dump out the content as hex and printable ascii characters. for (std::size_t Addr = 0, End = Contents.size(); Addr < End; Addr += 16) { @@ -3293,8 +3301,8 @@ static bool shouldWarnForInvalidStartStopAddress(ObjectFile *Obj) { return false; } -static void checkForInvalidStartStopAddress(ObjectFile *Obj, - uint64_t Start, uint64_t Stop) { +static void checkForInvalidStartStopAddress(ObjectFile *Obj, uint64_t Start, + uint64_t Stop) { if (!shouldWarnForInvalidStartStopAddress(Obj)) return; @@ -3617,13 +3625,25 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) { Prefix = InputArgs.getLastArgValue(OBJDUMP_prefix).str(); parseIntArg(InputArgs, OBJDUMP_prefix_strip, PrefixStrip); if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_debug_vars_EQ)) { - DbgVariables = StringSwitch<DebugVarsFormat>(A->getValue()) - .Case("ascii", DVASCII) - .Case("unicode", DVUnicode) - .Default(DVInvalid); - if (DbgVariables == DVInvalid) + DbgVariables = StringSwitch<DebugFormat>(A->getValue()) + .Case("ascii", DFASCII) + .Case("unicode", DFUnicode) + .Default(DFInvalid); + if (DbgVariables == DFInvalid) + invalidArgValue(A); + } + + if (const opt::Arg *A = + InputArgs.getLastArg(OBJDUMP_debug_inlined_funcs_EQ)) { + DbgInlinedFunctions = StringSwitch<DebugFormat>(A->getValue()) + .Case("ascii", DFASCII) + .Case("limits-only", DFLimitsOnly) + .Case("unicode", DFUnicode) + .Default(DFInvalid); + if (DbgInlinedFunctions == DFInvalid) invalidArgValue(A); } + if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_disassembler_color_EQ)) { DisassemblyColor = StringSwitch<ColorOutput>(A->getValue()) .Case("on", ColorOutput::Enable) @@ -3634,7 +3654,7 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) { invalidArgValue(A); } - parseIntArg(InputArgs, OBJDUMP_debug_vars_indent_EQ, DbgIndent); + parseIntArg(InputArgs, OBJDUMP_debug_indent_EQ, DbgIndent); parseMachOOptions(InputArgs); diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h index 25d9c1e..ce06429 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -40,11 +40,12 @@ class XCOFFObjectFile; namespace objdump { -enum DebugVarsFormat { DVDisabled, DVUnicode, DVASCII, DVInvalid }; +enum DebugFormat { DFASCII, DFDisabled, DFInvalid, DFLimitsOnly, DFUnicode }; extern bool ArchiveHeaders; extern int DbgIndent; -extern DebugVarsFormat DbgVariables; +extern DebugFormat DbgVariables; +extern DebugFormat DbgInlinedFunctions; extern bool Demangle; extern bool Disassemble; extern bool DisassembleAll; @@ -126,7 +127,7 @@ void printSectionContents(const object::ObjectFile *O); void reportWarning(const Twine &Message, StringRef File); template <typename T, typename... Ts> -T unwrapOrError(Expected<T> EO, Ts &&... Args) { +T unwrapOrError(Expected<T> EO, Ts &&...Args) { if (EO) return std::move(*EO); reportError(EO.takeError(), std::forward<Ts>(Args)...); |