Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(fix) buf size for smaller and larger data structures sent as perf events #284

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ sudo -E go run examples/bcc/perf/perf.go
## Tests

```
go test -tags integration -v ./...
sudo go test -tags integration -v ./...
```
100 changes: 98 additions & 2 deletions bpf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,21 @@ import (
"bytes"
"encoding/binary"
"fmt"
"github.com/iovisor/gobpf/pkg/test"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"syscall"
"testing"
"time"
"unsafe"

"github.com/iovisor/gobpf/bcc"
"github.com/iovisor/gobpf/elf"
"github.com/iovisor/gobpf/pkg/bpffs"
"github.com/iovisor/gobpf/pkg/progtestrun"
"github.com/stretchr/testify/assert"
)

// redefine flags here as cgo in test is not supported
Expand Down Expand Up @@ -351,8 +355,8 @@ func checkMaps(t *testing.T, b *elf.Module) {

func checkProbes(t *testing.T, b *elf.Module) {
var expectedProbes = []string{
"kprobe/dummy",
"kretprobe/dummy",
"kprobe/__x64_sys_write",
"kretprobe/__x64_sys_write",
}

var probes []*elf.Kprobe
Expand Down Expand Up @@ -626,6 +630,89 @@ func checkProgTestRun(t *testing.T, b *elf.Module) {
}
}

func checkPerfArray(t *testing.T, b *elf.Module) {
receiverChan := make(chan []byte, 100)
lostChan := make(chan uint64, 100)

perfMap, err := elf.InitPerfMap(b, "dummy_perf", receiverChan, lostChan)
if err != nil {
t.Fatal(err)
}

perfMap.PollStart()
defer perfMap.PollStop()

var actualS1 *test.S1
var actualS2 *test.S2
var actualS8 *test.S8

finished := make(chan bool)

go func() {
L:
for {
select {
case event := <-receiverChan:
s1 := test.ReadS1(event)
if s1 != nil {
actualS1 = s1
}
s2 := test.ReadS2(event)
if s2 != nil {
actualS2 = s2
}
s8 := test.ReadS8(event)
if s8 != nil {
actualS8 = s8
}
if actualS1 != nil && actualS2 != nil && actualS8 != nil {
break L
}
case lost := <-lostChan:
assert.Fail(t, "Unexpectedly lost %d events", lost)
break L
case <-time.After(5000 * time.Millisecond):
assert.Fail(t, "Didn't get all expected messages")
break L
}
}
finished <- true
}()

// perform test operation that should be detected by kprobe/sys_write
tfd, err := ioutil.TempFile("", "*")
if err != nil {
t.Fatal(err)
}
_, err = tfd.WriteString("bpf_integration_test")
if err != nil {
t.Fatal(err)
}
_ = tfd.Close()

<-finished

expectedS1 := test.S1{0x10000011}
expectedS2 := test.S2{0x20000011, 0x20000022}
expectedS8 := test.S8{
A: 0x80000011,
B: 0x80000022,
C: 0x80000033,
D: 0x80000044,
E: 0x80000055,
F: 0x80000066,
G: 0x80000077,
H: 0x80000088,
}

assert.NotNil(t, actualS1)
assert.Equal(t, expectedS1, *actualS1)
assert.NotNil(t, actualS2)
assert.Equal(t, expectedS2, *actualS2)
assert.NotNil(t, actualS8)
assert.Equal(t, expectedS8, *actualS8)
}

func TestModuleLoadELF(t *testing.T) {
var err error
kernelVersion, err = elf.CurrentKernelVersion()
Expand All @@ -648,6 +735,9 @@ func TestModuleLoadELF(t *testing.T) {
"maps/dummy_array_custom": elf.SectionParams{
PinPath: filepath.Join("gobpf-test", "testgroup1"),
},
"maps/dummy_perf": elf.SectionParams{
PerfRingBufferPageCount: 256,
},
}
var closeOptions = map[string]elf.CloseOptions{
"maps/dummy_array_custom": elf.CloseOptions{
Expand All @@ -664,9 +754,14 @@ func TestModuleLoadELF(t *testing.T) {
if b == nil {
t.Fatal("prog is nil")
}
b.EnableOptionCompatProbe()
if err := b.Load(secParams); err != nil {
t.Fatal(err)
}
err = b.EnableKprobes(256)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := b.CloseExt(closeOptions); err != nil {
t.Fatal(err)
Expand All @@ -685,4 +780,5 @@ func TestModuleLoadELF(t *testing.T) {
checkUpdateDeleteElement(t, b)
checkLookupElement(t, b)
checkProgTestRun(t, b)
checkPerfArray(t, b)
}
7 changes: 6 additions & 1 deletion elf/perf.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,12 @@ func (pm *PerfMap) PollStart() {
case 0:
break ringBufferLoop // nothing to read
case C.PERF_RECORD_SAMPLE:
size := sample.Size - 4
var size uint32
if sample.Size > 8 {
size = sample.Size - 4
} else {
size = sample.Size
}
b := C.GoBytes(unsafe.Pointer(&sample.data), C.int(size))
incoming.bytesArray = append(incoming.bytesArray, b)
harvestCount++
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/iovisor/gobpf

go 1.15

require github.com/stretchr/testify v1.7.0
65 changes: 65 additions & 0 deletions pkg/test/test_structs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package test

/*
#include "../../tests/dummy_structs.h"
*/
import "C"
import (
"unsafe"
)

type S1 struct {
A uint
}
type S2 struct {
A uint
B uint
}
type S8 struct {
A uint
B uint
C uint
D uint
E uint
F uint
G uint
H uint
}

func ReadS1(data []byte) *S1 {
if len(data) != int(unsafe.Sizeof(C.struct_S1{})) {
return nil
}
eventC := (*C.struct_S1)(unsafe.Pointer(&data[0]))
return &S1{
A: uint((*eventC).a),
}
}

func ReadS2(data []byte) *S2 {
if len(data) != int(unsafe.Sizeof(C.struct_S2{})) {
return nil
}
eventC := (*C.struct_S2)(unsafe.Pointer(&data[0]))
return &S2{
A: uint((*eventC).a),
B: uint((*eventC).b),
}
}

func ReadS8(data []byte) *S8 {
if len(data) != int(unsafe.Sizeof(C.struct_S8{})) {
return nil
}
eventC := (*C.struct_S8)(unsafe.Pointer(&data[0]))
return &S8{
A: uint((*eventC).a),
B: uint((*eventC).b),
C: uint((*eventC).c),
D: uint((*eventC).d),
E: uint((*eventC).e),
F: uint((*eventC).f),
G: uint((*eventC).g),
H: uint((*eventC).h),
}
}
Binary file modified tests/dummy-410.o
Binary file not shown.
Binary file modified tests/dummy-414.o
Binary file not shown.
Binary file modified tests/dummy-46.o
Binary file not shown.
Binary file modified tests/dummy-48.o
Binary file not shown.
36 changes: 30 additions & 6 deletions tests/dummy.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@

#include "../elf/include/uapi/linux/bpf.h"
#include "../elf/include/bpf_map.h"
#include "./dummy_structs.h"

#define SEC(NAME) __attribute__((section(NAME), used))

static int (*bpf_perf_event_output)(void *ctx, void *map,
unsigned long long flags, void *data,
int size) =
(void *) BPF_FUNC_perf_event_output;

#define PERF_MAX_STACK_DEPTH 127

#define KERNEL_VERSION_GTE(X) (KERNEL_VERSION >= X)
Expand Down Expand Up @@ -81,14 +87,30 @@ struct bpf_map_def SEC("maps/dummy_array_custom") dummy_array_custom = {
.pinning = PIN_CUSTOM_NS,
};

SEC("kprobe/dummy")
int kprobe__dummy(struct pt_regs *ctx)
{
return 0;
SEC("kprobe/sys_write")
int kprobe__sys_write(struct pt_regs *ctx) {
struct S1 s1 = {0x10000011};
struct S2 s2 = {0x20000011, 0x20000022};
struct S8 s8 = {
.a = 0x80000011,
.b = 0x80000022,
.c = 0x80000033,
.d = 0x80000044,
.e = 0x80000055,
.f = 0x80000066,
.g = 0x80000077,
.h = 0x80000088
};

bpf_perf_event_output(ctx, &dummy_perf, 0, &s1, sizeof(s1));
bpf_perf_event_output(ctx, &dummy_perf, 0, &s2, sizeof(s2));
bpf_perf_event_output(ctx, &dummy_perf, 0, &s8, sizeof(s8));
return 0;
}

SEC("kretprobe/dummy")
int kretprobe__dummy(struct pt_regs *ctx)

SEC("kretprobe/sys_write")
int kretprobe__sys_write(struct pt_regs *ctx)
{
return 0;
}
Expand Down Expand Up @@ -146,3 +168,5 @@ int xdp_pass(struct xdp_md *ctx) {
#endif

unsigned int _version SEC("version") = 0xFFFFFFFE;

char _license[] SEC("license") = "GPL";
Binary file modified tests/dummy.o
Binary file not shown.
18 changes: 18 additions & 0 deletions tests/dummy_structs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

struct S1 {
unsigned int a;
};
struct S2 {
unsigned int a;
unsigned int b;
};
struct S8 {
unsigned int a;
unsigned int b;
unsigned int c;
unsigned int d;
unsigned int e;
unsigned int f;
unsigned int g;
unsigned int h;
};