When compiler optimization inlines functions, related symbols are tricky to retrieve since they are no longer explicitely listed in the ELF symbol table.
Support for such cases would be highly beneficial.
For instance, the Go compiler often inlines functions with low LoC:
package main
import (
"fmt"
"os"
)
//go:noinline
func add(a, b int) int {
return a + b
}
//go:noinline
func multiply(a, b int) int {
return a * b
}
//go:noinline
func subtract(a, b int) int {
return a - b
}
//go:noinline
func divide(a, b int) int {
if b == 0 {
return 0
}
return a / b
}
//go:noinline
func greet(name string) {
fmt.Printf("Hello, %s!\n", name)
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: demo-app <command>")
fmt.Println("Commands: add, multiply, greet")
os.Exit(1)
}
cmd := os.Args[1]
switch cmd {
case "add":
result := add(10, 20)
fmt.Printf("10 + 20 = %d\n", result)
case "multiply":
result := multiply(5, 6)
fmt.Printf("5 * 6 = %d\n", result)
case "greet":
greet("xcover")
case "all":
// Call all functions for maximum coverage
fmt.Println("Running all functions:")
fmt.Printf("Add: %d\n", add(10, 20))
fmt.Printf("Multiply: %d\n", multiply(5, 6))
fmt.Printf("Subtract: %d\n", subtract(30, 10))
fmt.Printf("Divide: %d\n", divide(100, 5))
greet("xcover")
default:
fmt.Printf("Unknown command: %s\n", cmd)
}
}
$ go build .
$ go tool nm tmp | grep -E " main\."
499e00 T main.add
499e60 T main.divide
499e80 T main.greet
499f00 T main.main
499e20 T main.multiply
499e40 T main.subtract
without the noinline annotation, Go compiler would inline all the functions into the main one:
$ go build .
$ go tool nm demo-inlined | grep -E " main\."
499e00 T main.main
VA range from the DWARF info:
With inlined functions:
$ readelf -wi ./demo-inline | grep -A2 " main\."
<1dd2> DW_AT_name : main.greet
<1ddd> DW_AT_inline : 1 (inlined)
<1dde> DW_AT_decl_line : 27
--
<1e46> DW_AT_name : main.main
<1e50> DW_AT_low_pc : (index: 0x1): 0x499e00
<1e51> DW_AT_high_pc : 1174
Without:
$ readelf -wi ./demo-noinline | grep -A2 " main\."
<1e2b> DW_AT_name : main.add
<1e34> DW_AT_low_pc : (index: 0x1): 0x499e00
<1e35> DW_AT_high_pc : 4
--
<1e66> DW_AT_name : main.multiply
<1e74> DW_AT_low_pc : (index: 0x2): 0x499e20
<1e75> DW_AT_high_pc : 5
--
<1ea6> DW_AT_name : main.subtract
<1eb4> DW_AT_low_pc : (index: 0x3): 0x499e40
<1eb5> DW_AT_high_pc : 4
--
<1ee6> DW_AT_name : main.divide
<1ef2> DW_AT_low_pc : (index: 0x4): 0x499e60
<1ef3> DW_AT_high_pc : 27
--
<1f24> DW_AT_name : main.greet
<1f2f> DW_AT_low_pc : (index: 0x5): 0x499e80
<1f30> DW_AT_high_pc : 125
--
<1f66> DW_AT_name : main.main
<1f70> DW_AT_low_pc : (index: 0x6): 0x499f00
<1f71> DW_AT_high_pc : 1110
When compiler optimization inlines functions, related symbols are tricky to retrieve since they are no longer explicitely listed in the ELF symbol table.
Support for such cases would be highly beneficial.
For instance, the Go compiler often inlines functions with low LoC:
without the
noinlineannotation, Go compiler would inline all the functions into themainone:VA range from the DWARF info:
With inlined functions:
Without: