Skip to content

Commit

Permalink
mem_watcher:添加检测内核态内存泄漏的功能 (#789)
Browse files Browse the repository at this point in the history
* mem_watcher:添加检测内核态内存泄漏的功能

* mem_watcher:修改makefile
  • Loading branch information
syxl-time authored May 10, 2024
1 parent 2b0c89b commit ad2177e
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 26 deletions.
10 changes: 5 additions & 5 deletions eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
OUTPUT := .output
CLANG ?= clang
LIBBPF_SRC := $(abspath ../../libbpf/src)
BPFTOOL_SRC := $(abspath ../../bpftool/src)
LIBBPF_SRC := $(abspath ../libbpf/src)
BPFTOOL_SRC := $(abspath ../bpftool/src)
LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool)
BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool
LIBBLAZESYM_SRC := $(abspath ../../blazesym/)
LIBBLAZESYM_SRC := $(abspath ../blazesym/)
LIBBLAZESYM_INC := $(abspath $(LIBBLAZESYM_SRC)/capi/include)
LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym_c.a)
ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \
Expand All @@ -16,11 +16,11 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \
| sed 's/mips.*/mips/' \
| sed 's/riscv64/riscv/' \
| sed 's/loongarch64/loongarch/')
VMLINUX := ../../vmlinux/$(ARCH)/vmlinux.h
VMLINUX := ../vmlinux/$(ARCH)/vmlinux.h
# Use our own libbpf API headers and Linux UAPI headers distributed with
# libbpf to avoid dependency on system-wide headers, which could be missing or
# outdated
INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC)
INCLUDES := -I$(OUTPUT) -I../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC)
CFLAGS := -g -Wall
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)

Expand Down
66 changes: 49 additions & 17 deletions eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <time.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include <sys/select.h>
#include <unistd.h>
#include "paf.skel.h"
Expand All @@ -40,6 +41,9 @@ static const int stack_map_max_entries = 10240; //最大允许存储多少个sta
static __u64 *g_stacks = NULL;
static size_t g_stacks_size = 0;

#define KERN_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP)
#define USER_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK)

static struct blaze_symbolizer *symbolizer;

static int attach_pid;
Expand Down Expand Up @@ -86,6 +90,7 @@ static struct env {
bool procstat;
bool sysstat;
bool memleak;
bool kernel_trace;

bool part2;

Expand All @@ -98,31 +103,38 @@ static struct env {
.procstat = false,
.sysstat = false,
.memleak = false,
.kernel_trace = true,
.rss = false,
.part2 = false,
.choose_pid = 0,
};

const char argp_program_doc[] = "mem_watcher is in use ....\n";

static const struct argp_option opts[] = {
{0, 0, 0, 0, "select function:", 1},

{"paf", 'a', 0, 0, "print paf (内存页面状态报告)", 2},
{0, 0, 0, 0, "par:", 2},
{"paf", 'a', 0, 0, "print paf (内存页面状态报告)"},

{"pr", 'p', 0, 0, "print pr (页面回收状态报告)", 3},
{0, 0, 0, 0, "pr:", 3},
{"pr", 'p', 0, 0, "print pr (页面回收状态报告)"},

{"procstat", 'r', 0, 0, "print procstat (进程内存状态报告)", 4},
{0, 0, 0, 0, "procstat additional function:"},
{0, 0, 0, 0, "procstat:", 4},
{"procstat", 'r', 0, 0, "print procstat (进程内存状态报告)"},
{"choose_pid", 'P', "PID", 0, "选择进程号打印"},
{"Rss", 'R', NULL, 0, "打印进程页面", 5},

{"sysstat", 's', 0, 0, "print sysstat (系统内存状态报告)", 6},
{0, 0, 0, 0, "sysstat additional function:"},
{0, 0, 0, 0, "sysstat:", 6},
{"sysstat", 's', 0, 0, "print sysstat (系统内存状态报告)"},

{"part2", 'n', NULL, 0, "系统内存状态报告2", 7},

{"memleak", 'l', "PID", 0, "print memleak (内存泄漏检测)", 8},
{0, 0, 0, 0, "memleak:", 8},
{"memleak", 'l', 0, 0, "print memleak (内核态内存泄漏检测)", 8},
{"choose_pid", 'P', "PID", 0, "选择进程号打印, print memleak (用户态内存泄漏检测)", 9},

{"time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)", 9},
{"time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)", 10},
{NULL, 'h', NULL, OPTION_HIDDEN, "show the full help"},
{0},
};
Expand Down Expand Up @@ -296,6 +308,17 @@ int print_outstanding_combined_allocs(struct memleak_bpf *skel, pid_t pid) {
return 0;
}

void disable_kernel_tracepoints(struct memleak_bpf *skel) {
bpf_program__set_autoload(skel->progs.memleak__kmalloc, false);
bpf_program__set_autoload(skel->progs.memleak__kmalloc_node, false);
bpf_program__set_autoload(skel->progs.memleak__kfree, false);
bpf_program__set_autoload(skel->progs.memleak__kmem_cache_alloc, false);
bpf_program__set_autoload(skel->progs.memleak__kmem_cache_alloc_node, false);
bpf_program__set_autoload(skel->progs.memleak__kmem_cache_free, false);
bpf_program__set_autoload(skel->progs.memleak__mm_page_alloc, false);
bpf_program__set_autoload(skel->progs.memleak__mm_page_free, false);
}

static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) {
return vfprintf(stderr, format, args);
}
Expand Down Expand Up @@ -635,12 +658,13 @@ int main(int argc, char **argv) {
}

