Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ac4b7bc
[llvm-readobj] Dump callgraph section info for ELF
Prabhuk Sep 6, 2025
31c4d2b
Format code
Prabhuk Sep 8, 2025
4d1a7c7
Implement JSON output for printCallGraph()
Prabhuk Sep 8, 2025
f0810b2
ELFDumper cleanups
Prabhuk Sep 8, 2025
5f20b7b
Add readelf callgraph info tests.
Prabhuk Sep 9, 2025
d991194
more tests
Prabhuk Sep 9, 2025
c2d8d22
Extend tests to json
Prabhuk Sep 9, 2025
2874918
Expand all tests to include JSON and LLVM formats.
Prabhuk Sep 9, 2025
9803c62
Improve tests. Match full lines.
Prabhuk Sep 9, 2025
7285210
Make call graph processing agnostic of target architecture pointer wi…
Prabhuk Sep 17, 2025
8236798
Remove unnecessary function returns after reportError calls which bot…
Prabhuk Sep 17, 2025
abe189e
Formatting fixes.
Prabhuk Sep 17, 2025
0b9c46d
Remove callsites from direct call data.
Prabhuk Sep 18, 2025
5b504fc
Format
Prabhuk Sep 18, 2025
5b6bb00
Simplify readelf output.
Prabhuk Sep 18, 2025
937ff61
format
Prabhuk Sep 18, 2025
1b86857
Clean up callgraph output
Prabhuk Sep 18, 2025
7280ff3
Format
Prabhuk Sep 18, 2025
bdb8683
Show symbol names.
Prabhuk Sep 18, 2025
09d3a87
Format
Prabhuk Sep 18, 2025
b851b8d
Make llvm output better.
Prabhuk Sep 18, 2025
fd50ff0
format source.
Prabhuk Sep 18, 2025
ee3a081
Minor improvements to LLVM output.
Prabhuk Sep 18, 2025
3c94a44
Fix tests to match the new output.
Prabhuk Sep 18, 2025
026b184
Add REQUIRES: x86-registered-target to tests.
Prabhuk Sep 19, 2025
d1655c2
Account for windows binary name extension.
Prabhuk Sep 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
## Tests --call-graph-info prints information from call graph section.

# REQUIRES: x86-registered-target

# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --match-full-lines
# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=LLVM
# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=JSON

# CHECK: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
Comment on lines +9 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# CHECK: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
# CHECK: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.

I don't think you need to check the tool's name do you? What's the convention in other tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to use --match-full-lines in my test. Let me try if the test passes if the toolname prefix is not part of the CHECK lines.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a particular reason you want to match full lines? convention? specific criteria?

# CHECK-NEXT: Per-function call graph information::
# CHECK-EMPTY:
# CHECK-NEXT: Function:: foo
# CHECK-NEXT: Function PC:: 0x0
# CHECK-NEXT: FormatVersionNumber:: 0
# CHECK-NEXT: Function Kind:: NOT_INDIRECT
# CHECK-NEXT: Indirect callee count:: 0
# CHECK-NEXT: Direct callee count:: 1
# CHECK-NEXT: {
# CHECK-NEXT: CalleePC:: 0x5
# CHECK-NEXT: }
# CHECK-EMPTY:
# CHECK-NEXT: Function:: bar
# CHECK-NEXT: Function PC:: 0x6
# CHECK-NEXT: FormatVersionNumber:: 0
# CHECK-NEXT: Function Kind:: UNKNOWN_TID
# CHECK-NEXT: Indirect callee count:: 1
# CHECK-NEXT: {
# CHECK-NEXT: callsite: 0x9
# CHECK-NEXT: calleeTypeId: 0x10
# CHECK-NEXT: }
# CHECK-NEXT: Direct callee count:: 0
# CHECK-EMPTY:
# CHECK-NEXT: Function:: baz
# CHECK-NEXT: Function PC:: 0xa
# CHECK-NEXT: FormatVersionNumber:: 0
# CHECK-NEXT: Function Kind:: KNOWN_TID
# CHECK-NEXT: Function Type ID:: 0x20
# CHECK-NEXT: Indirect callee count:: 0
# CHECK-NEXT: Direct callee count:: 0
Comment on lines +10 to +40
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this section fail when you do json(and I guess llvm too) output? CHECK lines are always checked no matter the prefix.



