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
28*98cfbe42SPedro Tammela    def pre_suite(self, testcount, testist):
29c25e4736SBrenda J. Butler        '''run commands before test_runner goes into a test loop'''
30*98cfbe42SPedro Tammela        self.testidlist = [tidx['id'] for tidx in testlist]
31*98cfbe42SPedro Tammela        super().pre_suite(testcount, testlist)
32c25e4736SBrenda J. Butler        if self.args.verbose > 1:
33c25e4736SBrenda J. Butler            print('{}.pre_suite'.format(self.sub_class))
34c25e4736SBrenda J. Butler        if self.args.valgrind:
35c25e4736SBrenda J. Butler            self._add_to_tap('1..{}\n'.format(self.testcount))
36c25e4736SBrenda J. Butler
37c25e4736SBrenda J. Butler    def post_suite(self, index):
38c25e4736SBrenda J. Butler        '''run commands after test_runner goes into a test loop'''
39c25e4736SBrenda J. Butler        super().post_suite(index)
40c25e4736SBrenda J. Butler        if self.args.verbose > 1:
41c25e4736SBrenda J. Butler            print('{}.post_suite'.format(self.sub_class))
42915c158dSLucas Bates        #print('{}'.format(self.tap))
43915c158dSLucas Bates        for xx in range(index - 1, self.testcount):
44915c158dSLucas Bates            res = TestResult('{}-mem'.format(self.testidlist[xx]), 'Test skipped')
45915c158dSLucas Bates            res.set_result(ResultState.skip)
46915c158dSLucas Bates            res.set_errormsg('Skipped because of prior setup/teardown failure')
47915c158dSLucas Bates            self._add_results(res)
48c25e4736SBrenda J. Butler        if self.args.verbose < 4:
49c25e4736SBrenda J. Butler            subprocess.check_output('rm -f vgnd-*.log', shell=True)
50c25e4736SBrenda J. Butler
51c25e4736SBrenda J. Butler    def add_args(self, parser):
52c25e4736SBrenda J. Butler        super().add_args(parser)
53c25e4736SBrenda J. Butler        self.argparser_group = self.argparser.add_argument_group(
54c25e4736SBrenda J. Butler            'valgrind',
55c25e4736SBrenda J. Butler            'options for valgrindPlugin (run command under test under Valgrind)')
56c25e4736SBrenda J. Butler
57c25e4736SBrenda J. Butler        self.argparser_group.add_argument(
58c25e4736SBrenda J. Butler            '-V', '--valgrind', action='store_true',
59c25e4736SBrenda J. Butler            help='Run commands under valgrind')
60c25e4736SBrenda J. Butler
61c25e4736SBrenda J. Butler        return self.argparser
62c25e4736SBrenda J. Butler
63c25e4736SBrenda J. Butler    def adjust_command(self, stage, command):
64c25e4736SBrenda J. Butler        super().adjust_command(stage, command)
65c25e4736SBrenda J. Butler        cmdform = 'list'
66c25e4736SBrenda J. Butler        cmdlist = list()
67c25e4736SBrenda J. Butler
68c25e4736SBrenda J. Butler        if not self.args.valgrind:
69c25e4736SBrenda J. Butler            return command
70c25e4736SBrenda J. Butler
71c25e4736SBrenda J. Butler        if self.args.verbose > 1:
72c25e4736SBrenda J. Butler            print('{}.adjust_command'.format(self.sub_class))
73c25e4736SBrenda J. Butler
74c25e4736SBrenda J. Butler        if not isinstance(command, list):
75c25e4736SBrenda J. Butler            cmdform = 'str'
76c25e4736SBrenda J. Butler            cmdlist = command.split()
77c25e4736SBrenda J. Butler        else:
78c25e4736SBrenda J. Butler            cmdlist = command
79c25e4736SBrenda J. Butler
80c25e4736SBrenda J. Butler        if stage == 'execute':
81c25e4736SBrenda J. Butler            if self.args.verbose > 1:
82c25e4736SBrenda J. Butler                print('adjust_command:  stage is {}; inserting valgrind stuff in command [{}] list [{}]'.
83c25e4736SBrenda J. Butler                      format(stage, command, cmdlist))
84c25e4736SBrenda J. Butler            cmdlist.insert(0, '--track-origins=yes')
85c25e4736SBrenda J. Butler            cmdlist.insert(0, '--show-leak-kinds=definite,indirect')
86c25e4736SBrenda J. Butler            cmdlist.insert(0, '--leak-check=full')
87c25e4736SBrenda J. Butler            cmdlist.insert(0, '--log-file=vgnd-{}.log'.format(self.args.testid))
88c25e4736SBrenda J. Butler            cmdlist.insert(0, '-v')  # ask for summary of non-leak errors
89c25e4736SBrenda J. Butler            cmdlist.insert(0, ENVIR['VALGRIND_BIN'])
90c25e4736SBrenda J. Butler        else:
91c25e4736SBrenda J. Butler            pass
92c25e4736SBrenda J. Butler
93c25e4736SBrenda J. Butler        if cmdform == 'str':
94c25e4736SBrenda J. Butler            command = ' '.join(cmdlist)
95c25e4736SBrenda J. Butler        else:
96c25e4736SBrenda J. Butler            command = cmdlist
97c25e4736SBrenda J. Butler
98c25e4736SBrenda J. Butler        if self.args.verbose > 1:
99c25e4736SBrenda J. Butler            print('adjust_command:  return command [{}]'.format(command))
100c25e4736SBrenda J. Butler        return command
101c25e4736SBrenda J. Butler
102c25e4736SBrenda J. Butler    def post_execute(self):
103c25e4736SBrenda J. Butler        if not self.args.valgrind:
104c25e4736SBrenda J. Butler            return
105c25e4736SBrenda J. Butler
106255c1c72SLucas Bates        res = TestResult('{}-mem'.format(self.args.testid),
107255c1c72SLucas Bates              '{} memory leak check'.format(self.args.test_name))
108255c1c72SLucas Bates        if self.args.test_skip:
109255c1c72SLucas Bates            res.set_result(ResultState.skip)
110255c1c72SLucas Bates            res.set_errormsg('Test case designated as skipped.')
111255c1c72SLucas Bates            self._add_results(res)
112255c1c72SLucas Bates            return
113255c1c72SLucas Bates
114c25e4736SBrenda J. Butler        self.definitely_lost_re = re.compile(
115c25e4736SBrenda J. Butler            r'definitely lost:\s+([,0-9]+)\s+bytes in\s+([,0-9]+)\sblocks', re.MULTILINE | re.DOTALL)
116c25e4736SBrenda J. Butler        self.indirectly_lost_re = re.compile(
117c25e4736SBrenda J. Butler            r'indirectly lost:\s+([,0-9]+)\s+bytes in\s+([,0-9]+)\s+blocks', re.MULTILINE | re.DOTALL)
118c25e4736SBrenda J. Butler        self.possibly_lost_re = re.compile(
119c25e4736SBrenda J. Butler            r'possibly lost:\s+([,0-9]+)bytes in\s+([,0-9]+)\s+blocks', re.MULTILINE | re.DOTALL)
120c25e4736SBrenda J. Butler        self.non_leak_error_re = re.compile(
121c25e4736SBrenda J. Butler            r'ERROR SUMMARY:\s+([,0-9]+) errors from\s+([,0-9]+)\s+contexts', re.MULTILINE | re.DOTALL)
122c25e4736SBrenda J. Butler
123c25e4736SBrenda J. Butler        def_num = 0
124c25e4736SBrenda J. Butler        ind_num = 0
125c25e4736SBrenda J. Butler        pos_num = 0
126c25e4736SBrenda J. Butler        nle_num = 0
127c25e4736SBrenda J. Butler
128c25e4736SBrenda J. Butler        # what about concurrent test runs?  Maybe force them to be in different directories?
129c25e4736SBrenda J. Butler        with open('vgnd-{}.log'.format(self.args.testid)) as vfd:
130c25e4736SBrenda J. Butler            content = vfd.read()
131c25e4736SBrenda J. Butler            def_mo = self.definitely_lost_re.search(content)
132c25e4736SBrenda J. Butler            ind_mo = self.indirectly_lost_re.search(content)
133c25e4736SBrenda J. Butler            pos_mo = self.possibly_lost_re.search(content)
134c25e4736SBrenda J. Butler            nle_mo = self.non_leak_error_re.search(content)
135c25e4736SBrenda J. Butler
136c25e4736SBrenda J. Butler            if def_mo:
137c25e4736SBrenda J. Butler                def_num = int(def_mo.group(2))
138c25e4736SBrenda J. Butler            if ind_mo:
139c25e4736SBrenda J. Butler                ind_num = int(ind_mo.group(2))
140c25e4736SBrenda J. Butler            if pos_mo:
141c25e4736SBrenda J. Butler                pos_num = int(pos_mo.group(2))
142c25e4736SBrenda J. Butler            if nle_mo:
143c25e4736SBrenda J. Butler                nle_num = int(nle_mo.group(1))
144c25e4736SBrenda J. Butler
145c25e4736SBrenda J. Butler        mem_results = ''
146c25e4736SBrenda J. Butler        if (def_num > 0) or (ind_num > 0) or (pos_num > 0) or (nle_num > 0):
147c25e4736SBrenda J. Butler            mem_results += 'not '
148915c158dSLucas Bates            res.set_result(ResultState.fail)
149915c158dSLucas Bates            res.set_failmsg('Memory leak detected')
150915c158dSLucas Bates            res.append_failmsg(content)
151915c158dSLucas Bates        else:
152915c158dSLucas Bates            res.set_result(ResultState.success)
153915c158dSLucas Bates
154915c158dSLucas Bates        self._add_results(res)
155c25e4736SBrenda J. Butler
156c25e4736SBrenda J. Butler
157915c158dSLucas Bates    def _add_results(self, res):
158915c158dSLucas Bates        self._tsr.add_resultdata(res)
159915c158dSLucas Bates
160c25e4736SBrenda J. Butler    def _add_to_tap(self, more_tap_output):
161c25e4736SBrenda J. Butler        self.tap += more_tap_output
162