From ad2177e7ab9a62a105d3adf5b3487bcad30b51e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E5=AE=9C=E8=90=B1?= <85030740+syxl-time@users.noreply.github.com> Date: Fri, 10 May 2024 20:21:35 +0800 Subject: [PATCH] =?UTF-8?q?mem=5Fwatcher:=E6=B7=BB=E5=8A=A0=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E5=86=85=E6=A0=B8=E6=80=81=E5=86=85=E5=AD=98=E6=B3=84?= =?UTF-8?q?=E6=BC=8F=E7=9A=84=E5=8A=9F=E8=83=BD=20(#789)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * mem_watcher:添加检测内核态内存泄漏的功能 * mem_watcher:修改makefile --- .../Memory_Subsystem/mem_watcher/Makefile | 10 +- .../mem_watcher/mem_watcher.c | 66 ++++-- .../mem_watcher/memleak.bpf.c | 195 +++++++++++++++++- 3 files changed, 245 insertions(+), 26 deletions(-) diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile index 5847750c7..60cdd95ae 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile @@ -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/' \ @@ -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) diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c index 8c5eb3dc6..c1301ee2c 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "paf.skel.h" @@ -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; @@ -86,6 +90,7 @@ static struct env { bool procstat; bool sysstat; bool memleak; + bool kernel_trace; bool part2; @@ -98,8 +103,10 @@ 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"; @@ -107,22 +114,27 @@ 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}, }; @@ -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); } @@ -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"); @@ -653,10 +677,15 @@ 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) { @@ -664,10 +693,13 @@ int main(int argc, char **argv) { 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 diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c index d8b5d0d31..4ae642682 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c @@ -19,10 +19,10 @@ #include "vmlinux.h" #include #include +#include #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); @@ -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); @@ -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); } \ No newline at end of file