blob: 75316b2af6d57a2a8f160e5c851d9685045ebb80 [file] [log] [blame]
cmticeb70801a2014-12-11 14:29:34 -08001# Copyright 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Wrapper for running gdb.
6
7This handles the fun details like running against the right sysroot, via
8qemu, bind mounts, etc...
9"""
10
11from __future__ import print_function
12
13import argparse
14import contextlib
15import errno
16import os
17import sys
18import tempfile
19
20from chromite.lib import commandline
21from chromite.lib import cros_build_lib
cmticef23cb132015-04-10 15:13:00 -070022from chromite.lib import cros_logging as logging
cmticeb70801a2014-12-11 14:29:34 -080023from chromite.lib import namespaces
24from chromite.lib import osutils
cmticef23cb132015-04-10 15:13:00 -070025from chromite.lib import parallel
cmtice932e0aa2015-02-27 11:49:12 -080026from chromite.lib import qemu
cmticef23cb132015-04-10 15:13:00 -070027from chromite.lib import remote_access
cmticeb70801a2014-12-11 14:29:34 -080028from chromite.lib import retry_util
cmticef23cb132015-04-10 15:13:00 -070029from chromite.lib import timeout_util
30from chromite.lib import toolchain
cmticeb70801a2014-12-11 14:29:34 -080031
cmticef23cb132015-04-10 15:13:00 -070032class GdbException(Exception):
33 """Base exception for this module."""
34
35
36class GdbBadRemoteDeviceError(GdbException):
37 """Raised when remote device does not exist or is not responding."""
38
39
40class GdbMissingSysrootError(GdbException):
41 """Raised when path to sysroot cannot be found in chroot."""
42
43
44class GdbMissingInferiorError(GdbException):
45 """Raised when the binary to be debugged cannot be found."""
46
47
48class GdbMissingDebuggerError(GdbException):
49 """Raised when cannot find correct version of debugger."""
50
51
52class GdbCannotFindRemoteProcessError(GdbException):
53 """Raised when cannot find requested executing process on remote device."""
54
55
56class GdbUnableToStartGdbserverError(GdbException):
57 """Raised when error occurs trying to start gdbserver on remote device."""
58
59
60class GdbTooManyPidsError(GdbException):
61 """Raised when more than one matching pid is found running on device."""
62
63
64class GdbEarlyExitError(GdbException):
65 """Raised when user requests to exit early."""
66
67
68class GdbCannotDetectBoardError(GdbException):
69 """Raised when board isn't specified and can't be automatically determined."""
cmticeb70801a2014-12-11 14:29:34 -080070
cmtice932e0aa2015-02-27 11:49:12 -080071
cmticeb70801a2014-12-11 14:29:34 -080072class BoardSpecificGdb(object):
73 """Framework for running gdb."""
74
75 _BIND_MOUNT_PATHS = ('dev', 'dev/pts', 'proc', 'mnt/host/source', 'sys')
cmticef23cb132015-04-10 15:13:00 -070076 _GDB = '/usr/bin/gdb'
77 _EXTRA_SSH_SETTINGS = {'CheckHostIP': 'no',
78 'BatchMode': 'yes'}
79 _MISSING_DEBUG_INFO_MSG = """
80%(inf_cmd)s is stripped and %(debug_file)s does not exist on your local machine.
81 The debug symbols for that package may not be installed. To install the debug
82 symbols for %(package)s only, run:
cmticeb70801a2014-12-11 14:29:34 -080083
cmticef23cb132015-04-10 15:13:00 -070084 cros_install_debug_syms --board=%(board)s %(package)s
85
86To install the debug symbols for all available packages, run:
87
88 cros_install_debug_syms --board=%(board)s --all"""
89
90 def __init__(self, board, gdb_args, inf_cmd, inf_args, remote, pid,
91 remote_process_name, cgdb_flag, ping):
cmticeb70801a2014-12-11 14:29:34 -080092 self.board = board
cmticef23cb132015-04-10 15:13:00 -070093 self.sysroot = None
94 self.prompt = '(gdb) '
cmtice932e0aa2015-02-27 11:49:12 -080095 self.inf_cmd = inf_cmd
cmticef23cb132015-04-10 15:13:00 -070096 self.run_as_root = False
97 self.gdb_args = gdb_args
cmtice932e0aa2015-02-27 11:49:12 -080098 self.inf_args = inf_args
cmticef23cb132015-04-10 15:13:00 -070099 self.remote = remote.hostname if remote else None
100 self.pid = pid
101 self.remote_process_name = remote_process_name
102 # Port used for sending ssh commands to DUT.
103 self.remote_port = remote.port if remote else None
104 # Port for communicating between gdb & gdbserver.
105 self.gdbserver_port = remote_access.GetUnusedPort()
106 self.ssh_settings = remote_access.CompileSSHConnectSettings(
107 **self._EXTRA_SSH_SETTINGS)
108 self.cgdb = cgdb_flag
cmtice932e0aa2015-02-27 11:49:12 -0800109 self.framework = 'auto'
110 self.qemu = None
cmticef23cb132015-04-10 15:13:00 -0700111 self.device = None
112 self.cross_gdb = None
113 self.ping = ping
cmtice932e0aa2015-02-27 11:49:12 -0800114
cmticef23cb132015-04-10 15:13:00 -0700115 def VerifyAndFinishInitialization(self, device):
116 """Verify files/processes exist and flags are correct."""
117 if not self.board:
118 if self.remote:
119 self.board = cros_build_lib.GetBoard(device_board=device.board,
120 override_board=self.board)
121 else:
122 raise GdbCannotDetectBoardError('Cannot determine which board to use. '
123 'Please specify the with --board flag.')
124
125 self.sysroot = cros_build_lib.GetSysroot(board=self.board)
126 self.prompt = '(%s-gdb) ' % self.board
127 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
128 self.cross_gdb = self.GetCrossGdb()
129
130 if self.remote:
131
132 # If given remote process name, find pid & inf_cmd on remote device.
133 if self.remote_process_name or self.pid:
134 self._FindRemoteProcess(device)
135
136 # Verify that sysroot is valid (exists).
137 if not os.path.isdir(self.sysroot):
138 raise GdbMissingSysrootError('Sysroot does not exist: %s' %
139 self.sysroot)
140
141 self.device = device
142 sysroot_inf_cmd = ''
143 if self.inf_cmd:
144 sysroot_inf_cmd = os.path.join(self.sysroot,
145 self.inf_cmd.lstrip('/'))
146
147 # Verify that inf_cmd, if given, exists.
148 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
149 raise GdbMissingInferiorError('Cannot find file %s (in sysroot).' %
150 sysroot_inf_cmd)
151
152 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
153 # exists. If not, tell user and give them the option of quitting & getting
154 # the debug info.
155 if sysroot_inf_cmd:
156 stripped_info = cros_build_lib.RunCommand(['file', sysroot_inf_cmd],
157 capture_output=True).output
158 if not ' not stripped' in stripped_info:
159 debug_file = os.path.join(self.sysroot, 'usr/lib/debug',
160 self.inf_cmd.lstrip('/'))
161 debug_file += '.debug'
162 if not os.path.exists(debug_file):
163 equery = 'equery-%s' % self.board
164 package = cros_build_lib.RunCommand([equery, '-q', 'b',
165 self.inf_cmd],
166 capture_output=True).output
167 logging.info(self._MISSING_DEBUG_INFO_MSG % {
168 'board': self.board,
169 'inf_cmd': self.inf_cmd,
170 'package': package,
171 'debug_file': debug_file})
172 answer = cros_build_lib.BooleanPrompt()
173 if not answer:
174 raise GdbEarlyExitError('Exiting early, at user request.')
175
176 # Set up qemu, if appropriate.
177 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800178 if qemu_arch is None:
179 self.framework = 'ldso'
180 else:
181 self.framework = 'qemu'
182 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800183
cmticef23cb132015-04-10 15:13:00 -0700184 if self.remote:
185 # Verify cgdb flag info.
186 if self.cgdb:
187 if osutils.Which('cgdb') is None:
188 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
189 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800190
cmticef23cb132015-04-10 15:13:00 -0700191 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800192 """Returns the given path with any sysroot prefix removed."""
193 # If the sysroot is /, then the paths are already normalized.
194 if self.sysroot != '/' and path.startswith(self.sysroot):
195 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800196 return path
197
198 @staticmethod
199 def GetNonRootAccount():
200 """Return details about the non-root account we want to use.
201
202 Returns:
203 A tuple of (username, uid, gid, home).
204 """
205 return (
206 os.environ.get('SUDO_USER', 'nobody'),
207 int(os.environ.get('SUDO_UID', '65534')),
208 int(os.environ.get('SUDO_GID', '65534')),
209 # Should we find a better home?
210 '/tmp/portage',
211 )
212
213 @staticmethod
214 @contextlib.contextmanager
215 def LockDb(db):
216 """Lock an account database.
217
218 We use the same algorithm as shadow/user.eclass. This way we don't race
219 and corrupt things in parallel.
220 """
221 lock = '%s.lock' % db
222 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
223
224 # First try forever to grab the lock.
225 retry = lambda e: e.errno == errno.EEXIST
226 # Retry quickly at first, but slow down over time.
227 try:
228 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700229 except Exception as e:
230 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800231
232 # Yield while holding the lock, but try to clean it no matter what.
233 try:
234 os.unlink(tmplock)
235 yield lock
236 finally:
237 os.unlink(lock)
238
239 def SetupUser(self):
240 """Propogate the user name<->id mapping from outside the chroot.
241
242 Some unittests use getpwnam($USER), as does bash. If the account
243 is not registered in the sysroot, they get back errors.
244 """
245 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
246 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
247 SDK_GECOS = 'ChromeOS Developer'
248
249 user, uid, gid, home = self.GetNonRootAccount()
250 if user == 'nobody':
251 return
252
253 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
254 with self.LockDb(passwd_db):
255 data = osutils.ReadFile(passwd_db)
256 accts = data.splitlines()
257 for acct in accts:
258 passwd = acct.split(':')
259 if passwd[0] == user:
260 # Did the sdk make this account?
261 if passwd[4] == SDK_GECOS:
262 # Don't modify it (see below) since we didn't create it.
263 return
264
265 # Did we make this account?
266 if passwd[4] != MAGIC_GECOS:
267 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
268 (passwd_db, user))
269
270 # Maybe we should see if it needs to be updated? Like if they
271 # changed UIDs? But we don't really check that elsewhere ...
272 return
273
274 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
275 'name': user,
276 'uid': uid,
277 'gid': gid,
278 'gecos': MAGIC_GECOS,
279 'homedir': home,
280 'shell': '/bin/bash',
281 }
282 with open(passwd_db, 'a') as f:
283 if data[-1] != '\n':
284 f.write('\n')
285 f.write('%s\n' % acct)
286
cmticef23cb132015-04-10 15:13:00 -0700287 def _FindRemoteProcess(self, device):
288 """Find a named process (or a pid) running on a remote device."""
289 if not self.remote_process_name and not self.pid:
290 return
cmticeb70801a2014-12-11 14:29:34 -0800291
cmticef23cb132015-04-10 15:13:00 -0700292 if self.remote_process_name:
293 # Look for a process with the specified name on the remote device; if
294 # found, get its pid.
295 pname = self.remote_process_name
296 if pname == 'browser':
297 all_chrome_pids = set(device.GetRunningPids(
298 '/opt/google/chrome/chrome'))
299 sandbox_pids = set(device.GetRunningPids(
300 '/opt/google/chrome/chrome-sandbox'))
301 non_main_chrome_pids = set(device.GetRunningPids('type='))
302 pids = list(all_chrome_pids - sandbox_pids - non_main_chrome_pids)
303 elif pname == 'renderer' or pname == 'gpu-process':
304 pids = device.GetRunningPids('type=%s'% pname)
305 else:
306 pids = device.GetRunningPids(pname)
307
308 if pids:
309 if len(pids) == 1:
310 self.pid = pids[0]
311 else:
312 raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
313 'You must specify the correct pid.'
314 % (pname, repr(pids)))
315 else:
316 raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
317 (pname, self.remote))
318
319 # Find full path for process, from pid (and verify pid).
320 command = [
321 'readlink',
322 '-e', '/proc/%s/exe' % self.pid,
323 ]
324 try:
325 res = device.RunCommand(command, capture_output=True)
326 if res.returncode == 0:
327 self.inf_cmd = res.output.rstrip('\n')
328 except cros_build_lib.RunCommandError:
329 raise GdbCannotFindRemoteProcessError('Unable to find name of process '
330 'with pid %s on %s' %
331 (self.pid, self.remote))
332
333 def GetCrossGdb(self):
334 """Find the appropriate cross-version of gdb for the board."""
335 toolchains = toolchain.GetToolchainsForBoard(self.board)
336 tc = toolchain.FilterToolchains(toolchains, 'default', True).keys()
337 cross_gdb = tc[0] + '-gdb'
338 if not osutils.Which(cross_gdb):
339 raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
340 'setup_board?' % cross_gdb)
341 return cross_gdb
342
343 def StartGdbserver(self, inf_cmd, device):
344 """Set up and start gdbserver running on remote."""
345
346 # Generate appropriate gdbserver command.
347 command = ['gdbserver']
348 if self.pid:
349 # Attach to an existing process.
350 command += [
351 '--attach',
352 'localhost:%s' % self.gdbserver_port,
353 '%s' % self.pid,
354 ]
355 elif inf_cmd:
356 # Start executing a new process.
357 command += ['localhost:%s' % self.gdbserver_port, inf_cmd] + self.inf_args
358
359 self.ssh_settings.append('-n')
360 self.ssh_settings.append('-L%s:localhost:%s' %
361 (self.gdbserver_port, self.gdbserver_port))
362 return device.RunCommand(command,
363 connect_settings=self.ssh_settings,
364 input=open('/dev/null')).returncode
365
366 def GetGdbInitCommands(self, inferior_cmd):
367 """Generate list of commands with which to initialize the gdb session."""
368 gdb_init_commands = []
369
370 if self.remote:
371 sysroot_var = self.sysroot
372 else:
373 sysroot_var = '/'
374
375 gdb_init_commands = [
376 'set sysroot %s' % sysroot_var,
377 'set solib-absolute-prefix %s' % sysroot_var,
378 'set solib-search-path %s' % sysroot_var,
379 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
380 'set prompt %s' % self.prompt,
381 ]
382
383 if self.remote:
384 if inferior_cmd and not inferior_cmd.startswith(self.sysroot):
385 inferior_cmd = os.path.join(self.sysroot, inferior_cmd.lstrip('/'))
386
387 if inferior_cmd:
388 gdb_init_commands.append('file %s' % inferior_cmd)
389 gdb_init_commands.append('target remote localhost:%s' %
390 self.gdbserver_port)
391 else:
392 if inferior_cmd:
393 gdb_init_commands.append('file %s ' % inferior_cmd)
394 gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
395
396 return gdb_init_commands
397
398 def RunRemote(self):
399 """Handle remote debugging, via gdbserver & cross debugger."""
400 device = None
401 try:
402 device = remote_access.ChromiumOSDeviceHandler(
403 self.remote,
404 port=self.remote_port,
405 connect_settings=self.ssh_settings,
406 ping=self.ping).device
407 except remote_access.DeviceNotPingableError:
408 raise GdbBadRemoteDeviceError('Remote device %s is not responding to '
409 'ping.' % self.remote)
410
411 self.VerifyAndFinishInitialization(device)
412 gdb_cmd = self.cross_gdb
413
414 gdb_commands = self.GetGdbInitCommands(self.inf_cmd)
415 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
416 for x in gdb_commands]
417 if self.cgdb:
418 gdb_args = ['cgdb'] + gdb_args
419
420 with parallel.BackgroundTaskRunner(self.StartGdbserver,
421 self.inf_cmd,
422 device) as task:
423 task.put([])
424 # Verify that gdbserver finished launching.
425 try:
426 timeout_util.WaitForSuccess(
427 lambda x: len(x) == 0, self.device.GetRunningPids,
428 4, func_args=('gdbserver',))
429 except timeout_util.TimeoutError:
430 raise GdbUnableToStartGdbserverError('gdbserver did not start on'
431 ' remote device.')
432 cros_build_lib.RunCommand(gdb_args)
433
434 def Run(self):
435 """Runs the debugger in a proper environment (e.g. qemu)."""
436
437 self.VerifyAndFinishInitialization(None)
438 self.SetupUser()
cmtice932e0aa2015-02-27 11:49:12 -0800439 if self.framework == 'qemu':
440 self.qemu.Install(self.sysroot)
441 self.qemu.RegisterBinfmt()
442
cmticeb70801a2014-12-11 14:29:34 -0800443 for mount in self._BIND_MOUNT_PATHS:
444 path = os.path.join(self.sysroot, mount)
445 osutils.SafeMakedirs(path)
446 osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
447
cmticef23cb132015-04-10 15:13:00 -0700448 gdb_cmd = self._GDB
449 inferior_cmd = self.inf_cmd
450
cmtice932e0aa2015-02-27 11:49:12 -0800451 gdb_argv = self.gdb_args[:]
452 if gdb_argv:
cmticef23cb132015-04-10 15:13:00 -0700453 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
cmticeb70801a2014-12-11 14:29:34 -0800454 # Some programs expect to find data files via $CWD, so doing a chroot
455 # and dropping them into / would make them fail.
cmticef23cb132015-04-10 15:13:00 -0700456 cwd = self.RemoveSysrootPrefix(os.getcwd())
cmticeb70801a2014-12-11 14:29:34 -0800457
cmticeb70801a2014-12-11 14:29:34 -0800458 os.chroot(self.sysroot)
459 os.chdir(cwd)
460 # The TERM the user is leveraging might not exist in the sysroot.
461 # Force a sane default that supports standard color sequences.
462 os.environ['TERM'] = 'ansi'
463 # Some progs want this like bash else they get super confused.
464 os.environ['PWD'] = cwd
465 if not self.run_as_root:
466 _, uid, gid, home = self.GetNonRootAccount()
467 os.setgid(gid)
468 os.setuid(uid)
469 os.environ['HOME'] = home
470
cmticef23cb132015-04-10 15:13:00 -0700471 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
cmticeb70801a2014-12-11 14:29:34 -0800472
cmticef23cb132015-04-10 15:13:00 -0700473 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
474 for x in gdb_commands]
cmtice932e0aa2015-02-27 11:49:12 -0800475 gdb_args += self.gdb_args
476
cmtice932e0aa2015-02-27 11:49:12 -0800477 sys.exit(os.execvp(gdb_cmd, gdb_args))
cmticeb70801a2014-12-11 14:29:34 -0800478
479
cmtice932e0aa2015-02-27 11:49:12 -0800480def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
cmticeb70801a2014-12-11 14:29:34 -0800481 """Re-execute gdb as root.
482
483 We often need to do things as root, so make sure we're that. Like chroot
484 for proper library environment or do bind mounts.
485
486 Also unshare the mount namespace so as to ensure that doing bind mounts for
487 tests don't leak out to the normal chroot. Also unshare the UTS namespace
488 so changes to `hostname` do not impact the host.
489 """
490 if os.geteuid() != 0:
491 cmd = ['sudo', '-E', '--'] + argv
492 os.execvp(cmd[0], cmd)
493 else:
cmtice932e0aa2015-02-27 11:49:12 -0800494 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
495
496
cmticef23cb132015-04-10 15:13:00 -0700497def FindInferior(arg_list):
cmtice932e0aa2015-02-27 11:49:12 -0800498 """Look for the name of the inferior (to be debugged) in arg list."""
499
500 program_name = ''
501 new_list = []
502 for item in arg_list:
503 if item[0] == '-':
504 new_list.append(item)
505 elif not program_name:
506 program_name = item
507 else:
508 raise RuntimeError('Found multiple program names: %s %s'
509 % (program_name, item))
510
511 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800512
513
514def main(argv):
515
516 parser = commandline.ArgumentParser(description=__doc__)
517
cmticef23cb132015-04-10 15:13:00 -0700518 parser.add_argument('--board', default=None,
cmticeb70801a2014-12-11 14:29:34 -0800519 help='board to debug for')
cmticef23cb132015-04-10 15:13:00 -0700520 parser.add_argument('-g', '--gdb_args', action='append', default=[],
521 help='Arguments to gdb itself. If multiple arguments are'
522 ' passed, each argument needs a separate \'-g\' flag.')
523 parser.add_argument(
524 '--remote', default=None,
525 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
526 help='Remote device on which to run the binary. Use'
527 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
528 ' already running local virtual machine.')
529 parser.add_argument('--pid', default='',
530 help='Process ID of the (already) running process on the'
531 ' remote device to which to attach.')
532 parser.add_argument('--remote_pid', dest='pid', default='',
533 help='Deprecated alias for --pid.')
534 parser.add_argument('--no-ping', dest='ping', default=True,
535 action='store_false',
536 help='Do not ping remote before attempting to connect.')
537 parser.add_argument('--attach', dest='attach_name', default='',
538 help='Name of existing process to which to attach, on'
539 ' remote device (remote debugging only). "--attach'
540 ' browser" will find the main chrome browser process;'
541 ' "--attach renderer" will find a chrome renderer'
542 ' process; "--attach gpu-process" will find the chrome'
543 ' gpu process.')
544 parser.add_argument('--cgdb', default=False,
545 action='store_true',
546 help='Use cgdb curses interface rather than plain gdb.'
547 'This option is only valid for remote debugging.')
548 parser.add_argument('inf_args', nargs=argparse.REMAINDER,
549 help='Arguments for gdb to pass to the program being'
550 ' debugged. These are positional and must come at the end'
551 ' of the command line. This will not work if attaching'
552 ' to an already running program.')
cmticeb70801a2014-12-11 14:29:34 -0800553
554 options = parser.parse_args(argv)
555 options.Freeze()
556
cmtice932e0aa2015-02-27 11:49:12 -0800557 gdb_args = []
558 inf_args = []
559 inf_cmd = ''
560
cmticef23cb132015-04-10 15:13:00 -0700561 if options.inf_args:
562 inf_cmd = options.inf_args[0]
563 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800564
cmticef23cb132015-04-10 15:13:00 -0700565 if options.gdb_args:
566 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800567
568 if inf_cmd:
569 fname = os.path.join(cros_build_lib.GetSysroot(options.board),
570 inf_cmd.lstrip('/'))
571 if not os.path.exists(fname):
572 cros_build_lib.Die('Cannot find program %s.' % fname)
cmticef23cb132015-04-10 15:13:00 -0700573 else:
574 if inf_args:
575 parser.error('Cannot specify arguments without a program.')
cmtice932e0aa2015-02-27 11:49:12 -0800576
cmticef23cb132015-04-10 15:13:00 -0700577 if inf_args and (options.pid or options.attach_name):
578 parser.error('Cannot pass arguments to an already'
579 ' running process (--remote-pid or --attach).')
580
581 if options.remote:
582 if not options.pid and not inf_cmd and not options.attach_name:
583 parser.error('Must specify a program to start or a pid to attach '
584 'to on the remote device.')
585 if options.attach_name and options.attach_name == 'browser':
586 inf_cmd = '/opt/google/chrome/chrome'
587 else:
588 if options.cgdb:
589 parser.error('--cgdb option can only be used with remote debugging.')
590 if options.pid:
591 parser.error('Must specify a remote device (--remote) if you want '
592 'to attach to a remote pid.')
593 if options.attach_name:
594 parser.error('Must specify remote device (--remote) when using'
595 ' --attach option.')
cmtice932e0aa2015-02-27 11:49:12 -0800596
cmticeb70801a2014-12-11 14:29:34 -0800597 # Once we've finished sanity checking args, make sure we're root.
cmticef23cb132015-04-10 15:13:00 -0700598 if not options.remote:
599 _ReExecuteIfNeeded([sys.argv[0]] + argv)
cmticeb70801a2014-12-11 14:29:34 -0800600
cmticef23cb132015-04-10 15:13:00 -0700601 gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
602 options.remote, options.pid, options.attach_name,
603 options.cgdb, options.ping)
cmticeb70801a2014-12-11 14:29:34 -0800604
cmticef23cb132015-04-10 15:13:00 -0700605 try:
606 if options.remote:
607 gdb.RunRemote()
608 else:
609 gdb.Run()
610
611 except GdbException as e:
612 if options.debug:
613 raise
614 else:
615 raise cros_build_lib.Die(str(e))