Skip to content
Merged

Arena #427

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 138 additions & 34 deletions gotests/main.go

Large diffs are not rendered by default.

144 changes: 137 additions & 7 deletions gotests/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,63 @@ func TestKernelSelectionHelpers(t *testing.T) {
})
}

osReleaseCases := []struct {
name string
content string
want int
}{
{
name: "alma 9 without redhat-release",
content: `NAME="AlmaLinux"
VERSION="9.4 (Seafoam Ocelot)"
ID="almalinux"
ID_LIKE="rhel centos fedora"
VERSION_ID="9.4"
PLATFORM_ID="platform:el9"
`,
want: 9*256 + 4,
},
{
name: "rhel 8 via ID_LIKE",
content: `NAME="Red Hat Enterprise Linux"
VERSION="8.9 (Ootpa)"
ID="rhel"
VERSION_ID="8.9"
`,
want: 8*256 + 9,
},
{
name: "rocky linux",
content: `NAME="Rocky Linux"
ID="rocky"
ID_LIKE="rhel centos fedora"
VERSION_ID="9.3"
`,
want: 9*256 + 3,
},
{
name: "debian ignored",
content: `NAME="Debian GNU/Linux"
ID=debian
VERSION_ID="12"
`,
want: -1,
},
{
name: "empty content",
content: ``,
want: -1,
},
}

for _, tc := range osReleaseCases {
t.Run("os-release "+tc.name, func(t *testing.T) {
if got := parseOSRelease(tc.content); got != tc.want {
t.Fatalf("unexpected parsed os-release for %q: got %d want %d", tc.name, got, tc.want)
}
})
}

leadingCases := []struct {
input string
want int
Expand Down Expand Up @@ -162,11 +219,13 @@ func TestCandidateSelectionHelpers(t *testing.T) {
{name: "wrong family", filename: "pnetdata_ebpf_swap.3.10.o", module: "swap", version: "3.10", rhf: 1, wantMatch: false},
{name: "non-rhf exact", filename: "pnetdata_ebpf_swap.5.14.o", module: "swap", version: "5.14", rhf: -1, wantMatch: true},
{name: "non-rhf rejects rhf", filename: "pnetdata_ebpf_swap.5.14.rhf.o", module: "swap", version: "5.14", rhf: -1, wantMatch: false},
{name: "arena suffix exact", filename: "pnetdata_ebpf_swap_arena.6.12.o", module: "swap", version: "6.12", rhf: -1, wantMatch: true},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := candidateMatches(tc.filename, tc.module, tc.isReturn, tc.version, tc.rhf, false); got != tc.wantMatch {
arenaMode := tc.name == "arena suffix exact"
if got := candidateMatches(tc.filename, tc.module, tc.isReturn, tc.version, tc.rhf, false, arenaMode); got != tc.wantMatch {
t.Fatalf("unexpected match result for %q: got %v want %v", tc.filename, got, tc.wantMatch)
}
})
Expand All @@ -188,7 +247,7 @@ func TestCandidateSelectionHelpers(t *testing.T) {
}
}

got := discoverCandidates("swap", false, 1, netdataV310, 0, dir, false)
got := discoverCandidates("swap", false, 1, netdataV310, 0, dir, false, false)
want := []string{
filepath.Join(dir, "pnetdata_ebpf_swap.3.10.rhf.o"),
filepath.Join(dir, "pnetdata_ebpf_swap.3.10.variant.rhf.o"),
Expand Down Expand Up @@ -219,13 +278,34 @@ func TestCandidateSelectionHelpers(t *testing.T) {
}
}

got := discoverCandidates("swap", false, -1, netdataV54|netdataV68|netdataV612, 11, dir, false)
got := discoverCandidates("swap", false, -1, netdataV54|netdataV68|netdataV612, 11, dir, false, false)
want := []string{filepath.Join(dir, "pnetdata_ebpf_swap.6.12.o")}
if len(got) != len(want) || got[0] != want[0] {
t.Fatalf("unexpected candidates: got %v want %v", got, want)
}
})

t.Run("discovers arena candidates when arena mode is enabled", func(t *testing.T) {
dir := t.TempDir()
files := []string{
"pnetdata_ebpf_swap_arena.5.4.o",
"pnetdata_ebpf_swap_buffer.5.4.o",
"pnetdata_ebpf_swap_arena.6.12.o",
"pnetdata_ebpf_swap_buffer.6.12.o",
}
for _, name := range files {
if err := os.WriteFile(filepath.Join(dir, name), []byte("x"), 0o644); err != nil {
t.Fatalf("cannot create %s: %v", name, err)
}
}

got := discoverCandidates("swap", false, -1, netdataV54|netdataV68|netdataV612, 11, dir, false, true)
want := []string{filepath.Join(dir, "pnetdata_ebpf_swap_arena.6.12.o")}
if len(got) != len(want) || got[0] != want[0] {
t.Fatalf("unexpected arena candidates: got %v want %v", got, want)
}
})

