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