# LLVM: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
# LLVM: callgraph_info [
# LLVM-NEXT: Function {
# LLVM-NEXT: Name: foo
# LLVM-NEXT: Address: 0x0
# LLVM-NEXT: Version: 0
# LLVM-NEXT: KindStr: NOT_INDIRECT
# LLVM-NEXT: Kind: 0
# LLVM-NEXT: NumIndirectCallSites: 0
# LLVM-NEXT: NumDirectCallSites: 1
# LLVM-NEXT: DirectCallees [
# LLVM-NEXT: Entry {
# LLVM-NEXT: Address: 0x5
# LLVM-NEXT: }
# LLVM-NEXT: ]
# LLVM-NEXT: }
# LLVM-NEXT: Function {
# LLVM-NEXT: Name: bar
# LLVM-NEXT: Address: 0x6
# LLVM-NEXT: Version: 0
# LLVM-NEXT: KindStr: UNKNOWN_TID
# LLVM-NEXT: Kind: 1
# LLVM-NEXT: NumIndirectCallSites: 1
# LLVM-NEXT: IndirectCallsites [
# LLVM-NEXT: IndirectCallsite {
# LLVM-NEXT: Address: 0x9
# LLVM-NEXT: TypeId: 0x10
# LLVM-NEXT: }
# LLVM-NEXT: ]
# LLVM-NEXT: NumDirectCallSites: 0
# LLVM-NEXT: }
# LLVM-NEXT: Function {
# LLVM-NEXT: Name: baz
# LLVM-NEXT: Address: 0xA
# LLVM-NEXT: Version: 0
# LLVM-NEXT: KindStr: KNOWN_TID
# LLVM-NEXT: Kind: 2
# LLVM-NEXT: TypeId: 0x20
# LLVM-NEXT: NumIndirectCallSites: 0
# LLVM-NEXT: NumDirectCallSites: 0
# LLVM-NEXT: }
# LLVM-NEXT: ]

# JSON: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets.
# JSON: "callgraph_info": [
# JSON-NEXT: {
# JSON-NEXT: "Function": {
# JSON-NEXT: "Name": "foo",
# JSON-NEXT: "Address": 0,
# JSON-NEXT: "Version": 0,
# JSON-NEXT: "KindStr": "NOT_INDIRECT",
# JSON-NEXT: "Kind": 0,
# JSON-NEXT: "NumIndirectCallSites": 0,
# JSON-NEXT: "NumDirectCallSites": 1,
# JSON-NEXT: "DirectCallees": [
# JSON-NEXT: {
# JSON-NEXT: "Entry": {
# JSON-NEXT: "Address": 5
# JSON-NEXT: }
# JSON-NEXT: }
# JSON-NEXT: ]
# JSON-NEXT: }
# JSON-NEXT: },
# JSON-NEXT: {
# JSON-NEXT: "Function": {
# JSON-NEXT: "Name": "bar",
# JSON-NEXT: "Address": 6,
# JSON-NEXT: "Version": 0,
# JSON-NEXT: "KindStr": "UNKNOWN_TID",
# JSON-NEXT: "Kind": 1,
# JSON-NEXT: "NumIndirectCallSites": 1,
# JSON-NEXT: "IndirectCallsites": [
# JSON-NEXT: {
# JSON-NEXT: "IndirectCallsite": {
# JSON-NEXT: "Address": 9,
# JSON-NEXT: "TypeId": 16
# JSON-NEXT: }
# JSON-NEXT: }
# JSON-NEXT: ],
# JSON-NEXT: "NumDirectCallSites": 0
# JSON-NEXT: }
# JSON-NEXT: },
# JSON-NEXT: {
# JSON-NEXT: "Function": {
# JSON-NEXT: "Name": "baz",
# JSON-NEXT: "Address": 10,
# JSON-NEXT: "Version": 0,
# JSON-NEXT: "KindStr": "KNOWN_TID",
# JSON-NEXT: "Kind": 2,
# JSON-NEXT: "TypeId": 32,
# JSON-NEXT: "NumIndirectCallSites": 0,
# JSON-NEXT: "NumDirectCallSites": 0
# JSON-NEXT: }
# JSON-NEXT: }
# JSON-NEXT: ]


.text

.globl foo
.type foo,@function
foo: #< foo is at 0.
.Lfoo_begin:
callq foo #< direct call is at 5. target is foo (5).
retq

.globl bar
.type bar,@function
bar: #< bar is at 6.
callq *-40(%rbp) #< indirect call is at 9.
retq

.globl baz
.type baz,@function
baz: #< baz is at 10 (a).
retq

.globl qux
.type qux,@function
qux: #< qux is at 11 (b).
retq

