From c8148c8811314ff7672abbdd6e442755be078cc1 Mon Sep 17 00:00:00 2001 From: Sasha Goldshtein Date: Tue, 9 Feb 2016 11:15:41 -0800 Subject: [PATCH] Added option to display only top N stacks by size --- man/man8/memleak.8 | 14 +++++++++++++- tools/memleak.py | 11 ++++++++--- tools/memleak_examples.txt | 3 ++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/man/man8/memleak.8 b/man/man8/memleak.8 index 835aafb..fd02b96 100644 --- a/man/man8/memleak.8 +++ b/man/man8/memleak.8 @@ -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" diff --git a/tools/memleak.py b/tools/memleak.py index 980ced0..41063c7 100755 --- a/tools/memleak.py +++ b/tools/memleak.py @@ -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"))) diff --git a/tools/memleak_examples.txt b/tools/memleak_examples.txt index 2948606..1a11c0c 100644 --- a/tools/memleak_examples.txt +++ b/tools/memleak_examples.txt @@ -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: -- 2.7.4