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