Commit 49df9944 authored by Sasha Goldshtein's avatar Sasha Goldshtein

memleak: Migrate to new symbols resolution API

Remove usyms.py dependency and replace with new symbols API.
parent febed330
......@@ -11,31 +11,13 @@
# Licensed under the Apache License, Version 2.0 (the "License")
# Copyright (C) 2016 Sasha Goldshtein.
from bcc import BPF, ProcessSymbols
from bcc import BPF
from time import sleep
from datetime import datetime
import argparse
import subprocess
import os
class KStackDecoder(object):
def refresh(self):
pass
def __call__(self, addr):
return "%s [kernel] (%x)" % (BPF.ksym(addr), addr)
class UStackDecoder(object):
def __init__(self, pid):
self.pid = pid
self.proc_sym = ProcessSymbols(pid)
def refresh(self):
self.proc_sym.refresh_code_ranges()
def __call__(self, addr):
return "%s (%x)" % (self.proc_sym.decode_addr(addr), addr)
class Allocation(object):
def __init__(self, stack, size):
self.stack = stack
......@@ -240,8 +222,6 @@ else:
bpf_program.attach_kretprobe(event="__kmalloc", fn_name="alloc_exit")
bpf_program.attach_kprobe(event="kfree", fn_name="free_enter")
decoder = KStackDecoder() if kernel_trace else UStackDecoder(pid)
def print_outstanding():
print("[%s] Top %d stacks with outstanding allocations:" %
(datetime.now().strftime("%H:%M:%S"), top_stacks))
......@@ -256,8 +236,12 @@ def print_outstanding():
if info.stack_id in alloc_info:
alloc_info[info.stack_id].update(info.size)
else:
stack = list(stack_traces.walk(info.stack_id, decoder))
alloc_info[info.stack_id] = Allocation(stack,
stack = list(stack_traces.walk(info.stack_id))
combined = []
for addr in stack:
combined.append(bpf_program.sym(addr, pid,
show_module=True, show_address=True))
alloc_info[info.stack_id] = Allocation(combined,
info.size)
if args.show_allocs:
print("\taddr = %x size = %s" %
......@@ -277,7 +261,6 @@ while True:
sleep(interval)
except KeyboardInterrupt:
exit()
decoder.refresh()
print_outstanding()
count_so_far += 1
if num_prints is not None and count_so_far >= num_prints:
......
......@@ -10,13 +10,13 @@ For example:
Attaching to malloc and free in pid 5193, Ctrl+C to quit.
[11:16:33] Top 2 stacks with outstanding allocations:
80 bytes in 5 allocations from stack
main+0x6d [/home/vagrant/allocs] (400862)
__libc_start_main+0xf0 [/usr/lib64/libc-2.21.so] (7fd460ac2790)
main+0x6d [allocs]
__libc_start_main+0xf0 [libc-2.21.so]
[11:16:34] Top 2 stacks with outstanding allocations:
160 bytes in 10 allocations from stack
main+0x6d [/home/vagrant/allocs] (400862)
__libc_start_main+0xf0 [/usr/lib64/libc-2.21.so] (7fd460ac2790)
main+0x6d [allocs]
__libc_start_main+0xf0 [libc-2.21.so]
Each entry printed is a set of allocations that originate from the same call
......@@ -40,8 +40,8 @@ Attaching to malloc and free in pid 5193, Ctrl+C to quit.
addr = 948d30 size = 16
addr = 948cf0 size = 16
64 bytes in 4 allocations from stack
main+0x6d [/home/vagrant/allocs] (400862)
__libc_start_main+0xf0 [/usr/lib64/libc-2.21.so] (7fd460ac2790)
main+0x6d [allocs]
__libc_start_main+0xf0 [libc-2.21.so]
[11:16:34] Top 2 stacks with outstanding allocations:
addr = 948d50 size = 16
......@@ -55,8 +55,8 @@ Attaching to malloc and free in pid 5193, Ctrl+C to quit.
addr = 948d70 size = 16
addr = 948df0 size = 16
160 bytes in 10 allocations from stack
main+0x6d [/home/vagrant/allocs] (400862)
__libc_start_main+0xf0 [/usr/lib64/libc-2.21.so] (7fd460ac2790)
main+0x6d [allocs]
__libc_start_main+0xf0 [libc-2.21.so]
When using the -p switch, memleak traces the allocations of a particular
......@@ -67,35 +67,35 @@ For example:
Attaching to kmalloc and kfree, Ctrl+C to quit.
...
248 bytes in 4 allocations from stack
bpf_prog_load [kernel] (ffffffff8118c471)
sys_bpf [kernel] (ffffffff8118c8b5)
bpf_prog_load [kernel]
sys_bpf [kernel]
328 bytes in 1 allocations from stack
perf_mmap [kernel] (ffffffff811990fd)
mmap_region [kernel] (ffffffff811df5d4)
do_mmap [kernel] (ffffffff811dfb83)
vm_mmap_pgoff [kernel] (ffffffff811c494f)
sys_mmap_pgoff [kernel] (ffffffff811ddf02)
sys_mmap [kernel] (ffffffff8101b0ab)
perf_mmap [kernel]
mmap_region [kernel]
do_mmap [kernel]
vm_mmap_pgoff [kernel]
sys_mmap_pgoff [kernel]
sys_mmap [kernel]
464 bytes in 1 allocations from stack
traceprobe_command [kernel] (ffffffff81187cf2)
traceprobe_probes_write [kernel] (ffffffff81187d86)
probes_write [kernel] (ffffffff81181580)
__vfs_write [kernel] (ffffffff812237b7)
vfs_write [kernel] (ffffffff81223ec6)
sys_write [kernel] (ffffffff81224b85)
entry_SYSCALL_64_fastpath [kernel] (ffffffff8178182e)
traceprobe_command [kernel]
traceprobe_probes_write [kernel]
probes_write [kernel]
__vfs_write [kernel]
vfs_write [kernel]
sys_write [kernel]
entry_SYSCALL_64_fastpath [kernel]
8192 bytes in 1 allocations from stack
alloc_and_copy_ftrace_hash.constprop.59 [kernel] (ffffffff8115d17e)
ftrace_set_hash [kernel] (ffffffff8115e767)
ftrace_set_filter_ip [kernel] (ffffffff8115e9a8)
arm_kprobe [kernel] (ffffffff81148600)
enable_kprobe [kernel] (ffffffff811486f6)
kprobe_register [kernel] (ffffffff81182399)
perf_trace_init [kernel] (ffffffff8117c4e0)
perf_tp_event_init [kernel] (ffffffff81192479)
alloc_and_copy_ftrace_hash.constprop.59 [kernel]
ftrace_set_hash [kernel]
ftrace_set_filter_ip [kernel]
arm_kprobe [kernel]
enable_kprobe [kernel]
kprobe_register [kernel]
perf_trace_init [kernel]
perf_tp_event_init [kernel]
Here you can see that arming the kprobe to which our eBPF program is attached
......@@ -129,18 +129,18 @@ seconds, 3 times before quitting:
Attaching to malloc and free in pid 2614, Ctrl+C to quit.
[11:16:33] Top 2 stacks with outstanding allocations:
16 bytes in 1 allocations from stack
main+0x6d [/home/vagrant/allocs] (400862)
__libc_start_main+0xf0 [/usr/lib64/libc-2.21.so] (7fdc11ce8790)
main+0x6d [allocs]
__libc_start_main+0xf0 [libc-2.21.so]
[11:16:38] Top 2 stacks with outstanding allocations:
16 bytes in 1 allocations from stack
main+0x6d [/home/vagrant/allocs] (400862)
__libc_start_main+0xf0 [/usr/lib64/libc-2.21.so] (7fdc11ce8790)
main+0x6d [allocs]
__libc_start_main+0xf0 [libc-2.21.so]
[11:16:43] Top 2 stacks with outstanding allocations:
32 bytes in 2 allocations from stack
main+0x6d [/home/vagrant/allocs] (400862)
__libc_start_main+0xf0 [/usr/lib64/libc-2.21.so] (7fdc11ce8790)
main+0x6d [allocs]
__libc_start_main+0xf0 [libc-2.21.so]
Note that even though the application leaks 16 bytes of memory every second,
the report (printed every 5 seconds) doesn't "see" all the allocations because
......
......@@ -11,36 +11,21 @@
# Licensed under the Apache License, Version 2.0 (the "License")
# Copyright (C) 2016 Sasha Goldshtein.
from bcc import BPF, ProcessSymbols
from bcc import BPF, SymbolCache
from time import sleep
from datetime import datetime
import argparse
import subprocess
import os
class StackDecoder(object):
def __init__(self, pid):
self.pid = pid
if pid != -1:
self.proc_sym = ProcessSymbols(pid)
def refresh(self):
if self.pid != -1:
self.proc_sym.refresh_code_ranges()
def decode_stack(self, info, is_kernel_trace):
stack = ""
if info.num_frames <= 0:
return "???"
for i in range(0, info.num_frames):
addr = info.callstack[i]
if is_kernel_trace:
stack += " %s [kernel] (%x) ;" % \
(BPF.ksym(addr), addr)
else:
stack += " %s (%x) ;" % \
(self.proc_sym.decode_addr(addr), addr)
return stack
def decode_stack(bpf, pid, info):
stack = ""
if info.num_frames <= 0:
return "???"
for i in range(0, info.num_frames):
addr = info.callstack[i]
stack += " %s ;" % bpf.sym(addr, pid, show_address=True)
return stack
def run_command_get_output(command):
p = subprocess.Popen(command.split(),
......@@ -255,8 +240,6 @@ else:
bpf_program.attach_kretprobe(event="__kmalloc", fn_name="alloc_exit")
bpf_program.attach_kprobe(event="kfree", fn_name="free_enter")
decoder = StackDecoder(pid)
def print_outstanding():
stacks = {}
print("[%s] Top %d stacks with outstanding allocations:" %
......@@ -265,7 +248,7 @@ def print_outstanding():
for address, info in sorted(allocs.items(), key=lambda a: a[1].size):
if BPF.monotonic_time() - min_age_ns < info.timestamp_ns:
continue
stack = decoder.decode_stack(info, kernel_trace)
stack = decode_stack(bpf_program, pid, info)
if stack in stacks:
stacks[stack] = (stacks[stack][0] + 1,
stacks[stack][1] + info.size)
......@@ -288,7 +271,6 @@ while True:
sleep(interval)
except KeyboardInterrupt:
exit()
decoder.refresh()
print_outstanding()
count_so_far += 1
if num_prints is not None and count_so_far >= num_prints:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment