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