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