Skip to content

Commit

Permalink
btf: move vmlinux search logic into findVMLinux()
Browse files Browse the repository at this point in the history
loadKernelSpec() gets short-circuited in the first statement on most kernels,
resulting in the vmlinux search code being rarely exercised successfully in CI.

Split the search logic out into its own function to make it testable in
isolation. Although virtme VMs don't have images at any of these paths,
running the test suite on a host with a full distro does result in a proper
test execution.

Signed-off-by: Timo Beckers <[email protected]>
  • Loading branch information
ti-mo committed Feb 11, 2022
1 parent 0e6f2cf commit 3418f34
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 18 deletions.
37 changes: 19 additions & 18 deletions internal/btf/btf.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@ func LoadKernelSpec() (*Spec, error) {
return kernelBTF.Spec, err
}

// loadKernelSpec attempts to load the raw vmlinux BTF blob at
// /sys/kernel/btf/vmlinux and falls back to scanning the file system
// for vmlinux ELFs.
func loadKernelSpec() (*Spec, error) {
fh, err := os.Open("/sys/kernel/btf/vmlinux")
if err == nil {
Expand All @@ -320,13 +323,21 @@ func loadKernelSpec() (*Spec, error) {
return loadRawSpec(fh, internal.NativeEndian, nil, nil)
}

var uname unix.Utsname
if err := unix.Uname(&uname); err != nil {
return nil, fmt.Errorf("uname failed: %w", err)
file, err := findVMLinux()
if err != nil {
return nil, err
}
defer file.Close()

return loadSpecFromELF(file)
}

end := bytes.IndexByte(uname.Release[:], 0)
release := string(uname.Release[:end])
// findVMLinux scans multiple well-known paths for vmlinux kernel images.
func findVMLinux() (*internal.SafeELFFile, error) {
release, err := internal.KernelRelease()
if err != nil {
return nil, err
}

// use same list of locations as libbpf
// https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122
Expand All @@ -341,24 +352,14 @@ func loadKernelSpec() (*Spec, error) {
}

for _, loc := range locations {
path := fmt.Sprintf(loc, release)

fh, err := os.Open(path)
fh, err := os.Open(fmt.Sprintf(loc, release))
if err != nil {
continue
}
defer fh.Close()

file, err := internal.NewSafeELFFile(fh)
if err != nil {
return nil, err
}
defer file.Close()

return loadSpecFromELF(file)
return internal.NewSafeELFFile(fh)
}

return nil, fmt.Errorf("no BTF for kernel version %s: %w", release, internal.ErrNotSupported)
return nil, fmt.Errorf("no BTF found for kernel version %s: %w", release, internal.ErrNotSupported)
}

// parseBTFHeader parses the header of the .BTF section.
Expand Down
17 changes: 17 additions & 0 deletions internal/btf/btf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,23 @@ func TestParseCurrentKernelBTF(t *testing.T) {
}
}

func TestFindVMLinux(t *testing.T) {
file, err := findVMLinux()
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal("Can't find vmlinux:", err)
}

spec, err := loadSpecFromELF(file)
if err != nil {
t.Fatal("Can't load BTF:", err)
}

if len(spec.namedTypes) == 0 {
t.Fatal("Empty kernel BTF")
}
}

func TestLoadSpecFromElf(t *testing.T) {
testutils.Files(t, testutils.Glob(t, "../../testdata/loader-e*.elf"), func(t *testing.T, file string) {
spec := parseELFBTF(t, file)
Expand Down
15 changes: 15 additions & 0 deletions internal/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package internal
import (
"fmt"
"sync"

"github.com/cilium/ebpf/internal/unix"
)

const (
Expand Down Expand Up @@ -105,3 +107,16 @@ func detectKernelVersion() (Version, error) {
}
return NewVersionFromCode(vc), nil
}

// KernelRelease returns the release string of the running kernel.
// Its format depends on the Linux distribution and corresponds to directory
// names in /lib/modules by convention. Some examples are 5.15.17-1-lts and
// 4.19.0-16-amd64.
func KernelRelease() (string, error) {
var uname unix.Utsname
if err := unix.Uname(&uname); err != nil {
return "", fmt.Errorf("uname failed: %w", err)
}

return unix.ByteSliceToString(uname.Release[:]), nil
}
11 changes: 11 additions & 0 deletions internal/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,14 @@ func TestVersionFromCode(t *testing.T) {
})
}
}

func TestKernelRelease(t *testing.T) {
r, err := KernelRelease()
if err != nil {
t.Fatal(err)
}

if r == "" {
t.Fatal("unexpected empty kernel release")
}
}

0 comments on commit 3418f34

Please sign in to comment.