t.Run("returns first unsupported map type", func(t *testing.T) {
supported := map[uint32]bool{
bpfMapTypeHash: true,
Expand Down Expand Up @@ -314,7 +394,7 @@ func TestBinaryAndIPHelpers(t *testing.T) {

func TestParseArgumentsUnitTest(t *testing.T) {
var log bytes.Buffer
opts, code := parseArguments([]string{"--unit-test"}, netdataEBPFKernel68, &logState{writer: &log})
opts, code := parseArguments([]string{"--unit-test"}, netdataEBPFKernel68, -1, &logState{writer: &log})
if code != 0 {
t.Fatalf("unexpected parse code: %d", code)
}
Expand All @@ -332,7 +412,7 @@ func TestParseArgumentsUnitTest(t *testing.T) {
func TestParseArgumentsAll(t *testing.T) {
t.Run("--all enables content flag for PID collection", func(t *testing.T) {
var log bytes.Buffer
opts, code := parseArguments([]string{"--all"}, netdataEBPFKernel68, &logState{writer: &log})
opts, code := parseArguments([]string{"--all"}, netdataEBPFKernel68, -1, &logState{writer: &log})
if code != 0 {
t.Fatalf("unexpected parse code: %d", code)
}
Expand All @@ -346,7 +426,7 @@ func TestParseArgumentsAll(t *testing.T) {

t.Run("--pid 3 is accepted", func(t *testing.T) {
var log bytes.Buffer
opts, code := parseArguments([]string{"--all", "--pid", "3"}, netdataEBPFKernel68, &logState{writer: &log})
opts, code := parseArguments([]string{"--all", "--pid", "3"}, netdataEBPFKernel68, -1, &logState{writer: &log})
if code != 0 {
t.Fatalf("unexpected parse code: %d", code)
}
Expand All @@ -362,7 +442,7 @@ func TestParseArgumentsAll(t *testing.T) {
func TestParseArgumentsBuffer(t *testing.T) {
t.Run("--buffer enables content flag for ring buffer data", func(t *testing.T) {
var log bytes.Buffer
opts, code := parseArguments([]string{"--buffer"}, netdataEBPFKernel612, &logState{writer: &log})
opts, code := parseArguments([]string{"--buffer"}, netdataEBPFKernel612, -1, &logState{writer: &log})
if code != 0 {
t.Fatalf("unexpected parse code: %d", code)
}
Expand All @@ -373,6 +453,56 @@ func TestParseArgumentsBuffer(t *testing.T) {
t.Fatal("--buffer must enable flagContent so ring buffer data is collected and ring size is shown")
}
})

t.Run("--arena enables content flag for arena data", func(t *testing.T) {
var log bytes.Buffer
opts, code := parseArguments([]string{"--arena"}, netdataEBPFKernel69, -1, &logState{writer: &log})
if code != 0 {
t.Fatalf("unexpected parse code: %d", code)
}
if !opts.arenaMode {
t.Fatal("--arena must set arenaMode")
}
if opts.flags&flagContent == 0 {
t.Fatal("--arena must enable flagContent so arena data is collected and ring size is shown")
}
})

t.Run("--buffer on RH kernel below 5.8 skips version abort", func(t *testing.T) {
var log bytes.Buffer
// netdataMinimumEBPFKernel (4.11) is below the 5.8 ring-buffer threshold.
// On a non-RH kernel this must be rejected; on RH it must proceed.
_, nonRHCode := parseArguments([]string{"--buffer"}, netdataMinimumEBPFKernel, -1, &logState{writer: &log})
if nonRHCode == 0 {
t.Fatal("non-RH kernel below 5.8 must be rejected for --buffer")
}
log.Reset()
_, rhCode := parseArguments([]string{"--buffer"}, netdataMinimumEBPFKernel, 1, &logState{writer: &log})
if rhCode != 0 {
t.Fatalf("RH kernel must bypass version check for --buffer, got code %d", rhCode)
}
})

t.Run("--arena on RH kernel below 6.9 skips version abort", func(t *testing.T) {
var log bytes.Buffer
_, nonRHCode := parseArguments([]string{"--arena"}, netdataEBPFKernel514, -1, &logState{writer: &log})
if nonRHCode == 0 {
t.Fatal("non-RH kernel below 6.9 must be rejected for --arena")
}
log.Reset()
_, rhCode := parseArguments([]string{"--arena"}, netdataEBPFKernel514, 1, &logState{writer: &log})
if rhCode != 0 {
t.Fatalf("RH kernel must bypass version check for --arena, got code %d", rhCode)
}
})

t.Run("mountName uses arena suffix", func(t *testing.T) {
got := mountName(11, "swap", false, -1, "/tmp", false, true)
want := "/tmp/pnetdata_ebpf_swap_arena.6.12.o"
if got != want {
t.Fatalf("unexpected arena mount name: got %q want %q", got, want)
}
})
}

func TestResolveUnitTestDir(t *testing.T) {
Expand Down
51 changes: 51 additions & 0 deletions includes/netdata_arena_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef _NETDATA_ARENA_COMMON_
#define _NETDATA_ARENA_COMMON_ 1

#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
#define __arena __attribute__((address_space(1)))
#define __arena_global __attribute__((address_space(1)))
#else
#define __arena
#define __arena_global SEC(".addr_space.1")
#endif

#ifndef __arg_arena
#define __arg_arena __attribute__((btf_decl_tag("arg:arena")))
#endif

#if defined(__TARGET_ARCH_arm64)
#define NETDATA_ARENA_MAP_EXTRA (0x1ull << 32)
#else
#define NETDATA_ARENA_MAP_EXTRA (0x1ull << 44)
#endif

#define NETDATA_ARENA_MAP_PAGES 256
#define NETDATA_ARENA_EVENT_SLOTS 1024

#define NETDATA_BPF_ARENA_DEF(NAME, MAX_ENTRIES) \
struct { \
__uint(type, BPF_MAP_TYPE_ARENA); \
__uint(map_flags, BPF_F_MMAPABLE); \
__uint(max_entries, NETDATA_ARENA_MAP_PAGES); \
__ulong(map_extra, NETDATA_ARENA_MAP_EXTRA); \
} NAME SEC(".maps")

#define NETDATA_ARENA_QUEUE_DECL(PREFIX, EVENT_TYPE, SLOT_COUNT) \
struct netdata_##PREFIX##_arena_state_t { \
__u32 head; \
EVENT_TYPE events[SLOT_COUNT]; \
}; \
extern __arena struct netdata_##PREFIX##_arena_state_t PREFIX##_arena_state; \
static __always_inline __arena EVENT_TYPE *netdata_##PREFIX##_arena_reserve(void) { \
/* BPF backend rejects using the XADD return value directly. */ \
__sync_fetch_and_add(&PREFIX##_arena_state.head, 1); \
__u32 idx = PREFIX##_arena_state.head - 1; \
return &PREFIX##_arena_state.events[idx % SLOT_COUNT]; \
} \
static __always_inline void netdata_##PREFIX##_arena_submit(__arena EVENT_TYPE *ev) { \
(void)ev; \
}

#endif /* _NETDATA_ARENA_COMMON_ */
11 changes: 11 additions & 0 deletions includes/netdata_cachestat_arena.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef _NETDATA_CACHESTAT_ARENA_H_
#define _NETDATA_CACHESTAT_ARENA_H_ 1

#include "netdata_cache_buffer.h"
#include "netdata_arena_common.h"

NETDATA_ARENA_QUEUE_DECL(cachestat, struct netdata_cachestat_event_t, NETDATA_ARENA_EVENT_SLOTS);

#endif /* _NETDATA_CACHESTAT_ARENA_H_ */
8 changes: 8 additions & 0 deletions includes/netdata_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,15 @@ static __always_inline __u32 monitor_apps(void *ctrl_tbl)
#define NETDATA_BPF_PERCPU_ARRAY_DEF(NAME, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES) \
NETDATA_BPF_MAP_DEF(NAME, BPF_MAP_TYPE_PERCPU_ARRAY, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES)

#ifndef NETDATA_BPF_RINGBUF_DEF
#define NETDATA_BPF_RINGBUF_DEF(NAME, MAX_ENTRIES) \
NETDATA_BPF_RINGBUF_MAP_DEF(NAME, BPF_MAP_TYPE_RINGBUF, MAX_ENTRIES)
#endif

#ifndef NETDATA_BPF_USER_RINGBUF_DEF
#define NETDATA_BPF_USER_RINGBUF_DEF(NAME, MAX_ENTRIES) \
NETDATA_BPF_RINGBUF_MAP_DEF(NAME, BPF_MAP_TYPE_USER_RINGBUF, MAX_ENTRIES)
#endif
#else
#define NETDATA_BPF_HASH_DEF(NAME, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES) \
NETDATA_BPF_MAP_DEF(NAME, BPF_MAP_TYPE_HASH, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES)
Expand All @@ -270,12 +274,16 @@ static __always_inline __u32 monitor_apps(void *ctrl_tbl)
#define NETDATA_BPF_PERCPU_ARRAY_DEF(NAME, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES) \
NETDATA_BPF_MAP_DEF(NAME, BPF_MAP_TYPE_ARRAY, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES)

#ifndef NETDATA_BPF_RINGBUF_DEF
#define NETDATA_BPF_RINGBUF_DEF(NAME, MAX_ENTRIES) \
NETDATA_BPF_RINGBUF_MAP_DEF(NAME, BPF_MAP_TYPE_RINGBUF, MAX_ENTRIES)
#endif

#ifndef NETDATA_BPF_USER_RINGBUF_DEF
#define NETDATA_BPF_USER_RINGBUF_DEF(NAME, MAX_ENTRIES) \
NETDATA_BPF_RINGBUF_MAP_DEF(NAME, BPF_MAP_TYPE_USER_RINGBUF, MAX_ENTRIES)
#endif
#endif

#else
#define NETDATA_BPF_MAP_DEF(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES) \
Expand Down
11 changes: 11 additions & 0 deletions includes/netdata_dc_arena.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef _NETDATA_DC_ARENA_H_
#define _NETDATA_DC_ARENA_H_ 1

#include "netdata_dc_buffer.h"
#include "netdata_arena_common.h"

NETDATA_ARENA_QUEUE_DECL(dc, struct netdata_dc_event_t, NETDATA_ARENA_EVENT_SLOTS);

#endif /* _NETDATA_DC_ARENA_H_ */
11 changes: 11 additions & 0 deletions includes/netdata_dns_arena.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef _NETDATA_DNS_ARENA_H_
#define _NETDATA_DNS_ARENA_H_ 1

#include "netdata_dns_buffer.h"
#include "netdata_arena_common.h"

NETDATA_ARENA_QUEUE_DECL(dns, struct netdata_dns_event_t, NETDATA_ARENA_EVENT_SLOTS);

#endif /* _NETDATA_DNS_ARENA_H_ */
11 changes: 11 additions & 0 deletions includes/netdata_fd_arena.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef _NETDATA_FD_ARENA_H_
#define _NETDATA_FD_ARENA_H_ 1

#include "netdata_fd_buffer.h"
#include "netdata_arena_common.h"

NETDATA_ARENA_QUEUE_DECL(fd, struct netdata_fd_event_t, NETDATA_ARENA_EVENT_SLOTS);

#endif /* _NETDATA_FD_ARENA_H_ */
11 changes: 11 additions & 0 deletions includes/netdata_oomkill_arena.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef _NETDATA_OOMKILL_ARENA_H_
#define _NETDATA_OOMKILL_ARENA_H_ 1

#include "netdata_oomkill_buffer.h"
#include "netdata_arena_common.h"

NETDATA_ARENA_QUEUE_DECL(oomkill, struct netdata_oomkill_event_t, NETDATA_ARENA_EVENT_SLOTS);

#endif /* _NETDATA_OOMKILL_ARENA_H_ */
11 changes: 11 additions & 0 deletions includes/netdata_process_arena.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef _NETDATA_PROCESS_ARENA_H_
#define _NETDATA_PROCESS_ARENA_H_ 1

#include "netdata_process_buffer.h"
#include "netdata_arena_common.h"

NETDATA_ARENA_QUEUE_DECL(process, struct netdata_process_event_t, NETDATA_ARENA_EVENT_SLOTS);

#endif /* _NETDATA_PROCESS_ARENA_H_ */
11 changes: 11 additions & 0 deletions includes/netdata_shm_arena.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef _NETDATA_SHM_ARENA_H_
#define _NETDATA_SHM_ARENA_H_ 1

#include "netdata_shm_buffer.h"
#include "netdata_arena_common.h"

NETDATA_ARENA_QUEUE_DECL(shm, struct netdata_shm_event_t, NETDATA_ARENA_EVENT_SLOTS);

#endif /* _NETDATA_SHM_ARENA_H_ */
11 changes: 11 additions & 0 deletions includes/netdata_swap_arena.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef _NETDATA_SWAP_ARENA_H_
#define _NETDATA_SWAP_ARENA_H_ 1

#include "netdata_swap_buffer.h"
#include "netdata_arena_common.h"

NETDATA_ARENA_QUEUE_DECL(swap, struct netdata_swap_event_t, NETDATA_ARENA_EVENT_SLOTS);

#endif /* _NETDATA_SWAP_ARENA_H_ */
Loading