.SH NAME
memleak \- Print a summary of outstanding allocations and their call stacks to detect memory leaks. Uses Linux eBPF/bcc.
.SH SYNOPSIS
-.B memleak [-h] [-p PID] [-t] [-a] [-o OLDER] [-c COMMAND] [-s SAMPLE_RATE] [-d STACK_DEPTH] [INTERVAL] [COUNT]
+.B memleak [-h] [-p PID] [-t] [-a] [-o OLDER] [-c COMMAND] [-s SAMPLE_RATE] [-d STACK_DEPTH] [-T TOP] [INTERVAL] [COUNT]
.SH DESCRIPTION
memleak traces and matches memory allocation and deallocation requests, and
collects call stacks for each allocation. memleak can then print a summary
When tracing a specific process, memleak instruments malloc and free from libc.
When tracing all processes, memleak instruments kmalloc and kfree.
+memleak may introduce significant overhead when tracing processes that allocate
+and free many blocks very quickly. See the OVERHEAD section below.
+
The stack depth is limited to 10 by default (+1 for the current instruction pointer),
but it can be controlled using the \-d switch if deeper stacks are required.
Capture STACK_DEPTH frames (or less) when obtaining allocation call stacks.
The default value is 10.
.TP
+\-t TOP
+Print only the top TOP stacks (sorted by size).
+The default value is 10.
+.TP
INTERVAL
Print a summary of oustanding allocations and their call stacks every INTERVAL seconds.
The default interval is 5 seconds.
#
.B memleak -p 1005 -a
.TP
+Sample roughly every 5th allocation (~20%) of the call stacks and print the top 5
+stacks 10 times before quitting.
+#
+.B memleak -s 5 --top=5 10
+.TP
Run ./allocs and print outstanding allocation stacks for that process:
#
.B memleak -c "./allocs"
from bcc import BPF
from time import sleep
+from datetime import datetime
import argparse
import subprocess
import ctypes
help="sample every N-th allocation to decrease the overhead")
parser.add_argument("-d", "--stack-depth", default=10, type=int,
help="maximum stack depth to capture")
+parser.add_argument("-T", "--top", type=int, default=10,
+ help="display only this many top allocating stacks (by size)")
args = parser.parse_args()
sample_every_n = args.sample_rate
num_prints = args.count
max_stack_size = args.stack_depth + 2
+top_stacks = args.top
if command is not None:
print("Executing '%s' and tracing the resulting process." % command)
def print_outstanding():
stacks = {}
- print("*** Outstanding allocations:")
+ print("[%s] Top %d stacks with outstanding allocations:" %
+ (datetime.now().strftime("%H:%M:%S"), top_stacks))
allocs = bpf_program.get_table("allocs")
for address, info in sorted(allocs.items(), key=lambda a: a[1].size):
if Time.monotonic_time() - min_age_ns < info.timestamp_ns:
if args.show_allocs:
print("\taddr = %x size = %s" %
(address.value, info.size))
- for stack, (count, size) in sorted(stacks.items(),
- key=lambda s: s[1][1]):
+ to_show = sorted(stacks.items(), key=lambda s: s[1][1])[-top_stacks:]
+ for stack, (count, size) in to_show:
print("\t%d bytes in %d allocations from stack\n\t\t%s" %
(size, count, stack.replace(";", "\n\t\t")))
# ./memleak.py -h
usage: memleak.py [-h] [-p PID] [-t] [-a] [-o OLDER] [-c COMMAND]
- [-s SAMPLE_RATE] [-d STACK_DEPTH]
+ [-s SAMPLE_RATE] [-d STACK_DEPTH] [-T TOP]
[interval] [count]
Trace outstanding memory allocations that weren't freed.
sample every N-th allocation to decrease the overhead
-d STACK_DEPTH, --stack_depth STACK_DEPTH
maximum stack depth to capture
+ -T TOP, --top TOP display only this many top allocating stacks (by size)
EXAMPLES: