Skip to content

Commit

Permalink
memleak: Migrate to new symbols resolution API
Browse files Browse the repository at this point in the history
Remove usyms.py dependency and replace with new symbols API.
  • Loading branch information
goldshtn committed Feb 21, 2017
1 parent febed33 commit 49df994
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 89 deletions.
31 changes: 7 additions & 24 deletions tools/memleak.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -240,8 +222,6 @@ def run_command_get_pid(command):
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))
Expand All @@ -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" %
Expand All @@ -277,7 +261,6 @@ def print_outstanding():
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:
Expand Down
74 changes: 37 additions & 37 deletions tools/memleak_example.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
38 changes: 10 additions & 28 deletions tools/old/memleak.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -255,8 +240,6 @@ def run_command_get_pid(command):
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:" %
Expand All @@ -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)
Expand All @@ -288,7 +271,6 @@ def print_outstanding():
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:
Expand Down

0 comments on commit 49df994

Please sign in to comment.