Added option to display only top N stacks by size
authorSasha Goldshtein <goldshtn@gmail.com>
Tue, 9 Feb 2016 19:15:41 +0000 (11:15 -0800)
committerSasha Goldshtein <goldshtn@gmail.com>
Tue, 9 Feb 2016 19:15:41 +0000 (11:15 -0800)
man/man8/memleak.8
tools/memleak.py
tools/memleak_examples.txt

index 835aafb..fd02b96 100644 (file)
@@ -2,7 +2,7 @@
 .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
@@ -11,6 +11,9 @@ of which call stacks performed allocations that weren't subsequently freed.
 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.
 
@@ -45,6 +48,10 @@ Record roughly every SAMPLE_RATE-th allocation to reduce overhead.
 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.
@@ -61,6 +68,11 @@ Print user outstanding allocation stacks and allocation details for the process
 #
 .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"
index 980ced0..41063c7 100755 (executable)
@@ -2,6 +2,7 @@
 
 from bcc import BPF
 from time import sleep
+from datetime import datetime
 import argparse
 import subprocess
 import ctypes
@@ -192,6 +193,8 @@ parser.add_argument("-s", "--sample-rate", default=1, type=int,
         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()
 
@@ -204,6 +207,7 @@ min_age_ns = 1e6 * args.older
 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)
@@ -235,7 +239,8 @@ decoder = StackDecoder(pid, bpf_program)
 
 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:
@@ -249,8 +254,8 @@ def print_outstanding():
                 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")))
 
index 2948606..1a11c0c 100644 (file)
@@ -151,7 +151,7 @@ USAGE message:
 
 # ./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.
@@ -177,6 +177,7 @@ optional arguments:
                         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: