From 50459640e0af4f3109d4b00a7e4242ff6347f227 Mon Sep 17 00:00:00 2001 From: Sasha Goldshtein Date: Wed, 10 Feb 2016 08:35:20 -0800 Subject: [PATCH] Added -z and -Z switches for filtering by size, added copyright notices --- man/man8/memleak.8 | 18 +++++++++++++++--- tools/memleak.c | 11 ++++++++--- tools/memleak.py | 31 +++++++++++++++++++++++++++++++ tools/memleak_examples.txt | 4 ++++ 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/man/man8/memleak.8 b/man/man8/memleak.8 index fd02b96..4abeea8 100644 --- a/man/man8/memleak.8 +++ b/man/man8/memleak.8 @@ -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 diff --git a/tools/memleak.c b/tools/memleak.c index 5c1ce42..48de6af 100644 --- a/tools/memleak.c +++ b/tools/memleak.c @@ -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 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) diff --git a/tools/memleak.py b/tools/memleak.py index 41063c7..7ab6b7c 100755 --- a/tools/memleak.py +++ b/tools/memleak.py @@ -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: diff --git a/tools/memleak_examples.txt b/tools/memleak_examples.txt index 2f7dd74..82a01b0 100644 --- a/tools/memleak_examples.txt +++ b/tools/memleak_examples.txt @@ -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: -- 2.7.4