gdb_dejagnu.py revision 34d759e2a60ad7ce7da18031b9a7d9f31d658ef6
1#! /usr/bin/python 2 3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""The gdb dejagnu test wrapper.""" 8import optparse 9import os 10from os import path 11import re 12import shutil 13import stat 14import sys 15import tempfile 16import time 17 18from utils import command_executer 19from utils import logger 20from utils import misc 21 22from run_dejagnu import TryAcquireMachine 23 24_VALID_TEST_RESULTS = ['FAIL', 'UNRESOLVED', 'XPASS', 25 'ERROR', 'UNSUPPORTED', 'PASS'] 26 27 28def ProcessArguments(argv): 29 """Processing/validating script arguments.""" 30 parser = optparse.OptionParser(description=( 31 'Launches gdb dejagnu test in chroot for chromeos toolchain, compares ' 32 'the test result with a repository baseline and prints out the result.'), 33 usage='run_dejagnu options') 34 parser.add_option('-c', '--chromeos_root', dest='chromeos_root', 35 help='Required. Specify chromeos root') 36 parser.add_option('-m', '--mount', dest='mount', 37 help=('Specify gdb source to mount instead of "auto". ' 38 'Under "auto" mode, which is the default - gdb is ' 39 'checked out and built automatically at default ' 40 'directories. Under "mount" mode ' 41 '- the gdb_source is set to "$chromeos_' 42 'root/chroot/usr/local/toolchain_root/gdb", which is ' 43 'the mount point for this option value.')) 44 parser.add_option('-b', '--board', dest='board', 45 help=('Required. Specify board.')) 46 parser.add_option('-r', '--remote', dest='remote', 47 help=('Required. Specify addresses/names of the board, ' 48 'seperate each address/name using comma(\',\').')) 49 parser.add_option('--cleanup', dest='cleanup', default=None, 50 help=('Optional. Values to this option could be ' 51 '\'chroot\' (delete chroot) and ' 52 '\'chromeos\' (delete the whole chromeos tree).')) 53 54 options, args = parser.parse_args(argv) 55 56 if not options.chromeos_root: 57 raise Exception('Missing argument for --chromeos_root.') 58 if not options.remote: 59 raise Exception('Missing argument for --remote.') 60 if not options.board: 61 raise Exception('Missing argument for --board.') 62 if options.cleanup == 'mount' and not options.mount: 63 raise Exception('--cleanup=\'mount\' not valid unless --mount is given.') 64 if options.cleanup and not ( 65 options.cleanup == 'mount' or 66 options.cleanup == 'chroot' or options.cleanup == 'chromeos'): 67 raise Exception('Invalid option value for --cleanup') 68 if options.cleanup and options.keep_intermediate_files: 69 raise Exception('Only one of --keep and --cleanup could be given.') 70 71 return options 72 73 74class DejagnuExecuter(object): 75 """The class wrapper for dejagnu test executer.""" 76 77 def __init__(self, base_dir, source_dir, chromeos_root, remote, board, 78 cleanup): 79 self._l = logger.GetLogger() 80 self._chromeos_root = chromeos_root 81 self._chromeos_chroot = path.join(chromeos_root, 'chroot') 82 83 self._remote = remote 84 self._board = board 85 ## Compute target from board 86 self._target = misc.GetCtargetFromBoard(board, chromeos_root) 87 if not self._target: 88 raise Exception('Unsupported board "%s"' % board) 89 self._executer = command_executer.GetCommandExecuter() 90 self._base_dir = base_dir 91 self._tmp_abs = None 92 self._cleanup = cleanup 93 self._sshflag = ('-o StrictHostKeyChecking=no' + 94 '-o CheckHostIP=no' + 95 '-o UserKnownHostsFile=$(mktemp)') 96 97 if source_dir: 98 self._source_dir = source_dir 99 self._mount_flag = 'USE="mounted_sources"' 100 self.MountSource() 101 else: 102 self._source_dir = None 103 self._mount_flag = '' 104 105 def SetupTestingDir(self): 106 self._tmp_abs = tempfile.mkdtemp(prefix='dejagnu_', dir=path.join( 107 self._chromeos_chroot, 'tmp')) 108 self._tmp = self._tmp_abs[len(self._chromeos_chroot):] 109 self._tmp_testing_rsa = path.join(self._tmp, 'testing_rsa') 110 self._tmp_testing_rsa_abs = path.join(self._tmp_abs, 'testing_rsa') 111 112 def PrepareTestingRsaKeys(self): 113 if not path.isfile(self._tmp_testing_rsa_abs): 114 shutil.copy(path.join( 115 self._chromeos_root, 116 'src/scripts/mod_for_test_scripts/ssh_keys/testing_rsa'), 117 self._tmp_testing_rsa_abs) 118 os.chmod(self._tmp_testing_rsa_abs, stat.S_IRUSR) 119 120 def PrepareTestFiles(self): 121 """Prepare site.exp and board exp files.""" 122 # Create the boards directory. 123 os.mkdir('%s/boards' % self._tmp_abs) 124 125 # Generate the chromeos.exp file. 126 with open('%s/boards/gdb.exp.in' % self._base_dir, 'r') as template_file: 127 content = template_file.read() 128 substitutions = dict({ 129 '__boardname__': self._board, 130 '__board_hostname__': self._remote, 131 '__tmp_testing_rsa__': self._tmp_testing_rsa, 132 '__tmp_dir__': self._tmp}) 133 for pat, sub in substitutions.items(): 134 content = content.replace(pat, sub) 135 136 board_file_name = '%s/boards/%s.exp' % (self._tmp_abs, self._board) 137 with open(board_file_name, 'w') as board_file: 138 board_file.write(content) 139 140 # Generate the site file 141 with open('%s/site.exp' % self._tmp_abs, 'w') as site_file: 142 site_file.write('set target_list "%s"\n' % self._board) 143 144 with open('%s/boards/gdbserver.sh.in' % self._base_dir, 'r') \ 145 as template_file: 146 content = template_file.read() 147 substitutions = dict({ 148 '__board_hostname__': self._remote, 149 '__tmp_testing_rsa__': self._tmp_testing_rsa, 150 '__tmp_dir__': self._tmp}) 151 for pat, sub in substitutions.items(): 152 content = content.replace(pat, sub) 153 154 gdbserver_file_name = '%s/boards/gdbserver.sh' % (self._tmp_abs) 155 with open(gdbserver_file_name, 'w') as board_file: 156 board_file.write(content) 157 158 st = os.stat(gdbserver_file_name) 159 os.chmod(gdbserver_file_name, st.st_mode | stat.S_IXGRP | stat.S_IXUSR) 160 161 def PrepareGdb(self): 162 self.PrepareGdbDefault() 163 164 def PrepareGdbDefault(self): 165 ret = self._executer.ChrootRunCommand( 166 self._chromeos_root, 167 'equery w cross-%s/gdb' % self._target, return_output=True)[1] 168 ret = path.basename(ret.strip()) 169 170 matcher = re.match(r'(.*).ebuild', ret) 171 if matcher: 172 gdb_reversion = matcher.group(1) 173 else: 174 raise Exception('Failed to get gdb reversion.') 175 gdb_version = gdb_reversion.split('-r')[0] 176 gdb_portage_dir = '/var/tmp/portage/cross-%s/%s/work' % ( 177 self._target, gdb_reversion) 178 self._gdb_source_dir = path.join(gdb_portage_dir, gdb_version) 179 180 ret = self._executer.ChrootRunCommand( 181 self._chromeos_root, 182 ('sudo %s ebuild $(equery w cross-%s/gdb) clean compile' % ( 183 self._mount_flag, self._target))) 184 if ret: 185 raise Exception('ebuild gdb failed.') 186 187 def PrepareGdbserver(self): 188 self.PrepareGdbserverDefault() 189 190 def PrepareGdbserverDefault(self): 191 cmd = ('./setup_board --board {0}; ' 192 '{1} emerge-{0} gdb'.format(self._board, self._mount_flag)) 193 ret = self._executer.ChrootRunCommand( 194 self._chromeos_root, 195 cmd, print_to_console=True) 196 if ret: 197 raise Exception('ebuild gdbserver failed.') 198 199 cmd = ('scp -i {0} {1} ' 200 '/build/{2}/usr/bin/gdbserver root@{3}:/usr/local/bin/' 201 .format(self._tmp_testing_rsa, self._sshflag, 202 self._board, self._remote)) 203 ret = self._executer.ChrootRunCommand( 204 self._chromeos_root, 205 cmd, print_to_console=True) 206 if ret: 207 raise Exception('copy gdbserver failed.') 208 209 if self._mount_flag: 210 self.MountSource(unmount=False) 211 212 def Cleanup(self): 213 if not self._cleanup: 214 return 215 216 if self._cleanup == 'chroot' or self._cleanup == 'chromeos': 217 self._l.LogOutput('[Cleanup]: Deleting chroot inside \'{0}\''.format( 218 self._chromeos_root)) 219 command = 'cd %s; cros_sdk --delete' % self._chromeos_root 220 rv = self._executer.RunCommand(command) 221 if rv: 222 self._l.LogWarning('Warning - failed to delete chroot.') 223 # Delete .cache - crosbug.com/34956 224 command = 'sudo rm -fr %s' % os.path.join(self._chromeos_root, '.cache') 225 rv = self._executer.RunCommand(command) 226 if rv: 227 self._l.LogWarning('Warning - failed to delete \'.cache\'.') 228 229 if self._cleanup == 'chromeos': 230 self._l.LogOutput('[Cleanup]: Deleting chromeos tree \'{0}\' ...'.format( 231 self._chromeos_root)) 232 command = 'rm -fr {0}'.format(self._chromeos_root) 233 rv = self._executer.RunCommand(command) 234 if rv: 235 self._l.LogWarning('Warning - failed to remove chromeos tree.') 236 237 def MakeCheck(self): 238 cmd = ('ssh -i {0} {1} root@{2} "reboot && exit"' 239 .format(self._tmp_testing_rsa, self._sshflag, 240 self._remote)) 241 self._executer.ChrootRunCommand( 242 self._chromeos_root, cmd) 243 time.sleep(40) 244 245 cmd = ('ssh -i {0} {1} root@{2} ' 246 '"iptables -A INPUT -p tcp --dport 1234 -j ACCEPT"' 247 .format(self._tmp_testing_rsa, self._sshflag, 248 self._remote)) 249 self._executer.ChrootRunCommand( 250 self._chromeos_root, cmd) 251 252 cmd = ('cd %s ; ' 253 'DEJAGNU=%s make check' % 254 (path.join(self._gdb_source_dir, 'gdb'), 255 path.join(self._tmp, 'site.exp'))) 256 ret = self._executer.ChrootRunCommand( 257 self._chromeos_root, cmd) 258 if ret: 259 raise Exception('Make check failed.') 260 261 # This method ensures necessary mount points before executing chroot comamnd. 262 def MountSource(self, unmount=False): 263 script = os.path.join(self._base_dir, 'build_tc.py') 264 if unmount: 265 mount = '-u' 266 else: 267 mount = '-m' 268 cmd = ('python {0} --chromeos_root={1} ' 269 '--gdb_dir={2} --board={3} {4}' 270 .format(script, self._chromeos_root, 271 self._source_dir, self._board, 272 mount)) 273 rv = self._executer.RunCommand(cmd) 274 if rv: 275 raise Exception('Mount source failed.') 276 277 def ResultValidate(self): 278 self.PrepareResult() 279 result = 0 280 for key, value in self.base_result.items(): 281 if 'PASS' not in value: 282 continue 283 test_result = self.test_result[key] 284 if 'PASS' not in test_result: 285 result = 1 286 return result 287 288 def PrepareResult(self): 289 test_output = os.path.join(self._gdb_source_dir, 'gdb', 290 'testsuite', 'gdb.sum') 291 test_output = misc.GetOutsideChrootPath(self._chromeos_root, 292 test_output) 293 base_output = os.path.join(self._base_dir, 'gdb_baseline', self._target) 294 295 self.test_result = self.ParseResult(test_output) 296 self.base_result = self.ParseResult(base_output) 297 298 def ParseResult(self, gdb_sum): 299 result = {} 300 multi_keys = {} 301 with open(gdb_sum) as input_sum: 302 for line in input_sum: 303 line = line.strip() 304 r = line.split(':', 1) 305 if r[0] in _VALID_TEST_RESULTS: 306 key = r[1] 307 if r[1] in result: 308 if r[1] in multi_keys: 309 multi_keys[r[1]] += 1 310 else: 311 multi_keys[r[1]] = 2 312 key = r[1] + '_____{0}_____'.format(multi_keys[r[1]]) 313 result[key] = r[0] 314 return result 315 316 317def Main(argv): 318 opts = ProcessArguments(argv) 319 available_machine = TryAcquireMachine(opts.remote) 320 executer = DejagnuExecuter(misc.GetRoot(argv[0])[0], 321 opts.mount, opts.chromeos_root, 322 available_machine, 323 opts.board, 324 opts.cleanup) 325 # Return value is a 3- or 4-element tuple 326 # element#1 - exit code 327 # element#2 - stdout 328 # element#3 - stderr 329 # element#4 - exception infor 330 # Some other scripts need these detailed information. 331 ret = (1, '', '') 332 try: 333 executer.SetupTestingDir() 334 executer.PrepareTestingRsaKeys() 335 executer.PrepareTestFiles() 336 executer.PrepareGdb() 337 executer.PrepareGdbserver() 338 executer.MakeCheck() 339 print executer.ResultValidate() 340 # ret = executer.ValidateFailures() 341 except Exception as e: 342 # At least log the exception on console. 343 print e 344 # The #4 element encodes the runtime exception. 345 ret = (1, '', '', 'Exception happened during execution: \n' + str(e)) 346 finally: 347 executer.Cleanup() 348 return ret 349 350if __name__ == '__main__': 351 retval = Main(sys.argv)[0] 352 sys.exit(retval) 353