else if (env.memleak) {
if (argc != 3) {
printf("usage:%s attach_pid\n", argv[0]);
return -1;
if (env.choose_pid != 0) {
printf("用户态内存泄漏\n");
env.kernel_trace = false;
attach_pid = env.choose_pid;
}

attach_pid = atoi(argv[2]);
else
attach_pid = 0;

strcpy(binary_path, "/lib/x86_64-linux-gnu/libc.so.6");

Expand All @@ -653,21 +677,29 @@ int main(int argc, char **argv) {
fprintf(stderr, "Failed to open BPF skeleton\n");
return 1;
}
//skel->rodata->stack_flags = KERN_STACKID_FLAGS;
skel->rodata->stack_flags = env.kernel_trace ? KERN_STACKID_FLAGS : USER_STACKID_FLAGS;

bpf_map__set_value_size(skel->maps.stack_traces, perf_max_stack_depth * sizeof(__u64));
bpf_map__set_max_entries(skel->maps.stack_traces, stack_map_max_entries);

if (!env.kernel_trace)
disable_kernel_tracepoints(skel);

/* Load & verify BPF programs */
err = memleak_bpf__load(skel);
if (err) {
fprintf(stderr, "Failed to load BPF skeleton\n");
goto memleak_cleanup;
}

err = attach_uprobes(skel);
if (err) {
fprintf(stderr, "failed to attach uprobes\n");
goto memleak_cleanup;
if (!env.kernel_trace) {
err = attach_uprobes(skel);
if (err) {
fprintf(stderr, "failed to attach uprobes\n");

goto memleak_cleanup;
}
}

/* Let libbpf perform auto-attach for uprobe_sub/uretprobe_sub
Expand Down
195 changes: 191 additions & 4 deletions eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include "mem_watcher.h"

#define KERN_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP)
#define USER_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK)

const volatile __u64 stack_flags = 0;

struct {
__uint(type, BPF_MAP_TYPE_HASH);
Expand Down Expand Up @@ -88,7 +88,7 @@ static int gen_alloc_exit2(void *ctx, u64 address) {
bpf_map_delete_elem(&sizes, &pid);

if (0 != address) {
info.stack_id = bpf_get_stackid(ctx, &stack_traces, USER_STACKID_FLAGS);
info.stack_id = bpf_get_stackid(ctx, &stack_traces, stack_flags);

bpf_map_update_elem(&allocs, &addr, &info, BPF_ANY);

Expand Down Expand Up @@ -258,4 +258,191 @@ int BPF_KPROBE(pvalloc_enter, size_t size) {
SEC("uretprobe")
int BPF_KRETPROBE(pvalloc_exit) {
return gen_alloc_exit(ctx);
}

struct trace_event_raw_kmem_alloc_node___x {
const void *ptr;
size_t bytes_alloc;
} __attribute__((preserve_access_index));

static __always_inline bool has_kmem_alloc_node(void) {
if (bpf_core_type_exists(struct trace_event_raw_kmem_alloc_node___x))
return true;
return false;
}

struct trace_event_raw_kmem_alloc___x {
const void *ptr;
size_t bytes_alloc;
} __attribute__((preserve_access_index));

struct trace_event_raw_kmalloc___x {
const void *ptr;
size_t bytes_alloc;
} __attribute__((preserve_access_index));

struct trace_event_raw_kmem_cache_alloc___x {
const void *ptr;
size_t bytes_alloc;
} __attribute__((preserve_access_index));

static __always_inline bool has_kmem_alloc(void)
{
if (bpf_core_type_exists(struct trace_event_raw_kmem_alloc___x))
return true;
return false;
}

SEC("tracepoint/kmem/kmalloc")
int memleak__kmalloc(void *ctx)
{
const void *ptr;
size_t bytes_alloc;

if (has_kmem_alloc()) {
struct trace_event_raw_kmem_alloc___x *args = ctx;
ptr = BPF_CORE_READ(args, ptr);
bytes_alloc = BPF_CORE_READ(args, bytes_alloc);
} else {
struct trace_event_raw_kmalloc___x *args = ctx;
ptr = BPF_CORE_READ(args, ptr);
bytes_alloc = BPF_CORE_READ(args, bytes_alloc);
}

gen_alloc_enter(bytes_alloc);

return gen_alloc_exit2(ctx, (u64)ptr);
}

SEC("tracepoint/kmem/kmalloc_node")
int memleak__kmalloc_node(void *ctx)
{
const void *ptr;
size_t bytes_alloc;

if (has_kmem_alloc_node()) {
struct trace_event_raw_kmem_alloc_node___x *args = ctx;
ptr = BPF_CORE_READ(args, ptr);
bytes_alloc = BPF_CORE_READ(args, bytes_alloc);

gen_alloc_enter( bytes_alloc);

return gen_alloc_exit2(ctx, (u64)ptr);
} else {
/* tracepoint is disabled if not exist, avoid compile warning */
return 0;
}
}

struct trace_event_raw_kmem_free___x {
const void *ptr;
} __attribute__((preserve_access_index));

struct trace_event_raw_kfree___x {
const void *ptr;
} __attribute__((preserve_access_index));

struct trace_event_raw_kmem_cache_free___x {
const void *ptr;
} __attribute__((preserve_access_index));

static __always_inline bool has_kfree()
{
if (bpf_core_type_exists(struct trace_event_raw_kfree___x))
return true;
return false;
}

static __always_inline bool has_kmem_cache_free()
{
if (bpf_core_type_exists(struct trace_event_raw_kmem_cache_free___x))
return true;
return false;
}

SEC("tracepoint/kmem/kfree")
int memleak__kfree(void *ctx)
{
const void *ptr;

if (has_kfree()) {
struct trace_event_raw_kfree___x *args = ctx;
ptr = BPF_CORE_READ(args, ptr);
} else {
struct trace_event_raw_kmem_free___x *args = ctx;
ptr = BPF_CORE_READ(args, ptr);
}

return gen_free_enter(ptr);
}

SEC("tracepoint/kmem/kmem_cache_alloc")
int memleak__kmem_cache_alloc(void *ctx)
{
const void *ptr;
size_t bytes_alloc;

if (has_kmem_alloc()) {
struct trace_event_raw_kmem_alloc___x *args = ctx;
ptr = BPF_CORE_READ(args, ptr);
bytes_alloc = BPF_CORE_READ(args, bytes_alloc);
} else {
struct trace_event_raw_kmem_cache_alloc___x *args = ctx;
ptr = BPF_CORE_READ(args, ptr);
bytes_alloc = BPF_CORE_READ(args, bytes_alloc);
}

gen_alloc_enter(bytes_alloc);

return gen_alloc_exit2(ctx, (u64)ptr);
}

SEC("tracepoint/kmem/kmem_cache_alloc_node")
int memleak__kmem_cache_alloc_node(void *ctx)
{
const void *ptr;
size_t bytes_alloc;

if (has_kmem_alloc_node()) {
struct trace_event_raw_kmem_alloc_node___x *args = ctx;
ptr = BPF_CORE_READ(args, ptr);
bytes_alloc = BPF_CORE_READ(args, bytes_alloc);

gen_alloc_enter(bytes_alloc);

return gen_alloc_exit2(ctx, (u64)ptr);
} else {
/* tracepoint is disabled if not exist, avoid compile warning */
return 0;
}
}

SEC("tracepoint/kmem/kmem_cache_free")
int memleak__kmem_cache_free(void *ctx)
{
const void *ptr;

if (has_kmem_cache_free()) {
struct trace_event_raw_kmem_cache_free___x *args = ctx;
ptr = BPF_CORE_READ(args, ptr);
} else {
struct trace_event_raw_kmem_free___x *args = ctx;
ptr = BPF_CORE_READ(args, ptr);
}

return gen_free_enter(ptr);
}

SEC("tracepoint/kmem/mm_page_alloc")
int memleak__mm_page_alloc(struct trace_event_raw_mm_page_alloc *ctx)
{
gen_alloc_enter(4096 << ctx->order);

return gen_alloc_exit2(ctx, ctx->pfn);
}

SEC("tracepoint/kmem/mm_page_free")
int memleak__mm_page_free(struct trace_event_raw_mm_page_free *ctx)
{
return gen_free_enter((void *)ctx->pfn);
}

0 comments on commit ad2177e

Please sign in to comment.