ebpf_exporter
116 строк · 2.9 Кб
1package exporter
2
3/*
4#include <stdlib.h>
5#include <bpf/libbpf.h>
6
7extern int attachPerfEventCallback(const struct bpf_program *prog,
8long cookie,
9struct bpf_link **link);
10*/
11import "C"
12
13import (
14"fmt"
15"strconv"
16"strings"
17"syscall"
18"unsafe"
19
20"github.com/aquasecurity/libbpfgo"
21"github.com/elastic/go-perf"
22"github.com/iovisor/gobpf/pkg/cpuonline"
23"golang.org/x/sys/unix"
24)
25
26var libbpfPerfHandlers []int
27
28func registerHandlers() error {
29if libbpfPerfHandlers != nil {
30return nil
31}
32
33name := C.CString("perf_event/")
34defer C.free(unsafe.Pointer(name))
35
36opts := C.struct_libbpf_prog_handler_opts{}
37opts.sz = C.sizeof_struct_libbpf_prog_handler_opts
38opts.prog_attach_fn = C.libbpf_prog_attach_fn_t(C.attachPerfEventCallback)
39
40handler := C.libbpf_register_prog_handler(name, uint32(libbpfgo.BPFProgTypePerfEvent), uint32(libbpfgo.BPFAttachTypePerfEvent), &opts)
41if handler < 0 {
42return fmt.Errorf("error registering prog handler: %s", unix.ErrnoName(syscall.Errno(handler)))
43}
44
45libbpfPerfHandlers = append(libbpfPerfHandlers, int(handler))
46
47return nil
48}
49
50func parseSectionConfig(section string) (*perf.Attr, []uint, error) {
51attr := &perf.Attr{}
52
53for _, item := range strings.Split(strings.TrimPrefix(section, "perf_event/"), ",") {
54kv := strings.SplitN(item, "=", 2)
55if len(kv) != 2 {
56return nil, nil, fmt.Errorf("invalid perf_event item: %q", item)
57}
58
59value, err := strconv.Atoi(kv[1])
60if err != nil {
61return nil, nil, fmt.Errorf("error parsing value of item %q as integer: %v", item, err)
62}
63
64switch kv[0] {
65case "type":
66attr.Type = perf.EventType(value)
67case "config":
68attr.Config = uint64(value)
69case "frequency":
70attr.SetSampleFreq(uint64(value))
71default:
72return nil, nil, fmt.Errorf("unknown perf_event item %q", item)
73}
74}
75
76cpus, err := cpuonline.Get()
77if err != nil {
78return nil, nil, fmt.Errorf("failed to determine online cpus: %v", err)
79}
80
81return attr, cpus, nil
82}
83
84func attachPerfEvent(prog *C.struct_bpf_program) ([]*C.struct_bpf_link, error) {
85section := C.GoString(C.bpf_program__section_name(prog))
86
87fa, cpus, err := parseSectionConfig(section)
88if err != nil {
89return nil, fmt.Errorf("failed to parse section %q: %v", section, err)
90}
91
92links := make([]*C.struct_bpf_link, len(cpus))
93
94name := C.GoString(C.bpf_program__name(prog))
95
96for i, cpu := range cpus {
97event, err := perf.Open(fa, perf.AllThreads, int(cpu), nil)
98if err != nil {
99return nil, fmt.Errorf("failed to open perf_event: %v", err)
100}
101
102fd, err := event.FD()
103if err != nil {
104return nil, fmt.Errorf("failed to get perf_event fd: %v", err)
105}
106
107link, err := C.bpf_program__attach_perf_event(prog, C.int(fd))
108if link == nil {
109return nil, fmt.Errorf("failed to attach perf event %d:%d to program %q on cpu %d: %v", fa.Type, fa.Config, name, cpu, err)
110}
111
112links[i] = link
113}
114
115return links, nil
116}
117