Last active
October 7, 2021 17:55
-
-
Save nadavrot/9a071a02ea20ab483158c443638dea7a to your computer and use it in GitHub Desktop.
A BCC/EBPF tool for detecting short-lived memory allocations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
from __future__ import print_function | |
from bcc import BPF | |
from bcc.utils import printb | |
from time import sleep | |
import sys | |
import pdb | |
if len(sys.argv) < 2: | |
print("USAGE: tracer PID") | |
sys.exit(1) | |
pid = int(sys.argv[1]) | |
# BPF program | |
b = BPF(text=""" | |
#include <uapi/linux/ptrace.h> | |
// Maps the allocated pointer to allocation time. | |
BPF_HASH(alloc_time, u64, u64); | |
// For all short-allocations, map the stack id to number of hits. | |
BPF_HASH(short_allocs, u32, u64, 1<<12); | |
BPF_STACK_TRACE(stack_traces, 1<<12); | |
int alloc_leave(struct pt_regs *ctx) { | |
u64 ptr = PT_REGS_RC(ctx); | |
u64 start_time = bpf_ktime_get_ns(); | |
u64 zero = 0; | |
u64 *val; | |
val = alloc_time.lookup_or_try_init(&ptr, &zero); | |
if (val) { | |
*val = start_time; | |
} | |
return 0; | |
} | |
int free_enter(struct pt_regs *ctx, size_t size) { | |
u64 key = bpf_get_current_pid_tgid(); | |
u64 ptr = PT_REGS_PARM1(ctx); | |
int loc = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); | |
if (loc < 0) | |
return 0; | |
u64 *val = alloc_time.lookup(&ptr); | |
if (!val) { | |
return 0; | |
} | |
u64 curr_time = bpf_ktime_get_ns(); | |
u64 alloc_time = *val; | |
u64 delta = curr_time - alloc_time; | |
if (delta > 1000000) { // This is 1/1000 of a second | |
return 0; | |
} | |
u64 zero = 0; | |
val = short_allocs.lookup_or_try_init(&loc, &zero); | |
if (val) { | |
*val += 1; | |
} | |
return 0; | |
} | |
""") | |
b.attach_uretprobe(name="c", sym="malloc", fn_name="alloc_leave", pid=pid) | |
b.attach_uprobe(name="c", sym="free", fn_name="free_enter", pid=pid) | |
print("Attaching to malloc/free in pid %d, Ctrl+C to quit." % pid) | |
stack_traces = b.get_table("stack_traces") | |
short_allocs = b.get_table("short_allocs") | |
def print_top_stack_traces(stack_traces, short_allocs , num): | |
top_allocs = sorted(short_allocs.items(), key = lambda kv: kv[1].value, reverse=True)[:num] | |
idx = 0 | |
for st, hits in top_allocs: | |
print("[%d ]Hits: %d" % (idx, hits.value)) | |
print_stack_trace(stack_traces, st.value) | |
print("-" * 10) | |
idx += 1 | |
def print_stack_trace(stack_traces, stack_id): | |
for addr in stack_traces.walk(stack_id): | |
printb(b"\t[%08x] %s" % (addr, b.sym(addr, pid, show_offset=True))) | |
# Process events until Ctrl-C | |
while 1: | |
try: | |
sleep(1) | |
print_top_stack_traces(stack_traces, short_allocs, 10) | |
except KeyboardInterrupt: | |
print("Detaching to malloc in pid %d." % pid) | |
b.detach_uretprobe(name="c", sym="malloc", pid=pid) | |
b.detach_uprobe(name="c", sym="free", pid=pid) | |
exit(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment