xref: /openbmc/linux/tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py (revision 597473720f4dc69749542bfcfed4a927a43d935e)
1c25e4736SBrenda J. Butler'''
2c25e4736SBrenda J. Butlerrun the command under test, under valgrind and collect memory leak info
3c25e4736SBrenda J. Butleras a separate test.
4c25e4736SBrenda J. Butler'''
5c25e4736SBrenda J. Butler
6c25e4736SBrenda J. Butler
7c25e4736SBrenda J. Butlerimport os
8c25e4736SBrenda J. Butlerimport re
9c25e4736SBrenda J. Butlerimport signal
10c25e4736SBrenda J. Butlerfrom string import Template
11c25e4736SBrenda J. Butlerimport subprocess
12c25e4736SBrenda J. Butlerimport time
13c25e4736SBrenda J. Butlerfrom TdcPlugin import TdcPlugin
14915c158dSLucas Batesfrom TdcResults import *
15c25e4736SBrenda J. Butler
16c25e4736SBrenda J. Butlerfrom tdc_config import *
17c25e4736SBrenda J. Butler
18c25e4736SBrenda J. Butlerdef vp_extract_num_from_string(num_as_string_maybe_with_commas):
19c25e4736SBrenda J. Butler    return int(num_as_string_maybe_with_commas.replace(',',''))
20c25e4736SBrenda J. Butler
21c25e4736SBrenda J. Butlerclass SubPlugin(TdcPlugin):
22c25e4736SBrenda J. Butler    def __init__(self):
23c25e4736SBrenda J. Butler        self.sub_class = 'valgrind/SubPlugin'
24c25e4736SBrenda J. Butler        self.tap = ''
25915c158dSLucas Bates        self._tsr = TestSuiteReport()
26c25e4736SBrenda J. Butler        super().__init__()
27c25e4736SBrenda J. Butler
28c25e4736SBrenda J. Butler    def pre_suite(self, testcount, testidlist):
29c25e4736SBrenda J. Butler        '''run commands before test_runner goes into a test loop'''
30c25e4736SBrenda J. Butler        super().pre_suite(testcount, testidlist)
31c25e4736SBrenda J. Butler        if self.args.verbose > 1:
32c25e4736SBrenda J. Butler            print('{}.pre_suite'.format(self.sub_class))
33c25e4736SBrenda J. Butler        if self.args.valgrind:
34c25e4736SBrenda J. Butler            self._add_to_tap('1..{}\n'.format(self.testcount))
35c25e4736SBrenda J. Butler
36c25e4736SBrenda J. Butler    def post_suite(self, index):
37c25e4736SBrenda J. Butler        '''run commands after test_runner goes into a test loop'''
38c25e4736SBrenda J. Butler        super().post_suite(index)
39c25e4736SBrenda J. Butler        if self.args.verbose > 1:
40c25e4736SBrenda J. Butler            print('{}.post_suite'.format(self.sub_class))
41915c158dSLucas Bates        #print('{}'.format(self.tap))
42915c158dSLucas Bates        for xx in range(index - 1, self.testcount):
43915c158dSLucas Bates            res = TestResult('{}-mem'.format(self.testidlist[xx]), 'Test skipped')
44915c158dSLucas Bates            res.set_result(ResultState.skip)
45915c158dSLucas Bates            res.set_errormsg('Skipped because of prior setup/teardown failure')
46915c158dSLucas Bates            self._add_results(res)
47c25e4736SBrenda J. Butler        if self.args.verbose < 4:
48c25e4736SBrenda J. Butler            subprocess.check_output('rm -f vgnd-*.log', shell=True)
49c25e4736SBrenda J. Butler
50c25e4736SBrenda J. Butler    def add_args(self, parser):
51c25e4736SBrenda J. Butler        super().add_args(parser)
52c25e4736SBrenda J. Butler        self.argparser_group = self.argparser.add_argument_group(
53c25e4736SBrenda J. Butler            'valgrind',
54c25e4736SBrenda J. Butler            'options for valgrindPlugin (run command under test under Valgrind)')
55c25e4736SBrenda J. Butler
56c25e4736SBrenda J. Butler        self.argparser_group.add_argument(
57c25e4736SBrenda J. Butler            '-V', '--valgrind', action='store_true',
58c25e4736SBrenda J. Butler            help='Run commands under valgrind')
59c25e4736SBrenda J. Butler
60c25e4736SBrenda J. Butler        return self.argparser
61c25e4736SBrenda J. Butler
62c25e4736SBrenda J. Butler    def adjust_command(self, stage, command):
63c25e4736SBrenda J. Butler        super().adjust_command(stage, command)
64c25e4736SBrenda J. Butler        cmdform = 'list'
65c25e4736SBrenda J. Butler        cmdlist = list()
66c25e4736SBrenda J. Butler
67c25e4736SBrenda J. Butler        if not self.args.valgrind:
68c25e4736SBrenda J. Butler            return command
69c25e4736SBrenda J. Butler
70c25e4736SBrenda J. Butler        if self.args.verbose > 1:
71c25e4736SBrenda J. Butler            print('{}.adjust_command'.format(self.sub_class))
72c25e4736SBrenda J. Butler
73c25e4736SBrenda J. Butler        if not isinstance(command, list):
74c25e4736SBrenda J. Butler            cmdform = 'str'
75c25e4736SBrenda J. Butler            cmdlist = command.split()
76c25e4736SBrenda J. Butler        else:
77c25e4736SBrenda J. Butler            cmdlist = command
78c25e4736SBrenda J. Butler
79c25e4736SBrenda J. Butler        if stage == 'execute':
80c25e4736SBrenda J. Butler            if self.args.verbose > 1:
81c25e4736SBrenda J. Butler                print('adjust_command:  stage is {}; inserting valgrind stuff in command [{}] list [{}]'.
82c25e4736SBrenda J. Butler                      format(stage, command, cmdlist))
83c25e4736SBrenda J. Butler            cmdlist.insert(0, '--track-origins=yes')
84c25e4736SBrenda J. Butler            cmdlist.insert(0, '--show-leak-kinds=definite,indirect')
85c25e4736SBrenda J. Butler            cmdlist.insert(0, '--leak-check=full')
86c25e4736SBrenda J. Butler            cmdlist.insert(0, '--log-file=vgnd-{}.log'.format(self.args.testid))
87c25e4736SBrenda J. Butler            cmdlist.insert(0, '-v')  # ask for summary of non-leak errors
88c25e4736SBrenda J. Butler            cmdlist.insert(0, ENVIR['VALGRIND_BIN'])
89c25e4736SBrenda J. Butler        else:
90c25e4736SBrenda J. Butler            pass
91c25e4736SBrenda J. Butler
92c25e4736SBrenda J. Butler        if cmdform == 'str':
93c25e4736SBrenda J. Butler            command = ' '.join(cmdlist)
94c25e4736SBrenda J. Butler        else:
95c25e4736SBrenda J. Butler            command = cmdlist
96c25e4736SBrenda J. Butler
97c25e4736SBrenda J. Butler        if self.args.verbose > 1:
98c25e4736SBrenda J. Butler            print('adjust_command:  return command [{}]'.format(command))
99c25e4736SBrenda J. Butler        return command
100c25e4736SBrenda J. Butler
101c25e4736SBrenda J. Butler    def post_execute(self):
102c25e4736SBrenda J. Butler        if not self.args.valgrind:
103c25e4736SBrenda J. Butler            return
104c25e4736SBrenda J. Butler
105*255c1c72SLucas Bates        res = TestResult('{}-mem'.format(self.args.testid),
106*255c1c72SLucas Bates              '{} memory leak check'.format(self.args.test_name))
107*255c1c72SLucas Bates        if self.args.test_skip:
108*255c1c72SLucas Bates            res.set_result(ResultState.skip)
109*255c1c72SLucas Bates            res.set_errormsg('Test case designated as skipped.')
110*255c1c72SLucas Bates            self._add_results(res)
111*255c1c72SLucas Bates            return
112*255c1c72SLucas Bates
113c25e4736SBrenda J. Butler        self.definitely_lost_re = re.compile(
114c25e4736SBrenda J. Butler            r'definitely lost:\s+([,0-9]+)\s+bytes in\s+([,0-9]+)\sblocks', re.MULTILINE | re.DOTALL)
115c25e4736SBrenda J. Butler        self.indirectly_lost_re = re.compile(
116c25e4736SBrenda J. Butler            r'indirectly lost:\s+([,0-9]+)\s+bytes in\s+([,0-9]+)\s+blocks', re.MULTILINE | re.DOTALL)
117c25e4736SBrenda J. Butler        self.possibly_lost_re = re.compile(
118c25e4736SBrenda J. Butler            r'possibly lost:\s+([,0-9]+)bytes in\s+([,0-9]+)\s+blocks', re.MULTILINE | re.DOTALL)
119c25e4736SBrenda J. Butler        self.non_leak_error_re = re.compile(
120c25e4736SBrenda J. Butler            r'ERROR SUMMARY:\s+([,0-9]+) errors from\s+([,0-9]+)\s+contexts', re.MULTILINE | re.DOTALL)
121c25e4736SBrenda J. Butler
122c25e4736SBrenda J. Butler        def_num = 0
123c25e4736SBrenda J. Butler        ind_num = 0
124c25e4736SBrenda J. Butler        pos_num = 0
125c25e4736SBrenda J. Butler        nle_num = 0
126c25e4736SBrenda J. Butler
127c25e4736SBrenda J. Butler        # what about concurrent test runs?  Maybe force them to be in different directories?
128c25e4736SBrenda J. Butler        with open('vgnd-{}.log'.format(self.args.testid)) as vfd:
129c25e4736SBrenda J. Butler            content = vfd.read()
130c25e4736SBrenda J. Butler            def_mo = self.definitely_lost_re.search(content)
131c25e4736SBrenda J. Butler            ind_mo = self.indirectly_lost_re.search(content)
132c25e4736SBrenda J. Butler            pos_mo = self.possibly_lost_re.search(content)
133c25e4736SBrenda J. Butler            nle_mo = self.non_leak_error_re.search(content)
134c25e4736SBrenda J. Butler
135c25e4736SBrenda J. Butler            if def_mo:
136c25e4736SBrenda J. Butler                def_num = int(def_mo.group(2))
137c25e4736SBrenda J. Butler            if ind_mo:
138c25e4736SBrenda J. Butler                ind_num = int(ind_mo.group(2))
139c25e4736SBrenda J. Butler            if pos_mo:
140c25e4736SBrenda J. Butler                pos_num = int(pos_mo.group(2))
141c25e4736SBrenda J. Butler            if nle_mo:
142c25e4736SBrenda J. Butler                nle_num = int(nle_mo.group(1))
143c25e4736SBrenda J. Butler
144c25e4736SBrenda J. Butler        mem_results = ''
145c25e4736SBrenda J. Butler        if (def_num > 0) or (ind_num > 0) or (pos_num > 0) or (nle_num > 0):
146c25e4736SBrenda J. Butler            mem_results += 'not '
147915c158dSLucas Bates            res.set_result(ResultState.fail)
148915c158dSLucas Bates            res.set_failmsg('Memory leak detected')
149915c158dSLucas Bates            res.append_failmsg(content)
150915c158dSLucas Bates        else:
151915c158dSLucas Bates            res.set_result(ResultState.success)
152915c158dSLucas Bates
153915c158dSLucas Bates        self._add_results(res)
154c25e4736SBrenda J. Butler
155c25e4736SBrenda J. Butler
156915c158dSLucas Bates    def _add_results(self, res):
157915c158dSLucas Bates        self._tsr.add_resultdata(res)
158915c158dSLucas Bates
159c25e4736SBrenda J. Butler    def _add_to_tap(self, more_tap_output):
160c25e4736SBrenda J. Butler        self.tap += more_tap_output
161