Skip to content

Commit

Permalink
fix ebpf type layout mismatch with binary.Read
Browse files Browse the repository at this point in the history
  • Loading branch information
brycekahle committed Jan 15, 2025
1 parent 3cafc4d commit 367790e
Show file tree
Hide file tree
Showing 39 changed files with 634 additions and 62 deletions.
7 changes: 4 additions & 3 deletions pkg/collector/corechecks/ebpf/c/runtime/oom-kill-kern-user.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#endif

struct oom_stats {
char cgroup_name[129];
// Pid of killed process
__u32 victim_pid;
// Pid of triggering process
Expand All @@ -19,12 +18,14 @@ struct oom_stats {
char trigger_comm[TASK_COMM_LEN];
// OOM score of killed process
__s64 score;
// OOM score adjustment of killed process
__s16 score_adj;
// Total number of pages
__u64 pages;
// Tracks if the OOM kill was triggered by a cgroup
__u32 memcg_oom;
// OOM score adjustment of killed process
__s16 score_adj;

char cgroup_name[129];
};

#endif /* defined(OOM_KILL_KERN_USER_H) */

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions pkg/collector/corechecks/ebpf/probe/oomkill/c_types_linux.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions pkg/collector/corechecks/ebpf/probe/oomkill/c_types_linux_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/dynamicinstrumentation/codegen/c/base_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
// standard fields which all events created in bpf will contain, regardless of the function that the
// probe is instrumenting
struct base_event {
char probe_id[36]; // identifier for each user-configured instrumentation point, it's a standard 36 character UUID
__u32 pid; // process ID
__u32 uid; // user ID
__u64 program_counters[10]; // program counters representing the stack trace of the instrumented function invocation
}__attribute__((aligned(8)));
char probe_id[36]; // identifier for each user-configured instrumentation point, it's a standard 36 character UUID
};

#endif
3 changes: 2 additions & 1 deletion pkg/dynamicinstrumentation/ditypes/ebpf_linux.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions pkg/dynamicinstrumentation/ditypes/ebpf_linux_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/ebpf/c/lock_contention.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct lock_range {
unsigned long long addr_start;
unsigned long long range;
lock_type_t type;
} __attribute__((packed));
};

struct contention_data {
unsigned long long total_time;
Expand Down
83 changes: 79 additions & 4 deletions pkg/ebpf/cgo/genpost.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
package main

import (
"bufio"
"bytes"
"fmt"
"go/format"
"io"
"log"
Expand All @@ -17,11 +20,27 @@ import (
)

func main() {
b, err := io.ReadAll(os.Stdin)
if err != nil {
var buf bytes.Buffer
rdr := io.TeeReader(os.Stdin, &buf)
if err := processFile(rdr, os.Stdout); err != nil {
log.Fatal(err)
}

if len(os.Args) >= 3 && os.Args[1] != "" && os.Args[2] != "" {
testsFile, packageName := os.Args[1], os.Args[2]
// we must add the .go extension here, otherwise `go run` will try to run this path
if err := writeTests(&buf, testsFile+".go", packageName); err != nil {
log.Fatal(err)
}
}
}

func processFile(rdr io.Reader, out io.Writer) error {
b, err := io.ReadAll(rdr)
if err != nil {
return err
}

b = removeAbsolutePath(b, runtime.GOOS)

int8variableNames := []string{
Expand All @@ -48,12 +67,17 @@ func main() {
convertPointerToUint64Regex := regexp.MustCompile(`\*_Ctype_struct_(\w+)`)
b = convertPointerToUint64Regex.ReplaceAll(b, []byte("uint64"))

// Convert *byte pointers to uint64 (original void * in C)
convertBytePointerToUint64Regex := regexp.MustCompile(`(\s+)(\*byte)`)
b = convertBytePointerToUint64Regex.ReplaceAll(b, []byte("${1}uint64"))

b, err = format.Source(b)
if err != nil {
log.Fatal(err)
return err
}

os.Stdout.Write(b)
_, err = out.Write(b)
return err
}

// removeAbsolutePath removes the absolute file path that is automatically output by cgo -godefs
Expand All @@ -71,3 +95,54 @@ func removeAbsolutePath(b []byte, platform string) []byte {

return removeAbsolutePathRegex.ReplaceAll(b, []byte("$1 $2"))
}

var testHeaderTemplate = `// Code generated by genpost.go; DO NOT EDIT.
package %s
`

var testImportTemplate = `
import (
"testing"
"github.com/DataDog/datadog-agent/pkg/ebpf/ebpftest"
)
`

var testTemplate = `
func TestCgoAlignment_%[1]s(t *testing.T) {
ebpftest.TestCgoAlignment[%[1]s](t)
}
`

func writeTests(rdr io.Reader, dstFile string, packageName string) error {
dst, err := os.Create(dstFile)
if err != nil {
return err
}
defer dst.Close()
fmt.Fprintf(dst, testHeaderTemplate, packageName)

var typeNames []string
structRegex := regexp.MustCompile("type ([^ ]+) struct")
scanner := bufio.NewScanner(rdr)
for scanner.Scan() {
matches := structRegex.FindSubmatch(scanner.Bytes())
if len(matches) >= 2 {
typeName := matches[1]
typeNames = append(typeNames, string(typeName))
}
}
if err := scanner.Err(); err != nil {
return err
}
if len(typeNames) == 0 {
return nil
}

fmt.Fprint(dst, testImportTemplate)
for _, typeName := range typeNames {
fmt.Fprintf(dst, testTemplate, typeName)
}
return nil
}
25 changes: 25 additions & 0 deletions pkg/ebpf/ebpftest/cgo_align.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2024-present Datadog, Inc.

package ebpftest

import (
"bytes"
"encoding/binary"
"testing"
"unsafe"

"github.com/stretchr/testify/require"
)

// TestCgoAlignment checks if the provided type's size and amount of data necessary to read with binary.Read match.
// If they do not, that likely indicates there is a "hole" in the C definition of the type.
func TestCgoAlignment[K any](t *testing.T) {
var x K
rdr := bytes.NewReader(make([]byte, unsafe.Sizeof(x)))
err := binary.Read(rdr, binary.NativeEndian, &x)
require.NoError(t, err)
require.Zero(t, rdr.Len(), "type %%T has holes or size does match between C and Go. Check 'pahole -C <c_type_name> <ebpf_object_file.o>' for layout", x)
}
17 changes: 17 additions & 0 deletions pkg/ebpf/telemetry/types_linux_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions pkg/ebpf/types_linux.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions pkg/ebpf/types_linux_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/gpu/ebpf/c/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ typedef enum {
#define MAX_CONTAINER_ID_LEN 129

typedef struct {
cuda_event_type_t type;
__u64 pid_tgid;
__u64 stream_id;
__u64 ktime_ns;
cuda_event_type_t type;
char cgroup[MAX_CONTAINER_ID_LEN];
} cuda_event_header_t;

Expand Down
14 changes: 7 additions & 7 deletions pkg/gpu/ebpf/kprobe_types_linux.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 367790e

Please sign in to comment.