1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*387f9dfdSAndroid Build Coastguard Worker 3*387f9dfdSAndroid Build Coastguard Workerfrom unittest import main, skipUnless, TestCase 4*387f9dfdSAndroid Build Coastguard Workerfrom utils import kernel_version_ge 5*387f9dfdSAndroid Build Coastguard Workerimport os 6*387f9dfdSAndroid Build Coastguard Workerimport subprocess 7*387f9dfdSAndroid Build Coastguard Workerimport sys 8*387f9dfdSAndroid Build Coastguard Workerimport tempfile 9*387f9dfdSAndroid Build Coastguard Worker 10*387f9dfdSAndroid Build Coastguard WorkerTOOLS_DIR = "/bcc/tools/" 11*387f9dfdSAndroid Build Coastguard Worker 12*387f9dfdSAndroid Build Coastguard Worker 13*387f9dfdSAndroid Build Coastguard Workerclass cfg: 14*387f9dfdSAndroid Build Coastguard Worker cmd_format = "" 15*387f9dfdSAndroid Build Coastguard Worker 16*387f9dfdSAndroid Build Coastguard Worker # Amount of memory to leak. Note, that test application allocates memory 17*387f9dfdSAndroid Build Coastguard Worker # for its own needs in libc, so this amount should be large enough to be 18*387f9dfdSAndroid Build Coastguard Worker # the biggest allocation. 19*387f9dfdSAndroid Build Coastguard Worker leaking_amount = 30000 20*387f9dfdSAndroid Build Coastguard Worker 21*387f9dfdSAndroid Build Coastguard Worker 22*387f9dfdSAndroid Build Coastguard Workerdef setUpModule(): 23*387f9dfdSAndroid Build Coastguard Worker # Build the memory leaking application. 24*387f9dfdSAndroid Build Coastguard Worker c_src = 'test_tools_memleak_leaker_app.c' 25*387f9dfdSAndroid Build Coastguard Worker tmp_dir = tempfile.mkdtemp(prefix='bcc-test-memleak-') 26*387f9dfdSAndroid Build Coastguard Worker c_src_full = os.path.dirname(sys.argv[0]) + os.path.sep + c_src 27*387f9dfdSAndroid Build Coastguard Worker exec_dst = tmp_dir + os.path.sep + 'leaker_app' 28*387f9dfdSAndroid Build Coastguard Worker 29*387f9dfdSAndroid Build Coastguard Worker if subprocess.call(['gcc', '-g', '-O0', '-o', exec_dst, c_src_full]) != 0: 30*387f9dfdSAndroid Build Coastguard Worker print("can't compile the leaking application") 31*387f9dfdSAndroid Build Coastguard Worker raise Exception 32*387f9dfdSAndroid Build Coastguard Worker 33*387f9dfdSAndroid Build Coastguard Worker # Taking two snapshot with one second interval. Getting the largest 34*387f9dfdSAndroid Build Coastguard Worker # allocation. Since attaching to a program happens with a delay, we wait 35*387f9dfdSAndroid Build Coastguard Worker # for the first snapshot, then issue the command to the app. Finally, 36*387f9dfdSAndroid Build Coastguard Worker # second snapshot is used to extract the information. 37*387f9dfdSAndroid Build Coastguard Worker # Helper utilities "timeout" and "setbuf" are used to limit overall running 38*387f9dfdSAndroid Build Coastguard Worker # time, and to disable buffering. 39*387f9dfdSAndroid Build Coastguard Worker cfg.cmd_format = ( 40*387f9dfdSAndroid Build Coastguard Worker 'stdbuf -o 0 -i 0 timeout -s KILL 10s ' + TOOLS_DIR + 41*387f9dfdSAndroid Build Coastguard Worker 'memleak.py -c "{} {{}} {}" -T 1 1 2'.format(exec_dst, 42*387f9dfdSAndroid Build Coastguard Worker cfg.leaking_amount)) 43*387f9dfdSAndroid Build Coastguard Worker 44*387f9dfdSAndroid Build Coastguard Worker 45*387f9dfdSAndroid Build Coastguard Worker@skipUnless(kernel_version_ge(4, 6), "requires kernel >= 4.6") 46*387f9dfdSAndroid Build Coastguard Workerclass MemleakToolTests(TestCase): 47*387f9dfdSAndroid Build Coastguard Worker def tearDown(self): 48*387f9dfdSAndroid Build Coastguard Worker if self.p: 49*387f9dfdSAndroid Build Coastguard Worker del(self.p) 50*387f9dfdSAndroid Build Coastguard Worker def run_leaker(self, leak_kind): 51*387f9dfdSAndroid Build Coastguard Worker # Starting memleak.py, which in turn launches the leaking application. 52*387f9dfdSAndroid Build Coastguard Worker self.p = subprocess.Popen(cfg.cmd_format.format(leak_kind), 53*387f9dfdSAndroid Build Coastguard Worker stdin=subprocess.PIPE, stdout=subprocess.PIPE, 54*387f9dfdSAndroid Build Coastguard Worker shell=True) 55*387f9dfdSAndroid Build Coastguard Worker 56*387f9dfdSAndroid Build Coastguard Worker # Waiting for the first report. 57*387f9dfdSAndroid Build Coastguard Worker while True: 58*387f9dfdSAndroid Build Coastguard Worker self.p.poll() 59*387f9dfdSAndroid Build Coastguard Worker if self.p.returncode is not None: 60*387f9dfdSAndroid Build Coastguard Worker break 61*387f9dfdSAndroid Build Coastguard Worker line = self.p.stdout.readline() 62*387f9dfdSAndroid Build Coastguard Worker if b"with outstanding allocations" in line: 63*387f9dfdSAndroid Build Coastguard Worker break 64*387f9dfdSAndroid Build Coastguard Worker 65*387f9dfdSAndroid Build Coastguard Worker # At this point, memleak.py have already launched application and set 66*387f9dfdSAndroid Build Coastguard Worker # probes. Sending command to the leaking application to make its 67*387f9dfdSAndroid Build Coastguard Worker # allocations. 68*387f9dfdSAndroid Build Coastguard Worker out = self.p.communicate(input=b"\n")[0] 69*387f9dfdSAndroid Build Coastguard Worker 70*387f9dfdSAndroid Build Coastguard Worker # If there were memory leaks, they are in the output. Filter the lines 71*387f9dfdSAndroid Build Coastguard Worker # containing "byte" substring. Every interesting line is expected to 72*387f9dfdSAndroid Build Coastguard Worker # start with "N bytes from" 73*387f9dfdSAndroid Build Coastguard Worker x = [x for x in out.split(b'\n') if b'byte' in x] 74*387f9dfdSAndroid Build Coastguard Worker 75*387f9dfdSAndroid Build Coastguard Worker self.assertTrue(len(x) >= 1, 76*387f9dfdSAndroid Build Coastguard Worker msg="At least one line should have 'byte' substring.") 77*387f9dfdSAndroid Build Coastguard Worker 78*387f9dfdSAndroid Build Coastguard Worker # Taking last report. 79*387f9dfdSAndroid Build Coastguard Worker x = x[-1].split() 80*387f9dfdSAndroid Build Coastguard Worker self.assertTrue(len(x) >= 1, 81*387f9dfdSAndroid Build Coastguard Worker msg="There should be at least one word in the line.") 82*387f9dfdSAndroid Build Coastguard Worker 83*387f9dfdSAndroid Build Coastguard Worker # First word is the leak amount in bytes. 84*387f9dfdSAndroid Build Coastguard Worker return int(x[0]) 85*387f9dfdSAndroid Build Coastguard Worker 86*387f9dfdSAndroid Build Coastguard Worker def test_malloc(self): 87*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(cfg.leaking_amount, self.run_leaker("malloc")) 88*387f9dfdSAndroid Build Coastguard Worker 89*387f9dfdSAndroid Build Coastguard Worker def test_calloc(self): 90*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(cfg.leaking_amount, self.run_leaker("calloc")) 91*387f9dfdSAndroid Build Coastguard Worker 92*387f9dfdSAndroid Build Coastguard Worker def test_realloc(self): 93*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(cfg.leaking_amount, self.run_leaker("realloc")) 94*387f9dfdSAndroid Build Coastguard Worker 95*387f9dfdSAndroid Build Coastguard Worker def test_posix_memalign(self): 96*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(cfg.leaking_amount, self.run_leaker("posix_memalign")) 97*387f9dfdSAndroid Build Coastguard Worker 98*387f9dfdSAndroid Build Coastguard Worker def test_valloc(self): 99*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(cfg.leaking_amount, self.run_leaker("valloc")) 100*387f9dfdSAndroid Build Coastguard Worker 101*387f9dfdSAndroid Build Coastguard Worker def test_memalign(self): 102*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(cfg.leaking_amount, self.run_leaker("memalign")) 103*387f9dfdSAndroid Build Coastguard Worker 104*387f9dfdSAndroid Build Coastguard Worker def test_pvalloc(self): 105*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(cfg.leaking_amount, self.run_leaker("pvalloc")) 106*387f9dfdSAndroid Build Coastguard Worker 107*387f9dfdSAndroid Build Coastguard Worker def test_aligned_alloc(self): 108*387f9dfdSAndroid Build Coastguard Worker self.assertEqual(cfg.leaking_amount, self.run_leaker("aligned_alloc")) 109*387f9dfdSAndroid Build Coastguard Worker 110*387f9dfdSAndroid Build Coastguard Worker 111*387f9dfdSAndroid Build Coastguard Workerif __name__ == "__main__": 112*387f9dfdSAndroid Build Coastguard Worker main() 113