1''' 2run the command under test, under valgrind and collect memory leak info 3as a separate test. 4''' 5 6 7import os 8import re 9import signal 10from string import Template 11import subprocess 12import time 13from TdcPlugin import TdcPlugin 14 15from tdc_config import * 16 17def vp_extract_num_from_string(num_as_string_maybe_with_commas): 18 return int(num_as_string_maybe_with_commas.replace(',','')) 19 20class SubPlugin(TdcPlugin): 21 def __init__(self): 22 self.sub_class = 'valgrind/SubPlugin' 23 self.tap = '' 24 super().__init__() 25 26 def pre_suite(self, testcount, testidlist): 27 '''run commands before test_runner goes into a test loop''' 28 super().pre_suite(testcount, testidlist) 29 if self.args.verbose > 1: 30 print('{}.pre_suite'.format(self.sub_class)) 31 if self.args.valgrind: 32 self._add_to_tap('1..{}\n'.format(self.testcount)) 33 34 def post_suite(self, index): 35 '''run commands after test_runner goes into a test loop''' 36 super().post_suite(index) 37 self._add_to_tap('\n|---\n') 38 if self.args.verbose > 1: 39 print('{}.post_suite'.format(self.sub_class)) 40 print('{}'.format(self.tap)) 41 if self.args.verbose < 4: 42 subprocess.check_output('rm -f vgnd-*.log', shell=True) 43 44 def add_args(self, parser): 45 super().add_args(parser) 46 self.argparser_group = self.argparser.add_argument_group( 47 'valgrind', 48 'options for valgrindPlugin (run command under test under Valgrind)') 49 50 self.argparser_group.add_argument( 51 '-V', '--valgrind', action='store_true', 52 help='Run commands under valgrind') 53 54 return self.argparser 55 56 def adjust_command(self, stage, command): 57 super().adjust_command(stage, command) 58 cmdform = 'list' 59 cmdlist = list() 60 61 if not self.args.valgrind: 62 return command 63 64 if self.args.verbose > 1: 65 print('{}.adjust_command'.format(self.sub_class)) 66 67 if not isinstance(command, list): 68 cmdform = 'str' 69 cmdlist = command.split() 70 else: 71 cmdlist = command 72 73 if stage == 'execute': 74 if self.args.verbose > 1: 75 print('adjust_command: stage is {}; inserting valgrind stuff in command [{}] list [{}]'. 76 format(stage, command, cmdlist)) 77 cmdlist.insert(0, '--track-origins=yes') 78 cmdlist.insert(0, '--show-leak-kinds=definite,indirect') 79 cmdlist.insert(0, '--leak-check=full') 80 cmdlist.insert(0, '--log-file=vgnd-{}.log'.format(self.args.testid)) 81 cmdlist.insert(0, '-v') # ask for summary of non-leak errors 82 cmdlist.insert(0, ENVIR['VALGRIND_BIN']) 83 else: 84 pass 85 86 if cmdform == 'str': 87 command = ' '.join(cmdlist) 88 else: 89 command = cmdlist 90 91 if self.args.verbose > 1: 92 print('adjust_command: return command [{}]'.format(command)) 93 return command 94 95 def post_execute(self): 96 if not self.args.valgrind: 97 return 98 99 self.definitely_lost_re = re.compile( 100 r'definitely lost:\s+([,0-9]+)\s+bytes in\s+([,0-9]+)\sblocks', re.MULTILINE | re.DOTALL) 101 self.indirectly_lost_re = re.compile( 102 r'indirectly lost:\s+([,0-9]+)\s+bytes in\s+([,0-9]+)\s+blocks', re.MULTILINE | re.DOTALL) 103 self.possibly_lost_re = re.compile( 104 r'possibly lost:\s+([,0-9]+)bytes in\s+([,0-9]+)\s+blocks', re.MULTILINE | re.DOTALL) 105 self.non_leak_error_re = re.compile( 106 r'ERROR SUMMARY:\s+([,0-9]+) errors from\s+([,0-9]+)\s+contexts', re.MULTILINE | re.DOTALL) 107 108 def_num = 0 109 ind_num = 0 110 pos_num = 0 111 nle_num = 0 112 113 # what about concurrent test runs? Maybe force them to be in different directories? 114 with open('vgnd-{}.log'.format(self.args.testid)) as vfd: 115 content = vfd.read() 116 def_mo = self.definitely_lost_re.search(content) 117 ind_mo = self.indirectly_lost_re.search(content) 118 pos_mo = self.possibly_lost_re.search(content) 119 nle_mo = self.non_leak_error_re.search(content) 120 121 if def_mo: 122 def_num = int(def_mo.group(2)) 123 if ind_mo: 124 ind_num = int(ind_mo.group(2)) 125 if pos_mo: 126 pos_num = int(pos_mo.group(2)) 127 if nle_mo: 128 nle_num = int(nle_mo.group(1)) 129 130 mem_results = '' 131 if (def_num > 0) or (ind_num > 0) or (pos_num > 0) or (nle_num > 0): 132 mem_results += 'not ' 133 134 mem_results += 'ok {} - {}-mem # {}\n'.format( 135 self.args.test_ordinal, self.args.testid, 'memory leak check') 136 self._add_to_tap(mem_results) 137 if mem_results.startswith('not '): 138 print('{}'.format(content)) 139 self._add_to_tap(content) 140 141 def _add_to_tap(self, more_tap_output): 142 self.tap += more_tap_output 143