blob: 6ce98d80ebf433b969b3a654eea4693ae5f8eab8 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
cmticeb70801a2014-12-11 14:29:34 -08002# Copyright 2014 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Wrapper for running gdb.
7
8This handles the fun details like running against the right sysroot, via
9qemu, bind mounts, etc...
10"""
11
12from __future__ import print_function
13
14import argparse
15import contextlib
16import errno
17import os
Raul E Rangel746c45d2018-05-09 09:27:31 -060018import pipes
cmticeb70801a2014-12-11 14:29:34 -080019import sys
20import tempfile
21
22from chromite.lib import commandline
23from chromite.lib import cros_build_lib
cmticef23cb132015-04-10 15:13:00 -070024from chromite.lib import cros_logging as logging
cmticeb70801a2014-12-11 14:29:34 -080025from chromite.lib import namespaces
26from chromite.lib import osutils
cmtice932e0aa2015-02-27 11:49:12 -080027from chromite.lib import qemu
cmticef23cb132015-04-10 15:13:00 -070028from chromite.lib import remote_access
cmticeb70801a2014-12-11 14:29:34 -080029from chromite.lib import retry_util
cmticef23cb132015-04-10 15:13:00 -070030from 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'
Raul E Rangel746c45d2018-05-09 09:27:31 -060077 _EXTRA_SSH_SETTINGS = {
78 'CheckHostIP': 'no',
79 'BatchMode': 'yes',
80 'LogLevel': 'QUIET'
81 }
cmticef23cb132015-04-10 15:13:00 -070082 _MISSING_DEBUG_INFO_MSG = """
83%(inf_cmd)s is stripped and %(debug_file)s does not exist on your local machine.
84 The debug symbols for that package may not be installed. To install the debug
85 symbols for %(package)s only, run:
cmticeb70801a2014-12-11 14:29:34 -080086
cmticef23cb132015-04-10 15:13:00 -070087 cros_install_debug_syms --board=%(board)s %(package)s
88
89To install the debug symbols for all available packages, run:
90
91 cros_install_debug_syms --board=%(board)s --all"""
92
93 def __init__(self, board, gdb_args, inf_cmd, inf_args, remote, pid,
94 remote_process_name, cgdb_flag, ping):
cmticeb70801a2014-12-11 14:29:34 -080095 self.board = board
cmticef23cb132015-04-10 15:13:00 -070096 self.sysroot = None
97 self.prompt = '(gdb) '
cmtice932e0aa2015-02-27 11:49:12 -080098 self.inf_cmd = inf_cmd
cmticef23cb132015-04-10 15:13:00 -070099 self.run_as_root = False
100 self.gdb_args = gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800101 self.inf_args = inf_args
cmticef23cb132015-04-10 15:13:00 -0700102 self.remote = remote.hostname if remote else None
103 self.pid = pid
104 self.remote_process_name = remote_process_name
105 # Port used for sending ssh commands to DUT.
106 self.remote_port = remote.port if remote else None
107 # Port for communicating between gdb & gdbserver.
108 self.gdbserver_port = remote_access.GetUnusedPort()
109 self.ssh_settings = remote_access.CompileSSHConnectSettings(
110 **self._EXTRA_SSH_SETTINGS)
111 self.cgdb = cgdb_flag
cmtice932e0aa2015-02-27 11:49:12 -0800112 self.framework = 'auto'
113 self.qemu = None
cmticef23cb132015-04-10 15:13:00 -0700114 self.device = None
115 self.cross_gdb = None
116 self.ping = ping
cmtice932e0aa2015-02-27 11:49:12 -0800117
cmticef23cb132015-04-10 15:13:00 -0700118 def VerifyAndFinishInitialization(self, device):
119 """Verify files/processes exist and flags are correct."""
120 if not self.board:
121 if self.remote:
122 self.board = cros_build_lib.GetBoard(device_board=device.board,
123 override_board=self.board)
124 else:
125 raise GdbCannotDetectBoardError('Cannot determine which board to use. '
126 'Please specify the with --board flag.')
127
128 self.sysroot = cros_build_lib.GetSysroot(board=self.board)
129 self.prompt = '(%s-gdb) ' % self.board
130 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
131 self.cross_gdb = self.GetCrossGdb()
132
133 if self.remote:
134
135 # If given remote process name, find pid & inf_cmd on remote device.
136 if self.remote_process_name or self.pid:
137 self._FindRemoteProcess(device)
138
139 # Verify that sysroot is valid (exists).
140 if not os.path.isdir(self.sysroot):
141 raise GdbMissingSysrootError('Sysroot does not exist: %s' %
142 self.sysroot)
143
144 self.device = device
145 sysroot_inf_cmd = ''
146 if self.inf_cmd:
147 sysroot_inf_cmd = os.path.join(self.sysroot,
148 self.inf_cmd.lstrip('/'))
149
150 # Verify that inf_cmd, if given, exists.
151 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
152 raise GdbMissingInferiorError('Cannot find file %s (in sysroot).' %
153 sysroot_inf_cmd)
154
155 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
156 # exists. If not, tell user and give them the option of quitting & getting
157 # the debug info.
158 if sysroot_inf_cmd:
159 stripped_info = cros_build_lib.RunCommand(['file', sysroot_inf_cmd],
160 capture_output=True).output
161 if not ' not stripped' in stripped_info:
162 debug_file = os.path.join(self.sysroot, 'usr/lib/debug',
163 self.inf_cmd.lstrip('/'))
164 debug_file += '.debug'
165 if not os.path.exists(debug_file):
166 equery = 'equery-%s' % self.board
167 package = cros_build_lib.RunCommand([equery, '-q', 'b',
168 self.inf_cmd],
169 capture_output=True).output
170 logging.info(self._MISSING_DEBUG_INFO_MSG % {
171 'board': self.board,
172 'inf_cmd': self.inf_cmd,
173 'package': package,
174 'debug_file': debug_file})
175 answer = cros_build_lib.BooleanPrompt()
176 if not answer:
177 raise GdbEarlyExitError('Exiting early, at user request.')
178
179 # Set up qemu, if appropriate.
180 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800181 if qemu_arch is None:
182 self.framework = 'ldso'
183 else:
184 self.framework = 'qemu'
185 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800186
cmticef23cb132015-04-10 15:13:00 -0700187 if self.remote:
188 # Verify cgdb flag info.
189 if self.cgdb:
190 if osutils.Which('cgdb') is None:
191 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
192 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800193
cmticef23cb132015-04-10 15:13:00 -0700194 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800195 """Returns the given path with any sysroot prefix removed."""
196 # If the sysroot is /, then the paths are already normalized.
197 if self.sysroot != '/' and path.startswith(self.sysroot):
198 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800199 return path
200
201 @staticmethod
202 def GetNonRootAccount():
203 """Return details about the non-root account we want to use.
204
205 Returns:
206 A tuple of (username, uid, gid, home).
207 """
208 return (
209 os.environ.get('SUDO_USER', 'nobody'),
210 int(os.environ.get('SUDO_UID', '65534')),
211 int(os.environ.get('SUDO_GID', '65534')),
212 # Should we find a better home?
213 '/tmp/portage',
214 )
215
216 @staticmethod
217 @contextlib.contextmanager
218 def LockDb(db):
219 """Lock an account database.
220
221 We use the same algorithm as shadow/user.eclass. This way we don't race
222 and corrupt things in parallel.
223 """
224 lock = '%s.lock' % db
225 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
226
227 # First try forever to grab the lock.
228 retry = lambda e: e.errno == errno.EEXIST
229 # Retry quickly at first, but slow down over time.
230 try:
231 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700232 except Exception as e:
233 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800234
235 # Yield while holding the lock, but try to clean it no matter what.
236 try:
237 os.unlink(tmplock)
238 yield lock
239 finally:
240 os.unlink(lock)
241
242 def SetupUser(self):
243 """Propogate the user name<->id mapping from outside the chroot.
244
245 Some unittests use getpwnam($USER), as does bash. If the account
246 is not registered in the sysroot, they get back errors.
247 """
248 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
249 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
250 SDK_GECOS = 'ChromeOS Developer'
251
252 user, uid, gid, home = self.GetNonRootAccount()
253 if user == 'nobody':
254 return
255
256 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
257 with self.LockDb(passwd_db):
258 data = osutils.ReadFile(passwd_db)
259 accts = data.splitlines()
260 for acct in accts:
261 passwd = acct.split(':')
262 if passwd[0] == user:
263 # Did the sdk make this account?
264 if passwd[4] == SDK_GECOS:
265 # Don't modify it (see below) since we didn't create it.
266 return
267
268 # Did we make this account?
269 if passwd[4] != MAGIC_GECOS:
270 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
271 (passwd_db, user))
272
273 # Maybe we should see if it needs to be updated? Like if they
274 # changed UIDs? But we don't really check that elsewhere ...
275 return
276
277 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
278 'name': user,
279 'uid': uid,
280 'gid': gid,
281 'gecos': MAGIC_GECOS,
282 'homedir': home,
283 'shell': '/bin/bash',
284 }
285 with open(passwd_db, 'a') as f:
286 if data[-1] != '\n':
287 f.write('\n')
288 f.write('%s\n' % acct)
289
cmticef23cb132015-04-10 15:13:00 -0700290 def _FindRemoteProcess(self, device):
291 """Find a named process (or a pid) running on a remote device."""
292 if not self.remote_process_name and not self.pid:
293 return
cmticeb70801a2014-12-11 14:29:34 -0800294
cmticef23cb132015-04-10 15:13:00 -0700295 if self.remote_process_name:
296 # Look for a process with the specified name on the remote device; if
297 # found, get its pid.
298 pname = self.remote_process_name
299 if pname == 'browser':
300 all_chrome_pids = set(device.GetRunningPids(
301 '/opt/google/chrome/chrome'))
302 sandbox_pids = set(device.GetRunningPids(
303 '/opt/google/chrome/chrome-sandbox'))
304 non_main_chrome_pids = set(device.GetRunningPids('type='))
305 pids = list(all_chrome_pids - sandbox_pids - non_main_chrome_pids)
306 elif pname == 'renderer' or pname == 'gpu-process':
307 pids = device.GetRunningPids('type=%s'% pname)
308 else:
309 pids = device.GetRunningPids(pname)
310
311 if pids:
312 if len(pids) == 1:
313 self.pid = pids[0]
314 else:
315 raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
316 'You must specify the correct pid.'
317 % (pname, repr(pids)))
318 else:
319 raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
320 (pname, self.remote))
321
322 # Find full path for process, from pid (and verify pid).
323 command = [
324 'readlink',
325 '-e', '/proc/%s/exe' % self.pid,
326 ]
327 try:
328 res = device.RunCommand(command, capture_output=True)
329 if res.returncode == 0:
330 self.inf_cmd = res.output.rstrip('\n')
331 except cros_build_lib.RunCommandError:
332 raise GdbCannotFindRemoteProcessError('Unable to find name of process '
333 'with pid %s on %s' %
334 (self.pid, self.remote))
335
336 def GetCrossGdb(self):
337 """Find the appropriate cross-version of gdb for the board."""
338 toolchains = toolchain.GetToolchainsForBoard(self.board)
339 tc = toolchain.FilterToolchains(toolchains, 'default', True).keys()
340 cross_gdb = tc[0] + '-gdb'
341 if not osutils.Which(cross_gdb):
342 raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
343 'setup_board?' % cross_gdb)
344 return cross_gdb
345
Raul E Rangel746c45d2018-05-09 09:27:31 -0600346 def GetGdbInitCommands(self, inferior_cmd, device=None):
cmticef23cb132015-04-10 15:13:00 -0700347 """Generate list of commands with which to initialize the gdb session."""
348 gdb_init_commands = []
349
350 if self.remote:
351 sysroot_var = self.sysroot
352 else:
353 sysroot_var = '/'
354
355 gdb_init_commands = [
356 'set sysroot %s' % sysroot_var,
357 'set solib-absolute-prefix %s' % sysroot_var,
358 'set solib-search-path %s' % sysroot_var,
359 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
360 'set prompt %s' % self.prompt,
361 ]
362
Raul E Rangel746c45d2018-05-09 09:27:31 -0600363 if device:
364 ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
365
366 ssh_cmd.extend(['--', 'gdbserver'])
367
368 if self.pid:
369 ssh_cmd.extend(['--attach', 'stdio', str(self.pid)])
370 target_type = 'remote'
371 elif inferior_cmd:
372 ssh_cmd.extend(['-', inferior_cmd])
373 ssh_cmd.extend(self.inf_args)
374 target_type = 'remote'
375 else:
376 ssh_cmd.extend(['--multi', 'stdio'])
377 target_type = 'extended-remote'
378
379 ssh_cmd = ' '.join(map(pipes.quote, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700380
381 if inferior_cmd:
Raul E Rangel746c45d2018-05-09 09:27:31 -0600382 gdb_init_commands.append(
383 'file %s' % os.path.join(sysroot_var, inferior_cmd.lstrip(os.sep)))
384
385 gdb_init_commands.append('target %s | %s' % (target_type, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700386 else:
387 if inferior_cmd:
388 gdb_init_commands.append('file %s ' % inferior_cmd)
389 gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
390
391 return gdb_init_commands
392
393 def RunRemote(self):
394 """Handle remote debugging, via gdbserver & cross debugger."""
395 device = None
396 try:
397 device = remote_access.ChromiumOSDeviceHandler(
398 self.remote,
399 port=self.remote_port,
400 connect_settings=self.ssh_settings,
401 ping=self.ping).device
402 except remote_access.DeviceNotPingableError:
403 raise GdbBadRemoteDeviceError('Remote device %s is not responding to '
404 'ping.' % self.remote)
405
406 self.VerifyAndFinishInitialization(device)
407 gdb_cmd = self.cross_gdb
408
Raul E Rangel746c45d2018-05-09 09:27:31 -0600409 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
410 gdb_args = ['--quiet'] + ['--eval-command=%s' % x for x in gdb_commands]
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600411 gdb_args += self.gdb_args
412
cmticef23cb132015-04-10 15:13:00 -0700413 if self.cgdb:
Raul E Rangel746c45d2018-05-09 09:27:31 -0600414 gdb_args = ['-d', gdb_cmd, '--'] + gdb_args
415 gdb_cmd = 'cgdb'
cmticef23cb132015-04-10 15:13:00 -0700416
Raul E Rangel746c45d2018-05-09 09:27:31 -0600417 logging.debug('Running: %s', [gdb_cmd] + gdb_args)
418
419 os.chdir(self.sysroot)
420 sys.exit(os.execvp(gdb_cmd, gdb_args))
cmticef23cb132015-04-10 15:13:00 -0700421
422 def Run(self):
423 """Runs the debugger in a proper environment (e.g. qemu)."""
424
425 self.VerifyAndFinishInitialization(None)
426 self.SetupUser()
cmtice932e0aa2015-02-27 11:49:12 -0800427 if self.framework == 'qemu':
428 self.qemu.Install(self.sysroot)
429 self.qemu.RegisterBinfmt()
430
cmticeb70801a2014-12-11 14:29:34 -0800431 for mount in self._BIND_MOUNT_PATHS:
432 path = os.path.join(self.sysroot, mount)
433 osutils.SafeMakedirs(path)
434 osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
435
cmticef23cb132015-04-10 15:13:00 -0700436 gdb_cmd = self._GDB
437 inferior_cmd = self.inf_cmd
438
cmtice932e0aa2015-02-27 11:49:12 -0800439 gdb_argv = self.gdb_args[:]
440 if gdb_argv:
cmticef23cb132015-04-10 15:13:00 -0700441 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
cmticeb70801a2014-12-11 14:29:34 -0800442 # Some programs expect to find data files via $CWD, so doing a chroot
443 # and dropping them into / would make them fail.
cmticef23cb132015-04-10 15:13:00 -0700444 cwd = self.RemoveSysrootPrefix(os.getcwd())
cmticeb70801a2014-12-11 14:29:34 -0800445
cmticeb70801a2014-12-11 14:29:34 -0800446 os.chroot(self.sysroot)
447 os.chdir(cwd)
448 # The TERM the user is leveraging might not exist in the sysroot.
449 # Force a sane default that supports standard color sequences.
450 os.environ['TERM'] = 'ansi'
451 # Some progs want this like bash else they get super confused.
452 os.environ['PWD'] = cwd
453 if not self.run_as_root:
454 _, uid, gid, home = self.GetNonRootAccount()
455 os.setgid(gid)
456 os.setuid(uid)
457 os.environ['HOME'] = home
458
cmticef23cb132015-04-10 15:13:00 -0700459 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
cmticeb70801a2014-12-11 14:29:34 -0800460
cmticef23cb132015-04-10 15:13:00 -0700461 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
462 for x in gdb_commands]
cmtice932e0aa2015-02-27 11:49:12 -0800463 gdb_args += self.gdb_args
464
cmtice932e0aa2015-02-27 11:49:12 -0800465 sys.exit(os.execvp(gdb_cmd, gdb_args))
cmticeb70801a2014-12-11 14:29:34 -0800466
467
cmtice932e0aa2015-02-27 11:49:12 -0800468def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
cmticeb70801a2014-12-11 14:29:34 -0800469 """Re-execute gdb as root.
470
471 We often need to do things as root, so make sure we're that. Like chroot
472 for proper library environment or do bind mounts.
473
474 Also unshare the mount namespace so as to ensure that doing bind mounts for
475 tests don't leak out to the normal chroot. Also unshare the UTS namespace
476 so changes to `hostname` do not impact the host.
477 """
478 if os.geteuid() != 0:
479 cmd = ['sudo', '-E', '--'] + argv
480 os.execvp(cmd[0], cmd)
481 else:
cmtice932e0aa2015-02-27 11:49:12 -0800482 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
483
484
cmticef23cb132015-04-10 15:13:00 -0700485def FindInferior(arg_list):
cmtice932e0aa2015-02-27 11:49:12 -0800486 """Look for the name of the inferior (to be debugged) in arg list."""
487
488 program_name = ''
489 new_list = []
490 for item in arg_list:
491 if item[0] == '-':
492 new_list.append(item)
493 elif not program_name:
494 program_name = item
495 else:
496 raise RuntimeError('Found multiple program names: %s %s'
497 % (program_name, item))
498
499 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800500
501
502def main(argv):
503
504 parser = commandline.ArgumentParser(description=__doc__)
505
cmticef23cb132015-04-10 15:13:00 -0700506 parser.add_argument('--board', default=None,
cmticeb70801a2014-12-11 14:29:34 -0800507 help='board to debug for')
cmticef23cb132015-04-10 15:13:00 -0700508 parser.add_argument('-g', '--gdb_args', action='append', default=[],
509 help='Arguments to gdb itself. If multiple arguments are'
510 ' passed, each argument needs a separate \'-g\' flag.')
511 parser.add_argument(
512 '--remote', default=None,
513 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
514 help='Remote device on which to run the binary. Use'
515 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
516 ' already running local virtual machine.')
517 parser.add_argument('--pid', default='',
518 help='Process ID of the (already) running process on the'
519 ' remote device to which to attach.')
520 parser.add_argument('--remote_pid', dest='pid', default='',
521 help='Deprecated alias for --pid.')
522 parser.add_argument('--no-ping', dest='ping', default=True,
523 action='store_false',
524 help='Do not ping remote before attempting to connect.')
525 parser.add_argument('--attach', dest='attach_name', default='',
526 help='Name of existing process to which to attach, on'
527 ' remote device (remote debugging only). "--attach'
528 ' browser" will find the main chrome browser process;'
529 ' "--attach renderer" will find a chrome renderer'
530 ' process; "--attach gpu-process" will find the chrome'
531 ' gpu process.')
532 parser.add_argument('--cgdb', default=False,
533 action='store_true',
534 help='Use cgdb curses interface rather than plain gdb.'
535 'This option is only valid for remote debugging.')
536 parser.add_argument('inf_args', nargs=argparse.REMAINDER,
537 help='Arguments for gdb to pass to the program being'
538 ' debugged. These are positional and must come at the end'
539 ' of the command line. This will not work if attaching'
540 ' to an already running program.')
cmticeb70801a2014-12-11 14:29:34 -0800541
542 options = parser.parse_args(argv)
543 options.Freeze()
544
cmtice932e0aa2015-02-27 11:49:12 -0800545 gdb_args = []
546 inf_args = []
547 inf_cmd = ''
548
cmticef23cb132015-04-10 15:13:00 -0700549 if options.inf_args:
550 inf_cmd = options.inf_args[0]
551 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800552
cmticef23cb132015-04-10 15:13:00 -0700553 if options.gdb_args:
554 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800555
556 if inf_cmd:
557 fname = os.path.join(cros_build_lib.GetSysroot(options.board),
558 inf_cmd.lstrip('/'))
559 if not os.path.exists(fname):
560 cros_build_lib.Die('Cannot find program %s.' % fname)
cmticef23cb132015-04-10 15:13:00 -0700561 else:
562 if inf_args:
563 parser.error('Cannot specify arguments without a program.')
cmtice932e0aa2015-02-27 11:49:12 -0800564
cmticef23cb132015-04-10 15:13:00 -0700565 if inf_args and (options.pid or options.attach_name):
566 parser.error('Cannot pass arguments to an already'
567 ' running process (--remote-pid or --attach).')
568
569 if options.remote:
cmticef23cb132015-04-10 15:13:00 -0700570 if options.attach_name and options.attach_name == 'browser':
571 inf_cmd = '/opt/google/chrome/chrome'
572 else:
573 if options.cgdb:
574 parser.error('--cgdb option can only be used with remote debugging.')
575 if options.pid:
576 parser.error('Must specify a remote device (--remote) if you want '
577 'to attach to a remote pid.')
578 if options.attach_name:
579 parser.error('Must specify remote device (--remote) when using'
580 ' --attach option.')
cmtice932e0aa2015-02-27 11:49:12 -0800581
cmticeb70801a2014-12-11 14:29:34 -0800582 # Once we've finished sanity checking args, make sure we're root.
cmticef23cb132015-04-10 15:13:00 -0700583 if not options.remote:
584 _ReExecuteIfNeeded([sys.argv[0]] + argv)
cmticeb70801a2014-12-11 14:29:34 -0800585
cmticef23cb132015-04-10 15:13:00 -0700586 gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
587 options.remote, options.pid, options.attach_name,
588 options.cgdb, options.ping)
cmticeb70801a2014-12-11 14:29:34 -0800589
cmticef23cb132015-04-10 15:13:00 -0700590 try:
591 if options.remote:
592 gdb.RunRemote()
593 else:
594 gdb.Run()
595
596 except GdbException as e:
597 if options.debug:
598 raise
599 else:
600 raise cros_build_lib.Die(str(e))