Skip to content

Commit ea4430f

Browse files
committed
Add LBR decoder support
1 parent 31fe146 commit ea4430f

File tree

7 files changed

+179
-0
lines changed

7 files changed

+179
-0
lines changed

.vscode/config-schema.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ definitions:
145145
- inet_ip
146146
- kstack
147147
- ksym
148+
- lbr
148149
- majorminor
149150
- pci_class
150151
- pci_device

cmd/ebpf_exporter/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func main() {
4242
capabilities := kingpin.Flag("capabilities.keep", "Comma separated list of capabilities to keep (cap_syslog, cap_bpf, etc.), 'all' or 'none'").Default("all").String()
4343
btfPath := kingpin.Flag("btf.path", "Optional BTF file path.").Default("").String()
4444
skipCacheSize := kingpin.Flag("config.skip-cache-size", "Size of the LRU skip cache").Int()
45+
lbrEnable := kingpin.Flag("lbr.enable", "Enable LBR.").Bool()
4546
kingpin.Version(version.Print("ebpf_exporter"))
4647
kingpin.HelpFlag.Short('h')
4748
kingpin.Parse()
@@ -75,6 +76,15 @@ func main() {
7576

7677
started := time.Now()
7778

79+
if *lbrEnable {
80+
notify("enabling lbr...")
81+
82+
err := exporter.EnableLBR()
83+
if err != nil {
84+
log.Fatalf("Error enabling LBR: %v", err)
85+
}
86+
}
87+
7888
notify("parsing config...")
7989

8090
configs, err := config.ParseConfigs(*configDir, strings.Split(*configNames, ","))

decoder/decoder.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func NewSet(skipCacheSize int, monitor *cgroup.Monitor) (*Set, error) {
4646
"inet_ip": &InetIP{},
4747
"kstack": &KStack{ksym},
4848
"ksym": &KSym{ksym},
49+
"lbr": &LBR{ksym},
4950
"majorminor": &MajorMinor{},
5051
"pci_class": &PCIClass{},
5152
"pci_device": &PCIDevice{},

decoder/lbr.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package decoder
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/cloudflare/ebpf_exporter/v2/config"
8+
"github.com/cloudflare/ebpf_exporter/v2/kallsyms"
9+
"github.com/cloudflare/ebpf_exporter/v2/util"
10+
)
11+
12+
// struct perf_branch_entry is 24 bytes
13+
const perfBranchEntrySize = 24
14+
15+
// LBR is a decoder that transforms LBR entry array into a stack
16+
type LBR struct {
17+
decoder *kallsyms.Decoder
18+
}
19+
20+
// Decode transforms LBR entry array into a stack
21+
func (l *LBR) Decode(in []byte, _ config.Decoder) ([]byte, error) {
22+
if len(in) == 0 {
23+
return []byte("<empty>"), nil
24+
}
25+
26+
byteOrder := util.GetHostByteOrder()
27+
28+
lines := make([]string, len(in)/perfBranchEntrySize)
29+
30+
for i := range lines {
31+
from := uintptr(byteOrder.Uint64(in[i*24 : i*24+8]))
32+
to := uintptr(byteOrder.Uint64(in[i*24+8 : i*24+16]))
33+
34+
lines[i] = fmt.Sprintf("0x%08x -> 0x%08x | %s -> %s", from, to, l.decoder.Sym(from), l.decoder.Sym(to))
35+
}
36+
37+
return []byte(strings.Join(lines, "\n")), nil
38+
}

examples/lbr-trace.bpf.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <vmlinux.h>
2+
#include <bpf/bpf_tracing.h>
3+
#include "tracing.bpf.h"
4+
5+
// On Zen v4 this is the depth.
6+
#define LBR_DEPTH 16
7+
8+
struct failure_span_t {
9+
struct span_base_t span_base;
10+
struct perf_branch_entry entries[LBR_DEPTH];
11+
u32 errno;
12+
};
13+
14+
struct {
15+
__uint(type, BPF_MAP_TYPE_RINGBUF);
16+
__uint(max_entries, 256 * 1024);
17+
} failure_spans SEC(".maps");
18+
19+
SEC("fexit/do_sys_openat2")
20+
int BPF_PROG(do_sys_openat2, int dfd, const char *filename, struct open_how *how, int retval)
21+
{
22+
struct failure_span_t span = { 0 };
23+
struct failure_span_t *submit;
24+
u64 ts;
25+
26+
if (retval >= 0) {
27+
return 0;
28+
}
29+
30+
s64 snapshot_bytes = bpf_get_branch_snapshot(span.entries, sizeof(span.entries), 0);
31+
if (snapshot_bytes == 0) {
32+
return 0;
33+
}
34+
35+
u64 count = snapshot_bytes / sizeof(struct perf_branch_entry);
36+
for (int i = 0; i < count; i++) {
37+
if (i >= LBR_DEPTH) {
38+
break;
39+
}
40+
41+
bpf_printk(" lbr: %llx -> %llx", span.entries[i].from, span.entries[i].to);
42+
}
43+
44+
ts = bpf_ktime_get_ns();
45+
46+
span.span_base.span_id = ts;
47+
span.span_base.parent.trace_id_lo = ts;
48+
span.errno = -retval;
49+
50+
submit = bpf_ringbuf_reserve(&failure_spans, sizeof(struct failure_span_t), 0);
51+
if (!submit) {
52+
return 0;
53+
}
54+
55+
*submit = span;
56+
57+
bpf_ringbuf_submit(submit, 0);
58+
59+
return 0;
60+
}
61+
62+
char LICENSE[] SEC("license") = "GPL";

examples/lbr-trace.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
tracing:
2+
spans:
3+
- name: failure
4+
ringbuf: failure_spans
5+
service: syscall-failures
6+
labels:
7+
- name: trace_id
8+
size: 16
9+
decoders:
10+
- name: hex
11+
- name: parent_span_id
12+
size: 8
13+
decoders:
14+
- name: hex
15+
- name: span_id
16+
size: 8
17+
decoders:
18+
- name: hex
19+
- name: span_monotonic_timestamp_ns
20+
size: 8
21+
decoders:
22+
- name: uint
23+
- name: span_duration_ns
24+
size: 8
25+
decoders:
26+
- name: uint
27+
- name: data
28+
size: 384
29+
decoders:
30+
- name: lbr
31+
- name: errno
32+
size: 4
33+
decoders:
34+
- name: uint
35+
- name: errno

exporter/lbr.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package exporter
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/elastic/go-perf"
7+
"github.com/iovisor/gobpf/pkg/cpuonline"
8+
"golang.org/x/sys/unix"
9+
)
10+
11+
// EnableLBR configures a perf event that enables LBR
12+
func EnableLBR() error {
13+
attr := &perf.Attr{}
14+
attr.Type = perf.HardwareEvent
15+
attr.Config = unix.PERF_COUNT_HW_CPU_CYCLES
16+
attr.SampleFormat = perf.SampleFormat{BranchStack: true}
17+
attr.BranchSampleFormat = perf.BranchSampleFormat{Privilege: perf.BranchPrivilegeKernel, Sample: perf.BranchSampleAnyReturn}
18+
19+
cpus, err := cpuonline.Get()
20+
if err != nil {
21+
return fmt.Errorf("failed to determine online cpus: %w", err)
22+
}
23+
24+
for _, cpu := range cpus {
25+
_, err := perf.Open(attr, perf.AllThreads, int(cpu), nil)
26+
if err != nil {
27+
return fmt.Errorf("failed to open perf_event: %w", err)
28+
}
29+
}
30+
31+
return nil
32+
}

0 commit comments

Comments
 (0)