.section .callgraph,"o",@progbits,.text
.quad 0 #< Format version number.
.quad 0 #< foo()'s entry address.
.quad 0 #< Function kind: not an indirect target.
.quad 0 #< Count of indirect call sites that follow: 0.
.quad 1 #< Count of direct callees that follow: 1>
.quad 5 #< Direct callee foo's address>

.quad 0 #< Format version number.
.quad 6 #< bar()'s entry address.
.quad 1 #< Function kind: indirect target with unknown type id.
.quad 1 #< Count of indirect call sites that follow: 1.
.quad 16 #< Indirect call type id.
.quad 9 #< Indirect call site.
.quad 0 #< Count of direct call sites that follow: 0>

.quad 0 #< Format version number.
.quad 10 #< baz()'s entry address.
.quad 2 #< Function kind: indirect target with known type id.
.quad 32 #< Indirect target type id.
.quad 0 #< Count of indirect call sites that follow: 0>.
.quad 0 #< Count of direct call sites that follow: 0>

# No call graph section entry for qux.
# Technically its "UNKNOWN" type id but will not be printed as such by llvm-readelf.

.text
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Tests that --call-graph-info fails if .callgraph section has unknown format
## version number.

# REQUIRES: x86-registered-target

# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR

# ERR: {{.*}}llvm-readelf{{.*}}: error: 'Unknown value': Unknown format version value [1] in .callgraph section.

.text
.globl _Z3foov
.type _Z3foov,@function
_Z3foov:
callq _Z3foov@PLT

.section .callgraph,"o",@progbits,.text
.quad 1 #< Invalid format version number: the only supported version is 0.
.text
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## Tests that --call-graph-info fails if .callgraph section has invalid
## function kind value.

# REQUIRES: x86-registered-target

# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR

# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [FunctionKind] for function at [0x0]': Unknown value [4].

.text
.globl _Z3foov
.type _Z3foov,@function
_Z3foov:
callq _Z3foov@PLT

.section .callgraph,"o",@progbits,.text
.quad 0 #< Format version number.
.quad 0 #< Function entry address.
.quad 4 #< Invalid function kind: must be one of 0, 1, 2, 3.
.text
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Tests that --call-graph-info fails if .callgraph section has invalid size.

# REQUIRES: x86-registered-target

# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR

# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [number of indirect callsites] for function at [0x0]': unexpected end of data at offset 0x19 while reading [0x18, 0x20)

.text
.globl _Z3foov
.type _Z3foov,@function
_Z3foov:
callq _Z3foov@PLT

.section .callgraph,"o",@progbits,.text
# Each unit in .callgraph section must have 64bit size. Therefore, byte size
# must be divisible by 64.
.quad 0
.quad 0
.quad 0
.byte 0
.text
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Tests that --call-graph-info fails if .callgraph section does not have
## an expected value, e.g., not as much call sites as the given count.

# REQUIRES: x86-registered-target

# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR

# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [indirect target type id] for function at [0x0]': unexpected end of data at offset 0x20 while reading [0x20, 0x28)

.text
.globl _Z3foov
.type _Z3foov,@function
_Z3foov:
callq _Z3foov@PLT

.section .callgraph,"o",@progbits,.text
.quad 0 #< Format version number.
.quad 0 #< Function entry address.
.quad 0 #< Function kind.
.quad 1 #< Indirect call site count that follows.
#< Missing indirect call entries here.
.text
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Tests that --call-graph-info fails if .callgraph section does not have
## an expected value, e.g., not as much call sites as the given count.

# REQUIRES: x86-registered-target

# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR
# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR

# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [direct callee PC] for function at [0x0]': unexpected end of data at offset 0x28 while reading [0x28, 0x30)

.text
.globl _Z3foov
.type _Z3foov,@function
_Z3foov:
callq _Z3foov
retq

.section .callgraph,"o",@progbits,.text
.quad 0 #< Format version number.
.quad 0 #< Function entry address.
.quad 0 #< Function kind.
.quad 0 #< Indirect call site count.
.quad 1 #< Direct call site count that follows.
#< Missing direct call entries here.
.text
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## Tests that --call-graph-info warns if there is no .callgraph section.

# REQUIRES: x86-registered-target

# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t
# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t

# CHECK: {{.*}}llvm-readelf{{.*}}: error: 'Missing section': No .callgraph section found.

.text
.globl _Z3foov
.type _Z3foov,@function
_Z3foov:
callq _Z3foov@PLT
Loading
Loading