0001 '''
0002 run the command under test, under valgrind and collect memory leak info
0003 as a separate test.
0004 '''
0005
0006
0007 import os
0008 import re
0009 import signal
0010 from string import Template
0011 import subprocess
0012 import time
0013 from TdcPlugin import TdcPlugin
0014 from TdcResults import *
0015
0016 from tdc_config import *
0017
0018 def vp_extract_num_from_string(num_as_string_maybe_with_commas):
0019 return int(num_as_string_maybe_with_commas.replace(',',''))
0020
0021 class SubPlugin(TdcPlugin):
0022 def __init__(self):
0023 self.sub_class = 'valgrind/SubPlugin'
0024 self.tap = ''
0025 self._tsr = TestSuiteReport()
0026 super().__init__()
0027
0028 def pre_suite(self, testcount, testidlist):
0029 '''run commands before test_runner goes into a test loop'''
0030 super().pre_suite(testcount, testidlist)
0031 if self.args.verbose > 1:
0032 print('{}.pre_suite'.format(self.sub_class))
0033 if self.args.valgrind:
0034 self._add_to_tap('1..{}\n'.format(self.testcount))
0035
0036 def post_suite(self, index):
0037 '''run commands after test_runner goes into a test loop'''
0038 super().post_suite(index)
0039 if self.args.verbose > 1:
0040 print('{}.post_suite'.format(self.sub_class))
0041
0042 for xx in range(index - 1, self.testcount):
0043 res = TestResult('{}-mem'.format(self.testidlist[xx]), 'Test skipped')
0044 res.set_result(ResultState.skip)
0045 res.set_errormsg('Skipped because of prior setup/teardown failure')
0046 self._add_results(res)
0047 if self.args.verbose < 4:
0048 subprocess.check_output('rm -f vgnd-*.log', shell=True)
0049
0050 def add_args(self, parser):
0051 super().add_args(parser)
0052 self.argparser_group = self.argparser.add_argument_group(
0053 'valgrind',
0054 'options for valgrindPlugin (run command under test under Valgrind)')
0055
0056 self.argparser_group.add_argument(
0057 '-V', '--valgrind', action='store_true',
0058 help='Run commands under valgrind')
0059
0060 return self.argparser
0061
0062 def adjust_command(self, stage, command):
0063 super().adjust_command(stage, command)
0064 cmdform = 'list'
0065 cmdlist = list()
0066
0067 if not self.args.valgrind:
0068 return command
0069
0070 if self.args.verbose > 1:
0071 print('{}.adjust_command'.format(self.sub_class))
0072
0073 if not isinstance(command, list):
0074 cmdform = 'str'
0075 cmdlist = command.split()
0076 else:
0077 cmdlist = command
0078
0079 if stage == 'execute':
0080 if self.args.verbose > 1:
0081 print('adjust_command: stage is {}; inserting valgrind stuff in command [{}] list [{}]'.
0082 format(stage, command, cmdlist))
0083 cmdlist.insert(0, '--track-origins=yes')
0084 cmdlist.insert(0, '--show-leak-kinds=definite,indirect')
0085 cmdlist.insert(0, '--leak-check=full')
0086 cmdlist.insert(0, '--log-file=vgnd-{}.log'.format(self.args.testid))
0087 cmdlist.insert(0, '-v')
0088 cmdlist.insert(0, ENVIR['VALGRIND_BIN'])
0089 else:
0090 pass
0091
0092 if cmdform == 'str':
0093 command = ' '.join(cmdlist)
0094 else:
0095 command = cmdlist
0096
0097 if self.args.verbose > 1:
0098 print('adjust_command: return command [{}]'.format(command))
0099 return command
0100
0101 def post_execute(self):
0102 if not self.args.valgrind:
0103 return
0104
0105 res = TestResult('{}-mem'.format(self.args.testid),
0106 '{} memory leak check'.format(self.args.test_name))
0107 if self.args.test_skip:
0108 res.set_result(ResultState.skip)
0109 res.set_errormsg('Test case designated as skipped.')
0110 self._add_results(res)
0111 return
0112
0113 self.definitely_lost_re = re.compile(
0114 r'definitely lost:\s+([,0-9]+)\s+bytes in\s+([,0-9]+)\sblocks', re.MULTILINE | re.DOTALL)
0115 self.indirectly_lost_re = re.compile(
0116 r'indirectly lost:\s+([,0-9]+)\s+bytes in\s+([,0-9]+)\s+blocks', re.MULTILINE | re.DOTALL)
0117 self.possibly_lost_re = re.compile(
0118 r'possibly lost:\s+([,0-9]+)bytes in\s+([,0-9]+)\s+blocks', re.MULTILINE | re.DOTALL)
0119 self.non_leak_error_re = re.compile(
0120 r'ERROR SUMMARY:\s+([,0-9]+) errors from\s+([,0-9]+)\s+contexts', re.MULTILINE | re.DOTALL)
0121
0122 def_num = 0
0123 ind_num = 0
0124 pos_num = 0
0125 nle_num = 0
0126
0127
0128 with open('vgnd-{}.log'.format(self.args.testid)) as vfd:
0129 content = vfd.read()
0130 def_mo = self.definitely_lost_re.search(content)
0131 ind_mo = self.indirectly_lost_re.search(content)
0132 pos_mo = self.possibly_lost_re.search(content)
0133 nle_mo = self.non_leak_error_re.search(content)
0134
0135 if def_mo:
0136 def_num = int(def_mo.group(2))
0137 if ind_mo:
0138 ind_num = int(ind_mo.group(2))
0139 if pos_mo:
0140 pos_num = int(pos_mo.group(2))
0141 if nle_mo:
0142 nle_num = int(nle_mo.group(1))
0143
0144 mem_results = ''
0145 if (def_num > 0) or (ind_num > 0) or (pos_num > 0) or (nle_num > 0):
0146 mem_results += 'not '
0147 res.set_result(ResultState.fail)
0148 res.set_failmsg('Memory leak detected')
0149 res.append_failmsg(content)
0150 else:
0151 res.set_result(ResultState.success)
0152
0153 self._add_results(res)
0154
0155
0156 def _add_results(self, res):
0157 self._tsr.add_resultdata(res)
0158
0159 def _add_to_tap(self, more_tap_output):
0160 self.tap += more_tap_output