forked from iovisor/bcc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
memleak: expand allocator coverage (iovisor#1214)
* memleak: handle libc allocation functions other than malloc * memleak: use tracepoints to track kernel allocations * memleak: add combined-only mode With large number of outstanding allocations, amount of data passed from kernel becomes large, which slows everything down. This patch calculates allocation statistics inside kernel, allowing user- space part to pull combined statistics data only, thus significantly reducing amount of passed data. * memleak: increase hashtable capacities There are a lot of allocations happen in kernel. Default values are not enough to keep up. * test: add a test for the memleak tool
- Loading branch information
Showing
6 changed files
with
522 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
#!/usr/bin/env python | ||
|
||
from unittest import main, skipUnless, TestCase | ||
import distutils.version | ||
import os | ||
import subprocess | ||
import sys | ||
import tempfile | ||
|
||
TOOLS_DIR = "../../tools/" | ||
|
||
|
||
class cfg: | ||
cmd_format = "" | ||
|
||
# Amount of memory to leak. Note, that test application allocates memory | ||
# for its own needs in libc, so this amount should be large enough to be | ||
# the biggest allocation. | ||
leaking_amount = 30000 | ||
|
||
|
||
def kernel_version_ge(major, minor): | ||
# True if running kernel is >= X.Y | ||
version = distutils.version.LooseVersion(os.uname()[2]).version | ||
if version[0] > major: | ||
return True | ||
if version[0] < major: | ||
return False | ||
if minor and version[1] < minor: | ||
return False | ||
return True | ||
|
||
|
||
def setUpModule(): | ||
# Build the memory leaking application. | ||
c_src = 'test_tools_memleak_leaker_app.c' | ||
tmp_dir = tempfile.mkdtemp(prefix='bcc-test-memleak-') | ||
c_src_full = os.path.dirname(sys.argv[0]) + os.path.sep + c_src | ||
exec_dst = tmp_dir + os.path.sep + 'leaker_app' | ||
|
||
if subprocess.call(['gcc', '-g', '-O0', '-o', exec_dst, c_src_full]) != 0: | ||
print("can't compile the leaking application") | ||
raise Exception | ||
|
||
# Taking two snapshot with one second interval. Getting the largest | ||
# allocation. Since attaching to a program happens with a delay, we wait | ||
# for the first snapshot, then issue the command to the app. Finally, | ||
# second snapshot is used to extract the information. | ||
# Helper utilities "timeout" and "setbuf" are used to limit overall running | ||
# time, and to disable buffering. | ||
cfg.cmd_format = ( | ||
'stdbuf -o 0 -i 0 timeout -s KILL 10s ' + TOOLS_DIR + | ||
'memleak.py -c "{} {{}} {}" -T 1 1 2'.format(exec_dst, | ||
cfg.leaking_amount)) | ||
|
||
|
||
@skipUnless(kernel_version_ge(4, 6), "requires kernel >= 4.6") | ||
class MemleakToolTests(TestCase): | ||
def run_leaker(self, leak_kind): | ||
# Starting memleak.py, which in turn launches the leaking application. | ||
p = subprocess.Popen(cfg.cmd_format.format(leak_kind), | ||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, | ||
shell=True) | ||
|
||
# Waiting for the first report. | ||
while True: | ||
p.poll() | ||
if p.returncode is not None: | ||
break | ||
line = p.stdout.readline() | ||
if "with outstanding allocations" in line: | ||
break | ||
|
||
# At this point, memleak.py have already launched application and set | ||
# probes. Sending command to the leaking application to make its | ||
# allocations. | ||
out = p.communicate(input="\n")[0] | ||
|
||
# If there were memory leaks, they are in the output. Filter the lines | ||
# containing "byte" substring. Every interesting line is expected to | ||
# start with "N bytes from" | ||
x = [x for x in out.split('\n') if 'byte' in x] | ||
|
||
self.assertTrue(len(x) >= 1, | ||
msg="At least one line should have 'byte' substring.") | ||
|
||
# Taking last report. | ||
x = x[-1].split() | ||
self.assertTrue(len(x) >= 1, | ||
msg="There should be at least one word in the line.") | ||
|
||
# First word is the leak amount in bytes. | ||
return int(x[0]) | ||
|
||
def test_malloc(self): | ||
self.assertEqual(cfg.leaking_amount, self.run_leaker("malloc")) | ||
|
||
def test_calloc(self): | ||
self.assertEqual(cfg.leaking_amount, self.run_leaker("calloc")) | ||
|
||
def test_realloc(self): | ||
self.assertEqual(cfg.leaking_amount, self.run_leaker("realloc")) | ||
|
||
def test_posix_memalign(self): | ||
self.assertEqual(cfg.leaking_amount, self.run_leaker("posix_memalign")) | ||
|
||
def test_valloc(self): | ||
self.assertEqual(cfg.leaking_amount, self.run_leaker("valloc")) | ||
|
||
def test_memalign(self): | ||
self.assertEqual(cfg.leaking_amount, self.run_leaker("memalign")) | ||
|
||
def test_pvalloc(self): | ||
self.assertEqual(cfg.leaking_amount, self.run_leaker("pvalloc")) | ||
|
||
def test_aligned_alloc(self): | ||
self.assertEqual(cfg.leaking_amount, self.run_leaker("aligned_alloc")) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// This is a program that leaks memory, used for memory leak detector testing. | ||
|
||
#include <fcntl.h> | ||
#include <malloc.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <sys/stat.h> | ||
#include <sys/types.h> | ||
#include <unistd.h> | ||
|
||
static void generate_leak(const char *kind, int amount) { | ||
void *ptr = NULL; | ||
|
||
if (strcmp(kind, "malloc") == 0) { | ||
printf("leaking via malloc, %p\n", malloc(amount)); | ||
return; | ||
} | ||
|
||
if (strcmp(kind, "calloc") == 0) { | ||
printf("leaking via calloc, %p\n", calloc(amount, 1)); | ||
return; | ||
} | ||
|
||
if (strcmp(kind, "realloc") == 0) { | ||
printf("leaking via realloc, %p\n", realloc(malloc(10), amount)); | ||
return; | ||
} | ||
|
||
if (strcmp(kind, "posix_memalign") == 0) { | ||
posix_memalign(&ptr, 512, amount); | ||
printf("leaking via posix_memalign, %p\n", ptr); | ||
return; | ||
} | ||
|
||
if (strcmp(kind, "valloc") == 0) { | ||
printf("leaking via valloc, %p\n", valloc(amount)); | ||
return; | ||
} | ||
|
||
if (strcmp(kind, "memalign") == 0) { | ||
printf("leaking via memalign, %p\n", memalign(512, amount)); | ||
return; | ||
} | ||
|
||
if (strcmp(kind, "pvalloc") == 0) { | ||
printf("leaking via pvalloc, %p\n", pvalloc(amount)); | ||
return; | ||
} | ||
|
||
if (strcmp(kind, "aligned_alloc") == 0) { | ||
printf("leaking via aligned_alloc, %p\n", aligned_alloc(512, amount)); | ||
return; | ||
} | ||
|
||
if (strcmp(kind, "no_leak") == 0) { | ||
void *ptr = malloc(amount); | ||
printf("ptr = %p\n", ptr); | ||
free(ptr); | ||
return; | ||
} | ||
|
||
printf("unknown leak type '%s'\n", kind); | ||
} | ||
|
||
int main(int argc, char *argv[]) { | ||
if (argc < 2) { | ||
printf("usage: leak-userspace <kind-of-leak> [amount]\n"); | ||
return EXIT_SUCCESS; | ||
} | ||
|
||
const char *kind = argv[1]; | ||
|
||
int amount = 30; | ||
if (argc > 2) { | ||
amount = atoi(argv[2]); | ||
if (amount < 1) | ||
amount = 1; | ||
} | ||
|
||
// Wait for something in stdin to give external detector time to attach. | ||
char c; | ||
read(0, &c, sizeof(c)); | ||
|
||
// Do the work. | ||
generate_leak(kind, amount); | ||
return EXIT_SUCCESS; | ||
} |
Oops, something went wrong.