inspektor-gadget
141 строка · 3.3 Кб
1// Copyright 2023 The Inspektor Gadget authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package btfgen provides a way to load BTF information generated with btfgen. Files to be
16// incluided into the binary have to be generated with BTFGen (make btfgen on the root) before
17// compiling the binary.
18package btfgen
19
20import (
21"bufio"
22"bytes"
23_ "embed"
24"fmt"
25"os"
26"path/filepath"
27"runtime"
28"strings"
29"sync"
30
31"github.com/cilium/ebpf/btf"
32"github.com/inspektor-gadget/inspektor-gadget/pkg/utils/host"
33log "github.com/sirupsen/logrus"
34"golang.org/x/sys/unix"
35)
36
37var (
38spec *btf.Spec
39once sync.Once
40)
41
42func initialize() error {
43// If the kernel exposes BTF; nothing to do
44_, err := btf.LoadKernelSpec()
45if err == nil {
46return nil
47}
48
49info, err := getOSInfo()
50if err != nil {
51return err
52}
53
54// architecture naming is a mess:
55// - Golang uses amd64 and arm64
56// - btfhub uses x86_64 and arm64
57// - bpf2go uses x86 and arm64
58goarch := runtime.GOARCH
59if goarch == "amd64" {
60goarch = "x86"
61}
62
63btfFile := fmt.Sprintf("btfs/%s/%s/%s/%s/%s.btf",
64goarch, info.ID, info.VersionID, info.Arch, info.Kernel)
65
66file, err := btfs.ReadFile(btfFile)
67if err != nil {
68return fmt.Errorf("reading %s BTF file %w", btfFile, err)
69}
70
71s, err := btf.LoadSpecFromReader(bytes.NewReader(file))
72if err != nil {
73return fmt.Errorf("loading BTF spec: %w", err)
74}
75
76spec = s
77return nil
78}
79
80// GetBTFSpec returns the BTF spec with kernel information for the current kernel version. If the
81// kernel exposes BTF information or if the BTF for this kernel is not found, it returns nil.
82func GetBTFSpec() *btf.Spec {
83once.Do(func() {
84err := initialize()
85if err != nil {
86log.Warnf("Failed to initialize BTF: %v", err)
87}
88})
89return spec
90}
91
92type osInfo struct {
93ID string
94VersionID string
95Arch string
96Kernel string
97}
98
99func getOSInfo() (*osInfo, error) {
100osInfo := &osInfo{}
101
102file, err := os.Open(filepath.Join(host.HostRoot, "/etc/os-release"))
103if err != nil {
104return nil, fmt.Errorf("opening file: %w", err)
105}
106defer file.Close()
107
108scanner := bufio.NewScanner(file)
109for scanner.Scan() {
110line := scanner.Text()
111parts := strings.SplitN(line, "=", 2)
112if len(parts) != 2 {
113continue
114}
115
116switch parts[0] {
117case "ID":
118osInfo.ID = parts[1]
119case "VERSION_ID":
120osInfo.VersionID = strings.Trim(parts[1], "\"")
121}
122}
123
124if osInfo.ID == "" || osInfo.VersionID == "" {
125return nil, fmt.Errorf("os-release file is incomplete")
126}
127
128if err := scanner.Err(); err != nil {
129return nil, fmt.Errorf("scanning file: %w", err)
130}
131
132uts := &unix.Utsname{}
133if err := unix.Uname(uts); err != nil {
134return nil, fmt.Errorf("calling uname: %w", err)
135}
136
137osInfo.Kernel = unix.ByteSliceToString(uts.Release[:])
138osInfo.Arch = unix.ByteSliceToString(uts.Machine[:])
139
140return osInfo, nil
141}
142