Added -z and -Z switches for filtering by size, added copyright notices
authorSasha Goldshtein <goldshtn@gmail.com>
Wed, 10 Feb 2016 16:35:20 +0000 (08:35 -0800)
committerSasha Goldshtein <goldshtn@gmail.com>
Wed, 10 Feb 2016 16:35:20 +0000 (08:35 -0800)
man/man8/memleak.8
tools/memleak.c
tools/memleak.py
tools/memleak_examples.txt

index fd02b96..4abeea8 100644 (file)
@@ -2,7 +2,8 @@
 .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] [-T TOP] [INTERVAL] [COUNT]
+.B memleak [-h] [-p PID] [-t] [-a] [-o OLDER] [-c COMMAND] [-s SAMPLE_RATE]
+[-d STACK_DEPTH] [-T TOP] [-z MIN_SIZE] [-Z MAX_SIZE] [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
@@ -52,6 +53,12 @@ The default value is 10.
 Print only the top TOP stacks (sorted by size).
 The default value is 10.
 .TP
+\-z MIN_SIZE
+Capture only allocations that are larger than or equal to MIN_SIZE bytes.
+.TP
+\-Z MAX_SIZE
+Capture only allocations that are smaller than or equal to MAX_SIZE bytes.
+.TP
 INTERVAL
 Print a summary of oustanding allocations and their call stacks every INTERVAL seconds.
 The default interval is 5 seconds.
@@ -76,12 +83,17 @@ stacks 10 times before quitting.
 Run ./allocs and print outstanding allocation stacks for that process: 
 #
 .B memleak -c "./allocs"
+.TP
+Capture only allocations between 16 and 32 bytes in size:
+#
+.B memleak -z 16 -Z 32
 .SH OVERHEAD
 memleak can have significant overhead if the target process or kernel performs
 allocations at a very high rate. Pathological cases may exhibit up to 100x
 degradation in running time. Most of the time, however, memleak shouldn't cause
-a significant slowdown. You can also use the \-s switch to reduce the overhead
-further by capturing only every N-th allocation.
+a significant slowdown. You can use the \-s switch to reduce the overhead
+further by capturing only every N-th allocation. The \-z and \-Z switches can
+also reduce overhead by capturing only allocations of specific sizes.
 
 To determine the rate at which your application is calling malloc/free, or the
 rate at which your kernel is calling kmalloc/kfree, place a probe with perf and
index 5c1ce42..48de6af 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ * memleak.c    Trace and display outstanding allocations to detect 
+ *              memory leaks in user-mode processes and the kernel.
+ *
+ * Copyright (C) 2016 Sasha Goldshtein.
+ */
+
 #include <uapi/linux/ptrace.h>
 
 struct alloc_info_t {
@@ -33,9 +40,7 @@ static int grab_stack(struct pt_regs *ctx, struct alloc_info_t *info)
 
 int alloc_enter(struct pt_regs *ctx, size_t size)
 {
-        // Ideally, this should use a random number source, such as 
-        // BPF_FUNC_get_prandom_u32, but that's currently not supported
-        // by the bcc front-end.
+        SIZE_FILTER
         if (SAMPLE_EVERY_N > 1) {
                 u64 ts = bpf_ktime_get_ns();
                 if (ts % SAMPLE_EVERY_N != 0)
index 41063c7..7ab6b7c 100755 (executable)
@@ -1,4 +1,14 @@
 #!/usr/bin/env python
+#
+# memleak.py   Trace and display outstanding allocations to detect 
+#              memory leaks in user-mode processes and the kernel.
+#
+# USAGE: memleak.py [-h] [-p PID] [-t] [-a] [-o OLDER] [-c COMMAND]
+#                   [-s SAMPLE_RATE] [-d STACK_DEPTH] [-T TOP] [-z MIN_SIZE]
+#                   [-Z MAX_SIZE]
+#                   [interval] [count]
+#
+# Copyright (C) 2016 Sasha Goldshtein.
 
 from bcc import BPF
 from time import sleep
@@ -195,6 +205,10 @@ 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)")
+parser.add_argument("-z", "--min-size", type=int,
+        help="capture only allocations larger than this size")
+parser.add_argument("-Z", "--max-size", type=int,
+        help="capture only allocations smaller than this size")
 
 args = parser.parse_args()
 
@@ -208,6 +222,12 @@ sample_every_n = args.sample_rate
 num_prints = args.count
 max_stack_size = args.stack_depth + 2
 top_stacks = args.top
+min_size = args.min_size
+max_size = args.max_size
+
+if min_size is not None and max_size is not None and min_size > max_size:
+        print("min_size (-z) can't be greater than max_size (-Z)")
+        exit(1)
 
 if command is not None:
         print("Executing '%s' and tracing the resulting process." % command)
@@ -219,6 +239,17 @@ bpf_source = bpf_source.replace("SAMPLE_EVERY_N", str(sample_every_n))
 bpf_source = bpf_source.replace("GRAB_ONE_FRAME", max_stack_size *
         "\tif (!(info->callstack[depth++] = get_frame(&bp))) return depth;\n")
 bpf_source = bpf_source.replace("MAX_STACK_SIZE", str(max_stack_size))
+
+size_filter = ""
+if min_size is not None and max_size is not None:
+        size_filter = "if (size < %d || size > %d) return 0;" % \
+                      (min_size, max_size)
+elif min_size is not None:
+        size_filter = "if (size < %d) return 0;" % min_size
+elif max_size is not None:
+        size_filter = "if (size > %d) return 0;" % max_size
+bpf_source = bpf_source.replace("SIZE_FILTER", size_filter)
+
 bpf_program = BPF(text=bpf_source)
 
 if not kernel_trace:
index 2f7dd74..82a01b0 100644 (file)
@@ -178,6 +178,10 @@ optional arguments:
   -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)
+  -z MIN_SIZE, --min-size MIN_SIZE
+                        capture only allocations larger than this size
+  -Z MAX_SIZE, --max-size MAX_SIZE
+                        capture only allocations smaller than this size
 
 EXAMPLES: