xref: /aosp_15_r20/external/bcc/tests/python/test_tools_memleak.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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