memleak: the symbol of the function that calls `new` disappeared
Bojun-Seo opened this issue · comments
The symbol of the function that calls new
disappeared on memleak
tool.
Test file leak_loop_new.cpp
#include <chrono>
#include <thread>
int main(int argc, char* argv[]) {
while (true) {
auto p = new int;
std::this_thread::sleep_for(std::chrono::seconds(5));
}
return 0;
}
Compile and run the file leak_loop_new.cpp
and run memleak
$ g++ leak_loop_new.cpp
$ ./a.out &
[1] 322617
$ sudo ./memleak -p 322617
using default object: libc.so.6
using page size: 4096
tracing kernel: false
Tracing outstanding memory allocs... Hit Ctrl-C to end
[9:52:1] Top 1 stacks with outstanding allocations:
4 bytes in 1 allocations from stack
0 [<00007f0bb90ae98c>] _Znwm+0x1c
1 [<00007f0bb8c29d90>] __libc_init_first+0x90
[9:52:6] Top 1 stacks with outstanding allocations:
8 bytes in 2 allocations from stack
0 [<00007f0bb90ae98c>] _Znwm+0x1c
1 [<00007f0bb8c29d90>] __libc_init_first+0x90
^C[9:52:6] Top 1 stacks with outstanding allocations:
8 bytes in 2 allocations from stack
0 [<00007f0bb90ae98c>] _Znwm+0x1c
1 [<00007f0bb8c29d90>] __libc_init_first+0x90
done
It is obvious that new
is called in main
function but it is just disappeared.
_Znwm
is a mangled name of operator new
by the way.
The result of python version memleak
is same, except demangle.
$ sudo python3 memleak.py -p 322617
Attaching to pid 322617, Ctrl+C to quit.
[09:55:28] Top 10 stacks with outstanding allocations:
4 bytes in 1 allocations from stack
0x00007f0bb90ae98c operator new(unsigned long)+0x1c [libstdc++.so.6.0.30]
0x00007f0bb8c29d90 __libc_start_call_main+0x80 [libc.so.6]
[09:55:33] Top 10 stacks with outstanding allocations:
8 bytes in 2 allocations from stack
0x00007f0bb90ae98c operator new(unsigned long)+0x1c [libstdc++.so.6.0.30]
0x00007f0bb8c29d90 __libc_start_call_main+0x80 [libc.so.6]
You can get normal symbol(backtrace) if you use malloc
instead of new
.
See following test.
Test file leak_loop_malloc.cpp
$ cat leak_loop_malloc.cpp
#include <chrono>
#include <thread>
#include <cstdlib>
int main(int argc, char* argv[]) {
while (true) {
auto p = static_cast<int*>(malloc(sizeof(int)));
std::this_thread::sleep_for(std::chrono::seconds(5));
}
return 0;
}
Compile and run the file leak_loop_malloc.cpp
and run memleak
$ g++ leak_loop_malloc.cpp
$ ./a.out &
[1] 322727
$ sudo ./memleak -p 322727
using default object: libc.so.6
using page size: 4096
tracing kernel: false
Tracing outstanding memory allocs... Hit Ctrl-C to end
[10:14:43] Top 1 stacks with outstanding allocations:
4 bytes in 1 allocations from stack
0 [<000055bddfee31d5>] main+0x2c
1 [<00007f7298e29d90>] __libc_init_first+0x90
[10:14:48] Top 1 stacks with outstanding allocations:
8 bytes in 2 allocations from stack
0 [<000055bddfee31d5>] main+0x2c
1 [<00007f7298e29d90>] __libc_init_first+0x90
^C[10:14:49] Top 1 stacks with outstanding allocations:
8 bytes in 2 allocations from stack
0 [<000055bddfee31d5>] main+0x2c
1 [<00007f7298e29d90>] __libc_init_first+0x90
done
Some may think that it can happened only on special main
function.
So I added foo
function and you can check that foo
is disappeared in this time.
Test file leak_loop_foo.cpp
#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;
}
Compile and run the file leak_loop_foo.cpp
and run memleak
$ sudo ./memleak -p 322693
using default object: libc.so.6
using page size: 4096
tracing kernel: false
Tracing outstanding memory allocs... Hit Ctrl-C to end
[9:59:28] Top 1 stacks with outstanding allocations:
4 bytes in 1 allocations from stack
0 [<00007f88376ae98c>] _Znwm+0x1c
1 [<0000562fdbf6a1e4>] main+0x27
2 [<00007f8837229d90>] __libc_init_first+0x90
[9:59:33] Top 1 stacks with outstanding allocations:
8 bytes in 2 allocations from stack
0 [<00007f88376ae98c>] _Znwm+0x1c
1 [<0000562fdbf6a1e4>] main+0x27
2 [<00007f8837229d90>] __libc_init_first+0x90
^C[9:59:33] Top 1 stacks with outstanding allocations:
8 bytes in 2 allocations from stack
0 [<00007f88376ae98c>] _Znwm+0x1c
1 [<0000562fdbf6a1e4>] main+0x27
2 [<00007f8837229d90>] __libc_init_first+0x90
done
You can find main
but cannot find foo
.
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
$ objdump -D a.out
... snip ...
00000000000011bd <main>:
11bd: f3 0f 1e fa endbr64
11c1: 55 push %rbp
11c2: 48 89 e5 mov %rsp,%rbp
11c5: 48 83 ec 30 sub $0x30,%rsp
11c9: 89 7d dc mov %edi,-0x24(%rbp)
11cc: 48 89 75 d0 mov %rsi,-0x30(%rbp)
11d0: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
11d7: 00 00
11d9: 48 89 45 f8 mov %rax,-0x8(%rbp)
11dd: 31 c0 xor %eax,%eax
11df: e8 c5 ff ff ff call 11a9 <_Z3foov>
11e4: 48 89 45 f0 mov %rax,-0x10(%rbp)
11e8: c7 45 e4 05 00 00 00 movl $0x5,-0x1c(%rbp)
11ef: 48 8d 55 e4 lea -0x1c(%rbp),%rdx
11f3: 48 8d 45 e8 lea -0x18(%rbp),%rax
11f7: 48 89 d6 mov %rdx,%rsi
11fa: 48 89 c7 mov %rax,%rdi
11fd: e8 8c 00 00 00 call 128e <_ZNSt6chrono8durationIlSt5ratioILl1ELl1EEEC1IivEERKT_>
1202: 48 8d 45 e8 lea -0x18(%rbp),%rax
1206: 48 89 c7 mov %rax,%rdi
1209: e8 ef 01 00 00 call 13fd <_ZNSt11this_thread9sleep_forIlSt5ratioILl1ELl1EEEEvRKNSt6chrono8durationIT_T0_EE>
120e: eb cf jmp 11df <main+0x22>
Environment
x86_64
$ cat /etc/issue.net
Ubuntu 22.04.3 LTS
$ g++ --version
g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
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 operator new
function.
Kernel uses rbp
registers to unwind the stack.
You can check that on file arch/x86/events/core.c
in Linux kernel, at line 2875
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 operator new
function doesn't push and pop rbp
register on its prologue and epilogue.
_Znwm
is a mangled name of operator new
.
00000000000ae970 <_Znwm@@GLIBCXX_3.4>:
ae970: f3 0f 1e fa endbr64
ae974: 48 85 ff test %rdi,%rdi
ae977: b8 01 00 00 00 mov $0x1,%eax
ae97c: 53 push %rbx
ae97d: 48 0f 45 c7 cmovne %rdi,%rax
ae981: 48 89 c3 mov %rax,%rbx
ae984: 48 89 df mov %rbx,%rdi
ae987: e8 44 07 ff ff call 9f0d0 <malloc@plt>
ae98c: 48 85 c0 test %rax,%rax
ae98f: 74 02 je ae993 <_Znwm@@GLIBCXX_3.4+0x23>
ae991: 5b pop %rbx
ae992: c3 ret
ae993: e8 88 07 ff ff call 9f120 <_ZSt15get_new_handlerv@plt>
ae998: 48 85 c0 test %rax,%rax
ae99b: 0f 84 dd 3d ff ff je a277e <__cxa_throw_bad_array_new_length@@CXXABI_1.3.8+0x132>
ae9a1: ff d0 call *%rax
ae9a3: eb df jmp ae984 <_Znwm@@GLIBCXX_3.4+0x14>
ae9a5: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
ae9ac: 00 00 00
ae9af: 90 nop
The first backrace is acquired by pc
register.
And others use stack
and rbp
to unwind the stack.
Since operator new
function doesn't touch rbp
register.
The value inside rbp
register is same as if it is currently in the function that calls operator new
.
For example, imagine the call chain looks like this: main
-> foo
-> operator new
-> malloc
As memleak
saves stack backtrace information on uretprobe
of malloc
function.
So current pc
is inside operator new
function. And the rbp
value is same as when the pc
register is inside foo
function.
perf_callchain_user
function acquires top stack by using pc
register.
And it uses stack
and rbp
to unwind others, and at this point, it thinks foo
is the top frame.
So it doesn't print top frame because top frame is already printed by using pc
.
I found that following feature could resolve this issue.
#4463
I patched memleak
like followings, on that branch.
(It seems complicated but it's not. Almost every change is to change skel
string to obj
)
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 memleak
report prints foo
function.
root@ubuntu-Standard-PC-i440FX-PIIX-1996:~# ./memleak -p 8081
using default object: libc.so.6
using page size: 4096
tracing kernel: false
libbpf: Error in bpf_create_map_xattr(uw_stacks):Invalid argument(-22). Retrying
without BTF.
Tracing outstanding memory allocs... Hit Ctrl-C to end
^C[1:43:4] Top 1 stacks with outstanding allocations:
4 bytes in 1 allocations from stack
get_entries, err: 0
0 [<00007f348d6e1dad>] _Znwm+0x1d [/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19]
1 [<000055863a200798>] _Z3foov+0xe [/home/worker/a.out]
2 [<000055863a2007de>] main+0x44 [/home/worker/a.out]
3 [<00007f348d2dfec5>] __libc_start_main+0xf5 [/lib/x86_64-linux-gnu/libc-2.19.so]
4 [<000055863a2006aa>] _start+0x2a [/home/worker/a.out]
done
root@ubuntu-Standard-PC-i440FX-PIIX-1996:~#
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;
}