Skip to content

Commit 600e782

Browse files
committed
Add tests
1 parent 18c73d3 commit 600e782

File tree

6 files changed

+119
-19
lines changed

6 files changed

+119
-19
lines changed

pclntab/pclntab.go

+10-6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ const (
3535
magicGo1_16 = 0xfffffffa
3636
magicGo1_18 = 0xfffffff0
3737
magicGo1_20 = 0xfffffff1
38+
39+
disableRecover = true
3840
)
3941

4042
type GoPCLnTabInfo struct {
@@ -529,12 +531,14 @@ func parseGoPCLnTab(data []byte) (*GoPCLnTabInfo, error) {
529531
}
530532

531533
func FindGoPCLnTab(ef *pfelf.File) (goPCLnTabInfo *GoPCLnTabInfo, err error) {
532-
// gopclntab parsing code might panic if the data is corrupt.
533-
defer func() {
534-
if r := recover(); r != nil {
535-
err = fmt.Errorf("panic while searching pclntab: %v", r)
536-
}
537-
}()
534+
if !disableRecover {
535+
// gopclntab parsing code might panic if the data is corrupt.
536+
defer func() {
537+
if r := recover(); r != nil {
538+
err = fmt.Errorf("panic while searching pclntab: %v", r)
539+
}
540+
}()
541+
}
538542

539543
var data []byte
540544
var goPCLnTabAddr uint64

pclntab/pclntab_test.go

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
2+
// Copyright 2024 Datadog, Inc.
3+
4+
package pclntab
5+
6+
import (
7+
"fmt"
8+
"os/exec"
9+
"path/filepath"
10+
"runtime"
11+
"strings"
12+
"testing"
13+
14+
"github.com/stretchr/testify/require"
15+
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
16+
)
17+
18+
func getGoToolChain(goMinorVersion int) string {
19+
if goMinorVersion < 21 {
20+
return fmt.Sprintf("GOTOOLCHAIN=go1.%v", goMinorVersion)
21+
}
22+
return fmt.Sprintf("GOTOOLCHAIN=go1.%v.0", goMinorVersion)
23+
}
24+
25+
func TestGoPCLnTabExtraction(t *testing.T) {
26+
t.Parallel()
27+
testDataDir := "../testdata"
28+
srcFile := "helloworld.go"
29+
tests := map[string]struct {
30+
buildArgs []string
31+
}{
32+
// helloworld is a very basic Go binary without special build flags.
33+
"std": {},
34+
// helloworld.pie is a Go binary that is build with PIE enabled.
35+
"pie": {buildArgs: []string{"-buildmode=pie"}},
36+
// helloworld.stripped.pie is a Go binary that is build with PIE enabled and all debug
37+
// information stripped.
38+
"pie.sw": {buildArgs: []string{"-buildmode=pie", "-ldflags=-s -w"}},
39+
}
40+
41+
tmpDir := t.TempDir()
42+
goMinorVersions := []int{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}
43+
for _, goMinorVersion := range goMinorVersions {
44+
for name, test := range tests {
45+
if goMinorVersion <= 12 && strings.HasPrefix(name, "pie") {
46+
continue
47+
}
48+
if runtime.GOARCH == "arm64" && goMinorVersion <= 8 {
49+
continue
50+
}
51+
t.Run(fmt.Sprintf("go1.%v#%v", goMinorVersion, name), func(t *testing.T) {
52+
t.Parallel()
53+
exe := filepath.Join(tmpDir, fmt.Sprintf("%v.v1_%v.%v", strings.TrimRight(srcFile, ".go"), goMinorVersion, name))
54+
cmd := exec.Command("go", append([]string{"build", "-o", exe}, test.buildArgs...)...) // #nosec G204
55+
cmd.Args = append(cmd.Args, srcFile)
56+
cmd.Dir = testDataDir
57+
cmd.Env = append(cmd.Environ(), getGoToolChain(goMinorVersion))
58+
out, err := cmd.CombinedOutput()
59+
require.NoError(t, err, "failed to build test binary with `%v`: %s\n%s", cmd.String(), err, out)
60+
61+
ef, err := pfelf.Open(exe)
62+
require.NoError(t, err)
63+
defer ef.Close()
64+
65+
goPCLnTabInfo, err := FindGoPCLnTab(ef)
66+
require.NoError(t, err)
67+
require.NotNil(t, goPCLnTabInfo)
68+
69+
if goMinorVersion >= 18 {
70+
require.NotNil(t, goPCLnTabInfo.GoFuncAddr)
71+
}
72+
73+
exeStripped := exe + ".stripped"
74+
out, err = exec.Command("objcopy", "-S", "--rename-section", ".data.rel.ro.gopclntab=.foo1", "--rename-section", ".gopclntab=.foo2", exe, exeStripped).CombinedOutput() // #nosec G204
75+
require.NoError(t, err, "failed to rename section: %s\n%s", err, out)
76+
77+
goPCLnTabInfo2, err := FindGoPCLnTab(ef)
78+
require.NoError(t, err)
79+
require.NotNil(t, goPCLnTabInfo2)
80+
81+
require.Equal(t, goPCLnTabInfo.GoFuncAddr, goPCLnTabInfo2.GoFuncAddr)
82+
require.Equal(t, goPCLnTabInfo.Address, goPCLnTabInfo2.Address)
83+
require.GreaterOrEqual(t, len(goPCLnTabInfo2.Data), len(goPCLnTabInfo.Data))
84+
require.GreaterOrEqual(t, len(goPCLnTabInfo2.GoFuncData), len(goPCLnTabInfo.GoFuncData))
85+
})
86+
}
87+
}
88+
}

reporter/symbol_uploader.go

+12-9
Original file line numberDiff line numberDiff line change
@@ -606,20 +606,19 @@ func openELF(filePath string, opener process.FileOpener) (*elfWrapper, error) {
606606
// findSymbols attempts to find a symbol source for the elf file, it returns an elfWrapper around the elf file
607607
// with symbols if found, or nil if no symbols were found.
608608
func (e *elfWrapper) findSymbols(uploadGoPCLnTab bool) (*elfWrapper, SymbolSource, *pclntab.GoPCLnTabInfo) {
609+
var goPCLnTabInfo *pclntab.GoPCLnTabInfo
610+
609611
// Check if the elf file has a GoPCLnTab
610612
if uploadGoPCLnTab {
611-
goPCLnTabInfo, err := pclntab.FindGoPCLnTab(e.elfFile)
612-
if err == nil {
613-
if goPCLnTabInfo != nil {
614-
return e, GoPCLnTab, goPCLnTabInfo
615-
}
616-
} else {
613+
var err error
614+
goPCLnTabInfo, err = pclntab.FindGoPCLnTab(e.elfFile)
615+
if err != nil {
617616
log.Warnf("Failed to find .gopclntab in %s: %v", e.filePath, err)
618617
}
619618
}
620619

621620
if HasDWARFData(e.elfFile) {
622-
return e, DebugInfo, nil
621+
return e, DebugInfo, goPCLnTabInfo
623622
}
624623

625624
log.Debugf("No debug symbols found in %s", e.filePath)
@@ -632,7 +631,7 @@ func (e *elfWrapper) findSymbols(uploadGoPCLnTab bool) (*elfWrapper, SymbolSourc
632631
debugElf := e.findDebugSymbolsWithBuildID()
633632
if debugElf != nil {
634633
if HasDWARFData(debugElf.elfFile) {
635-
return debugElf, DebugInfo, nil
634+
return debugElf, DebugInfo, goPCLnTabInfo
636635
}
637636
debugElf.Close()
638637
log.Debugf("No debug symbols found in buildID link file %s", debugElf.filePath)
@@ -642,12 +641,16 @@ func (e *elfWrapper) findSymbols(uploadGoPCLnTab bool) (*elfWrapper, SymbolSourc
642641
debugElf = e.findDebugSymbolsWithDebugLink()
643642
if debugElf != nil {
644643
if HasDWARFData(debugElf.elfFile) {
645-
return debugElf, DebugInfo, nil
644+
return debugElf, DebugInfo, goPCLnTabInfo
646645
}
647646
log.Debugf("No debug symbols found in debug link file %s", debugElf.filePath)
648647
debugElf.Close()
649648
}
650649

650+
if goPCLnTabInfo != nil {
651+
return e, GoPCLnTab, goPCLnTabInfo
652+
}
653+
651654
// Check if initial elf file has a symbol table
652655
if e.elfFile.Section(".symtab") != nil {
653656
return e, SymbolTable, nil

reporter/symbol_uploader_test.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ func checkGoPCLnTab(t *testing.T, filename string, checkGoFunc bool) {
6565
}
6666

6767
func checkGoPCLnTabExtraction(t *testing.T, filename, tmpDir string) {
68-
f, err := pfelf.Open(filename)
68+
ef, err := pfelf.Open(filename)
6969
require.NoError(t, err)
70-
goPCLnTabInfo, err := pclntab.FindGoPCLnTab(f)
70+
defer ef.Close()
71+
72+
goPCLnTabInfo, err := pclntab.FindGoPCLnTab(ef)
7173
require.NoError(t, err)
7274
assert.NotNil(t, goPCLnTabInfo)
7375

@@ -79,7 +81,7 @@ func checkGoPCLnTabExtraction(t *testing.T, filename, tmpDir string) {
7981

8082
func TestGoPCLnTabExtraction(t *testing.T) {
8183
t.Parallel()
82-
srcFile := "./testdata/helloworld.go"
84+
srcFile := "../testdata/helloworld.go"
8385
tests := map[string]struct {
8486
buildArgs []string
8587
}{
@@ -97,14 +99,14 @@ func TestGoPCLnTabExtraction(t *testing.T) {
9799
t.Parallel()
98100
tmpDir := t.TempDir()
99101
exe := filepath.Join(tmpDir, strings.TrimRight(srcFile, ".go")+"."+name)
100-
exeStripped := exe + ".stripped"
101102
cmd := exec.Command("go", append([]string{"build", "-o", exe}, test.buildArgs...)...) // #nosec G204
102103
cmd.Args = append(cmd.Args, srcFile)
103104
out, err := cmd.CombinedOutput()
104105
require.NoError(t, err, "failed to build test binary with `%v`: %s\n%s", cmd.Args, err, out)
105106

106107
checkGoPCLnTabExtraction(t, exe, tmpDir)
107108

109+
exeStripped := exe + ".stripped"
108110
out, err = exec.Command("objcopy", "-S", "--rename-section", ".data.rel.ro.gopclntab=.foo1", "--rename-section", ".gopclntab=.foo2", exe, exeStripped).CombinedOutput() // #nosec G204
109111
require.NoError(t, err, "failed to rename section: %s\n%s", err, out)
110112
checkGoPCLnTabExtraction(t, exeStripped, tmpDir)

testdata/go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module helloworld
2+
3+
go 1.2
File renamed without changes.

0 commit comments

Comments
 (0)