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
memleak: the symbol of the function that calls new
disappeared
#4958
Comments
new
disappearednew
disappeared
new
disappearednew
disappeared
The current memleak implementation only supports C functions like malloc/calloc/mmap/memalign/free/munmap etc. If you are interested, you could contribute to add C++ support. |
The cause of this issue is that Linux kernel cannot unwind the stack frame of Kernel uses 2858 void
2859 perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs)
2860 {
2861 struct stack_frame frame;
2862 const struct stack_frame __user *fp;
2863
2864 if (perf_guest_state()) {
2865 /* TODO: We don't support guest os callchain now */
2866 return;
2867 }
2868
2869 /*
2870 * We don't know what to do with VM86 stacks.. ignore them for now.
2871 */
2872 if (regs->flags & (X86_VM_MASK | PERF_EFLAGS_VM))
2873 return;
2874
2875 fp = (void __user *)regs->bp;
2876
2877 perf_callchain_store(entry, regs->ip);
2878 But
The first backrace is acquired by For example, imagine the call chain looks like this: I found that following feature could resolve this issue. I patched diff --git a/libbpf-tools/memleak.bpf.c b/libbpf-tools/memleak.bpf.c
index cb13fdd8..9d7d7da3 100644
--- a/libbpf-tools/memleak.bpf.c
+++ b/libbpf-tools/memleak.bpf.c
@@ -7,6 +7,7 @@
#include "maps.bpf.h"
#include "memleak.h"
#include "core_fixes.bpf.h"
+#include "unwind.bpf.h"
const volatile size_t min_size = 0;
const volatile size_t max_size = -1;
@@ -122,7 +123,8 @@ static int gen_alloc_exit2(void *ctx, u64 address)
if (address != 0) {
info.timestamp_ns = bpf_ktime_get_ns();
- info.stack_id = bpf_get_stackid(ctx, &stack_traces, stack_flags);
+ //info.stack_id = bpf_get_stackid(ctx, &stack_traces, stack_flags);
+ info.stack_id = uw_get_stackid();
bpf_map_update_elem(&allocs, &address, &info, BPF_ANY);
diff --git a/libbpf-tools/memleak.c b/libbpf-tools/memleak.c
index a2c7d1cd..bab2b9e0 100644
--- a/libbpf-tools/memleak.c
+++ b/libbpf-tools/memleak.c
@@ -23,6 +23,7 @@
#include "memleak.h"
#include "memleak.skel.h"
#include "trace_helpers.h"
+#include "unwind_helpers.h"
#ifdef USE_BLAZESYM
#include "blazesym.h"
@@ -86,38 +87,38 @@ struct allocation {
struct allocation_node* allocations;
};
-#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \
+#define __ATTACH_UPROBE(obj, sym_name, prog_name, is_retprobe) \
do { \
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, \
.func_name = #sym_name, \
.retprobe = is_retprobe); \
- skel->links.prog_name = bpf_program__attach_uprobe_opts( \
- skel->progs.prog_name, \
+ obj->links.prog_name = bpf_program__attach_uprobe_opts( \
+ obj->progs.prog_name, \
env.pid, \
env.object, \
0, \
&uprobe_opts); \
} while (false)
-#define __CHECK_PROGRAM(skel, prog_name) \
+#define __CHECK_PROGRAM(obj, prog_name) \
do { \
- if (!skel->links.prog_name) { \
+ if (!obj->links.prog_name) { \
perror("no program attached for " #prog_name); \
return -errno; \
} \
} while (false)
-#define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \
+#define __ATTACH_UPROBE_CHECKED(obj, sym_name, prog_name, is_retprobe) \
do { \
- __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \
- __CHECK_PROGRAM(skel, prog_name); \
+ __ATTACH_UPROBE(obj, sym_name, prog_name, is_retprobe); \
+ __CHECK_PROGRAM(obj, prog_name); \
} while (false)
-#define ATTACH_UPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, false)
-#define ATTACH_URETPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, true)
+#define ATTACH_UPROBE(obj, sym_name, prog_name) __ATTACH_UPROBE(obj, sym_name, prog_name, false)
+#define ATTACH_URETPROBE(obj, sym_name, prog_name) __ATTACH_UPROBE(obj, sym_name, prog_name, true)
-#define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false)
-#define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true)
+#define ATTACH_UPROBE_CHECKED(obj, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(obj, sym_name, prog_name, false)
+#define ATTACH_URETPROBE_CHECKED(obj, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(obj, sym_name, prog_name, true)
static void sig_handler(int signo);
@@ -147,11 +148,11 @@ static int print_outstanding_allocs(int allocs_fd, int stack_traces_fd);
static int print_outstanding_combined_allocs(int combined_allocs_fd, int stack_traces_fd);
static bool has_kernel_node_tracepoints();
-static void disable_kernel_node_tracepoints(struct memleak_bpf *skel);
-static void disable_kernel_percpu_tracepoints(struct memleak_bpf *skel);
-static void disable_kernel_tracepoints(struct memleak_bpf *skel);
+static void disable_kernel_node_tracepoints(struct memleak_bpf *obj);
+static void disable_kernel_percpu_tracepoints(struct memleak_bpf *obj);
+static void disable_kernel_tracepoints(struct memleak_bpf *obj);
-static int attach_uprobes(struct memleak_bpf *skel);
+static int attach_uprobes(struct memleak_bpf *obj);
const char *argp_program_version = "memleak 0.1";
const char *argp_program_bug_address =
@@ -229,7 +230,7 @@ static const char default_object[] = "libc.so.6";
int main(int argc, char *argv[])
{
int ret = 0;
- struct memleak_bpf *skel = NULL;
+ struct memleak_bpf *obj = NULL;
static const struct argp argp = {
.options = argp_options,
@@ -331,51 +332,52 @@ int main(int argc, char *argv[])
libbpf_set_print(libbpf_print_fn);
- skel = memleak_bpf__open();
- if (!skel) {
+ obj = memleak_bpf__open();
+ if (!obj) {
fprintf(stderr, "failed to open bpf object\n");
ret = 1;
goto cleanup;
}
- skel->rodata->min_size = env.min_size;
- skel->rodata->max_size = env.max_size;
- skel->rodata->page_size = env.page_size;
- skel->rodata->sample_rate = env.sample_rate;
- skel->rodata->trace_all = env.trace_all;
- skel->rodata->stack_flags = env.kernel_trace ? 0 : BPF_F_USER_STACK;
- skel->rodata->wa_missing_free = env.wa_missing_free;
+ obj->rodata->min_size = env.min_size;
+ obj->rodata->max_size = env.max_size;
+ obj->rodata->page_size = env.page_size;
+ obj->rodata->sample_rate = env.sample_rate;
+ obj->rodata->trace_all = env.trace_all;
+ obj->rodata->stack_flags = env.kernel_trace ? 0 : BPF_F_USER_STACK;
+ obj->rodata->wa_missing_free = env.wa_missing_free;
- bpf_map__set_value_size(skel->maps.stack_traces,
+ bpf_map__set_value_size(obj->maps.stack_traces,
env.perf_max_stack_depth * sizeof(unsigned long));
- bpf_map__set_max_entries(skel->maps.stack_traces, env.stack_map_max_entries);
+ bpf_map__set_max_entries(obj->maps.stack_traces, env.stack_map_max_entries);
+ UW_INIT(obj, 128, 10240);
// disable kernel tracepoints based on settings or availability
if (env.kernel_trace) {
if (!has_kernel_node_tracepoints())
- disable_kernel_node_tracepoints(skel);
+ disable_kernel_node_tracepoints(obj);
if (!env.percpu)
- disable_kernel_percpu_tracepoints(skel);
+ disable_kernel_percpu_tracepoints(obj);
} else {
- disable_kernel_tracepoints(skel);
+ disable_kernel_tracepoints(obj);
}
- ret = memleak_bpf__load(skel);
+ ret = memleak_bpf__load(obj);
if (ret) {
fprintf(stderr, "failed to load bpf object\n");
goto cleanup;
}
- const int allocs_fd = bpf_map__fd(skel->maps.allocs);
- const int combined_allocs_fd = bpf_map__fd(skel->maps.combined_allocs);
- const int stack_traces_fd = bpf_map__fd(skel->maps.stack_traces);
+ const int allocs_fd = bpf_map__fd(obj->maps.allocs);
+ const int combined_allocs_fd = bpf_map__fd(obj->maps.combined_allocs);
+ const int stack_traces_fd = bpf_map__fd(obj->maps.stack_traces);
// if userspace oriented, attach upbrobes
if (!env.kernel_trace) {
- ret = attach_uprobes(skel);
+ ret = attach_uprobes(obj);
if (ret) {
fprintf(stderr, "failed to attach uprobes\n");
@@ -383,7 +385,7 @@ int main(int argc, char *argv[])
}
}
- ret = memleak_bpf__attach(skel);
+ ret = memleak_bpf__attach(obj);
if (ret) {
fprintf(stderr, "failed to attach bpf program(s)\n");
@@ -476,7 +478,7 @@ cleanup:
if (ksyms)
ksyms__free(ksyms);
#endif
- memleak_bpf__destroy(skel);
+ memleak_bpf__destroy(obj);
free(allocs);
free(stack);
@@ -786,14 +788,7 @@ int print_stack_frames(struct allocation *allocs, size_t nr_allocs, int stack_tr
}
}
- if (bpf_map_lookup_elem(stack_traces_fd, &alloc->stack_id, stack)) {
- if (errno == ENOENT)
- continue;
-
- perror("failed to lookup stack trace");
-
- return -errno;
- }
+ uw_map_lookup_elem(&alloc->stack_id, env.pid, stack, 32);
(*print_stack_frames_func)();
}
@@ -1004,68 +999,68 @@ bool has_kernel_node_tracepoints()
tracepoint_exists("kmem", "kmem_cache_alloc_node");
}
-void disable_kernel_node_tracepoints(struct memleak_bpf *skel)
+void disable_kernel_node_tracepoints(struct memleak_bpf *obj)
{
- bpf_program__set_autoload(skel->progs.memleak__kmalloc_node, false);
- bpf_program__set_autoload(skel->progs.memleak__kmem_cache_alloc_node, false);
+ bpf_program__set_autoload(obj->progs.memleak__kmalloc_node, false);
+ bpf_program__set_autoload(obj->progs.memleak__kmem_cache_alloc_node, false);
}
-void disable_kernel_percpu_tracepoints(struct memleak_bpf *skel)
+void disable_kernel_percpu_tracepoints(struct memleak_bpf *obj)
{
- bpf_program__set_autoload(skel->progs.memleak__percpu_alloc_percpu, false);
- bpf_program__set_autoload(skel->progs.memleak__percpu_free_percpu, false);
+ bpf_program__set_autoload(obj->progs.memleak__percpu_alloc_percpu, false);
+ bpf_program__set_autoload(obj->progs.memleak__percpu_free_percpu, false);
}
-void disable_kernel_tracepoints(struct memleak_bpf *skel)
+void disable_kernel_tracepoints(struct memleak_bpf *obj)
{
- 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);
- bpf_program__set_autoload(skel->progs.memleak__percpu_alloc_percpu, false);
- bpf_program__set_autoload(skel->progs.memleak__percpu_free_percpu, false);
+ bpf_program__set_autoload(obj->progs.memleak__kmalloc, false);
+ bpf_program__set_autoload(obj->progs.memleak__kmalloc_node, false);
+ bpf_program__set_autoload(obj->progs.memleak__kfree, false);
+ bpf_program__set_autoload(obj->progs.memleak__kmem_cache_alloc, false);
+ bpf_program__set_autoload(obj->progs.memleak__kmem_cache_alloc_node, false);
+ bpf_program__set_autoload(obj->progs.memleak__kmem_cache_free, false);
+ bpf_program__set_autoload(obj->progs.memleak__mm_page_alloc, false);
+ bpf_program__set_autoload(obj->progs.memleak__mm_page_free, false);
+ bpf_program__set_autoload(obj->progs.memleak__percpu_alloc_percpu, false);
+ bpf_program__set_autoload(obj->progs.memleak__percpu_free_percpu, false);
}
-int attach_uprobes(struct memleak_bpf *skel)
+int attach_uprobes(struct memleak_bpf *obj)
{
- ATTACH_UPROBE_CHECKED(skel, malloc, malloc_enter);
- ATTACH_URETPROBE_CHECKED(skel, malloc, malloc_exit);
+ ATTACH_UPROBE_CHECKED(obj, malloc, malloc_enter);
+ ATTACH_URETPROBE_CHECKED(obj, malloc, malloc_exit);
- ATTACH_UPROBE_CHECKED(skel, calloc, calloc_enter);
- ATTACH_URETPROBE_CHECKED(skel, calloc, calloc_exit);
+ ATTACH_UPROBE_CHECKED(obj, calloc, calloc_enter);
+ ATTACH_URETPROBE_CHECKED(obj, calloc, calloc_exit);
- ATTACH_UPROBE_CHECKED(skel, realloc, realloc_enter);
- ATTACH_URETPROBE_CHECKED(skel, realloc, realloc_exit);
+ ATTACH_UPROBE_CHECKED(obj, realloc, realloc_enter);
+ ATTACH_URETPROBE_CHECKED(obj, realloc, realloc_exit);
- ATTACH_UPROBE_CHECKED(skel, mmap, mmap_enter);
- ATTACH_URETPROBE_CHECKED(skel, mmap, mmap_exit);
+ ATTACH_UPROBE_CHECKED(obj, mmap, mmap_enter);
+ ATTACH_URETPROBE_CHECKED(obj, mmap, mmap_exit);
- ATTACH_UPROBE_CHECKED(skel, posix_memalign, posix_memalign_enter);
- ATTACH_URETPROBE_CHECKED(skel, posix_memalign, posix_memalign_exit);
+ ATTACH_UPROBE_CHECKED(obj, posix_memalign, posix_memalign_enter);
+ ATTACH_URETPROBE_CHECKED(obj, posix_memalign, posix_memalign_exit);
- ATTACH_UPROBE_CHECKED(skel, memalign, memalign_enter);
- ATTACH_URETPROBE_CHECKED(skel, memalign, memalign_exit);
+ ATTACH_UPROBE_CHECKED(obj, memalign, memalign_enter);
+ ATTACH_URETPROBE_CHECKED(obj, memalign, memalign_exit);
- ATTACH_UPROBE_CHECKED(skel, free, free_enter);
- ATTACH_UPROBE_CHECKED(skel, munmap, munmap_enter);
+ ATTACH_UPROBE_CHECKED(obj, free, free_enter);
+ ATTACH_UPROBE_CHECKED(obj, munmap, munmap_enter);
// the following probes are intentinally allowed to fail attachment
// deprecated in libc.so bionic
- ATTACH_UPROBE(skel, valloc, valloc_enter);
- ATTACH_URETPROBE(skel, valloc, valloc_exit);
+ ATTACH_UPROBE(obj, valloc, valloc_enter);
+ ATTACH_URETPROBE(obj, valloc, valloc_exit);
// deprecated in libc.so bionic
- ATTACH_UPROBE(skel, pvalloc, pvalloc_enter);
- ATTACH_URETPROBE(skel, pvalloc, pvalloc_exit);
+ ATTACH_UPROBE(obj, pvalloc, pvalloc_enter);
+ ATTACH_URETPROBE(obj, pvalloc, pvalloc_exit);
// added in C11
- ATTACH_UPROBE(skel, aligned_alloc, aligned_alloc_enter);
- ATTACH_URETPROBE(skel, aligned_alloc, aligned_alloc_exit);
+ ATTACH_UPROBE(obj, aligned_alloc, aligned_alloc_enter);
+ ATTACH_URETPROBE(obj, aligned_alloc, aligned_alloc_exit);
return 0; Now
In this test program #include <chrono>
#include <thread>
int* foo() { return new int; }
int main(int argc, char* argv[]) {
while (true) {
auto p = foo();
std::this_thread::sleep_for(std::chrono::seconds(5));
}
return 0;
} |
The symbol of the function that calls
new
disappeared onmemleak
tool.Test file
leak_loop_new.cpp
Compile and run the file
leak_loop_new.cpp
and runmemleak
It is obvious that
new
is called inmain
function but it is just disappeared._Znwm
is a mangled name ofoperator new
by the way.The result of python version
memleak
is same, except demangle.You can get normal symbol(backtrace) if you use
malloc
instead ofnew
.See following test.
Test file
leak_loop_malloc.cpp
Compile and run the file
leak_loop_malloc.cpp
and runmemleak
Some may think that it can happened only on special
main
function.So I added
foo
function and you can check thatfoo
is disappeared in this time.Test file
leak_loop_foo.cpp
Compile and run the file
leak_loop_foo.cpp
and runmemleak
You can find
main
but cannot findfoo
.Some may think that maybe compiler optimize out the
foo
function.But no. It is not optimized out. It exists inside the binary.
And it is exactly pointing out the return address of
foo
function(0x11e4
).main
function start address(0x11bd
) + offset(0x27
) =0x11e4
Environment
The text was updated successfully, but these errors were encountered: