blob: acfb7bb5a80c0a32ce47ca1a9e0ff97bdee2db7e [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
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070071class GdbSimpleChromeBinaryError(GdbException):
72 """Raised when none or multiple chrome binaries are under out_${board} dir."""
cmtice932e0aa2015-02-27 11:49:12 -080073
cmticeb70801a2014-12-11 14:29:34 -080074class BoardSpecificGdb(object):
75 """Framework for running gdb."""
76
77 _BIND_MOUNT_PATHS = ('dev', 'dev/pts', 'proc', 'mnt/host/source', 'sys')
cmticef23cb132015-04-10 15:13:00 -070078 _GDB = '/usr/bin/gdb'
Raul E Rangel746c45d2018-05-09 09:27:31 -060079 _EXTRA_SSH_SETTINGS = {
80 'CheckHostIP': 'no',
81 'BatchMode': 'yes',
82 'LogLevel': 'QUIET'
83 }
cmticef23cb132015-04-10 15:13:00 -070084 _MISSING_DEBUG_INFO_MSG = """
85%(inf_cmd)s is stripped and %(debug_file)s does not exist on your local machine.
86 The debug symbols for that package may not be installed. To install the debug
87 symbols for %(package)s only, run:
cmticeb70801a2014-12-11 14:29:34 -080088
cmticef23cb132015-04-10 15:13:00 -070089 cros_install_debug_syms --board=%(board)s %(package)s
90
91To install the debug symbols for all available packages, run:
92
93 cros_install_debug_syms --board=%(board)s --all"""
94
95 def __init__(self, board, gdb_args, inf_cmd, inf_args, remote, pid,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -070096 remote_process_name, cgdb_flag, ping, binary):
cmticeb70801a2014-12-11 14:29:34 -080097 self.board = board
cmticef23cb132015-04-10 15:13:00 -070098 self.sysroot = None
99 self.prompt = '(gdb) '
cmtice932e0aa2015-02-27 11:49:12 -0800100 self.inf_cmd = inf_cmd
cmticef23cb132015-04-10 15:13:00 -0700101 self.run_as_root = False
102 self.gdb_args = gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800103 self.inf_args = inf_args
cmticef23cb132015-04-10 15:13:00 -0700104 self.remote = remote.hostname if remote else None
105 self.pid = pid
106 self.remote_process_name = remote_process_name
107 # Port used for sending ssh commands to DUT.
108 self.remote_port = remote.port if remote else None
109 # Port for communicating between gdb & gdbserver.
110 self.gdbserver_port = remote_access.GetUnusedPort()
111 self.ssh_settings = remote_access.CompileSSHConnectSettings(
112 **self._EXTRA_SSH_SETTINGS)
113 self.cgdb = cgdb_flag
cmtice932e0aa2015-02-27 11:49:12 -0800114 self.framework = 'auto'
115 self.qemu = None
cmticef23cb132015-04-10 15:13:00 -0700116 self.device = None
117 self.cross_gdb = None
118 self.ping = ping
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700119 self.binary = binary
120 self.in_chroot = None
121 self.chrome_path = None
122 self.sdk_path = None
123
124 def IsInChroot(self):
125 """Decide whether we are in chroot or chrome-sdk."""
126 return os.path.exists("/mnt/host/source/chromite/")
127
128 def SimpleChromeGdb(self):
129 """Get the name of the cross gdb based on board name."""
130 bin_path = self.board + '+' + os.environ['SDK_VERSION'] + '+' + \
131 'target_toolchain'
132 bin_path = os.path.join(self.sdk_path, bin_path, 'bin')
133 for f in os.listdir(bin_path):
134 if f.endswith('gdb'):
135 return os.path.join(bin_path, f)
136 raise GdbMissingDebuggerError('Cannot find cros gdb for %s.'
137 % self.board)
138
139 def SimpleChromeSysroot(self):
140 """Get the sysroot in simple chrome."""
141 sysroot = self.board + '+' + os.environ['SDK_VERSION'] + \
142 '+' + 'sysroot_chromeos-base_chromeos-chrome.tar.xz'
143 sysroot = os.path.join(self.sdk_path, sysroot)
144 if not os.path.isdir(sysroot):
145 raise GdbMissingSysrootError('Cannot find sysroot for %s at.'
146 ' %s' % self.board, sysroot)
147 return sysroot
148
149 def GetSimpleChromeBinary(self):
150 """Get path to the binary in simple chrome."""
151 if self.binary:
152 return self.binary
153
154 output_dir = os.path.join(self.chrome_path, 'src',
155 'out_{}'.format(self.board))
156 target_binary = None
157 binary_name = os.path.basename(self.inf_cmd)
158 for root, _, files in os.walk(output_dir):
159 for f in files:
160 if f == binary_name:
161 if target_binary == None:
162 target_binary = os.path.join(root, f)
163 else:
164 raise GdbSimpleChromeBinaryError(
165 'There are multiple %s under %s. Please specify the path to '
166 'the binary via --binary'% binary_name, output_dir)
167 if target_binary == None:
168 raise GdbSimpleChromeBinaryError('There is no %s under %s.'
169 % binary_name, output_dir)
170 return target_binary
cmtice932e0aa2015-02-27 11:49:12 -0800171
cmticef23cb132015-04-10 15:13:00 -0700172 def VerifyAndFinishInitialization(self, device):
173 """Verify files/processes exist and flags are correct."""
174 if not self.board:
175 if self.remote:
176 self.board = cros_build_lib.GetBoard(device_board=device.board,
177 override_board=self.board)
178 else:
179 raise GdbCannotDetectBoardError('Cannot determine which board to use. '
180 'Please specify the with --board flag.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700181 self.in_chroot = self.IsInChroot()
cmticef23cb132015-04-10 15:13:00 -0700182 self.prompt = '(%s-gdb) ' % self.board
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700183 if self.in_chroot:
184 self.sysroot = cros_build_lib.GetSysroot(board=self.board)
185 self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
186 self.cross_gdb = self.GetCrossGdb()
187 else:
188 self.chrome_path = os.path.realpath(os.path.join(os.path.dirname(
189 os.path.realpath(__file__)), "../../../.."))
190 self.sdk_path = os.path.join(self.chrome_path,
191 '.cros_cache/chrome-sdk/tarballs/')
192 self.sysroot = self.SimpleChromeSysroot()
193 self.cross_gdb = self.SimpleChromeGdb()
cmticef23cb132015-04-10 15:13:00 -0700194
195 if self.remote:
196
197 # If given remote process name, find pid & inf_cmd on remote device.
198 if self.remote_process_name or self.pid:
199 self._FindRemoteProcess(device)
200
201 # Verify that sysroot is valid (exists).
202 if not os.path.isdir(self.sysroot):
203 raise GdbMissingSysrootError('Sysroot does not exist: %s' %
204 self.sysroot)
205
206 self.device = device
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700207 if not self.in_chroot:
208 return
209
cmticef23cb132015-04-10 15:13:00 -0700210 sysroot_inf_cmd = ''
211 if self.inf_cmd:
212 sysroot_inf_cmd = os.path.join(self.sysroot,
213 self.inf_cmd.lstrip('/'))
214
215 # Verify that inf_cmd, if given, exists.
216 if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
217 raise GdbMissingInferiorError('Cannot find file %s (in sysroot).' %
218 sysroot_inf_cmd)
219
220 # Check to see if inf_cmd is stripped, and if so, check to see if debug file
221 # exists. If not, tell user and give them the option of quitting & getting
222 # the debug info.
223 if sysroot_inf_cmd:
224 stripped_info = cros_build_lib.RunCommand(['file', sysroot_inf_cmd],
225 capture_output=True).output
226 if not ' not stripped' in stripped_info:
227 debug_file = os.path.join(self.sysroot, 'usr/lib/debug',
228 self.inf_cmd.lstrip('/'))
229 debug_file += '.debug'
230 if not os.path.exists(debug_file):
231 equery = 'equery-%s' % self.board
232 package = cros_build_lib.RunCommand([equery, '-q', 'b',
233 self.inf_cmd],
234 capture_output=True).output
235 logging.info(self._MISSING_DEBUG_INFO_MSG % {
236 'board': self.board,
237 'inf_cmd': self.inf_cmd,
238 'package': package,
239 'debug_file': debug_file})
240 answer = cros_build_lib.BooleanPrompt()
241 if not answer:
242 raise GdbEarlyExitError('Exiting early, at user request.')
243
244 # Set up qemu, if appropriate.
245 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800246 if qemu_arch is None:
247 self.framework = 'ldso'
248 else:
249 self.framework = 'qemu'
250 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800251
cmticef23cb132015-04-10 15:13:00 -0700252 if self.remote:
253 # Verify cgdb flag info.
254 if self.cgdb:
255 if osutils.Which('cgdb') is None:
256 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
257 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800258
cmticef23cb132015-04-10 15:13:00 -0700259 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800260 """Returns the given path with any sysroot prefix removed."""
261 # If the sysroot is /, then the paths are already normalized.
262 if self.sysroot != '/' and path.startswith(self.sysroot):
263 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800264 return path
265
266 @staticmethod
267 def GetNonRootAccount():
268 """Return details about the non-root account we want to use.
269
270 Returns:
271 A tuple of (username, uid, gid, home).
272 """
273 return (
274 os.environ.get('SUDO_USER', 'nobody'),
275 int(os.environ.get('SUDO_UID', '65534')),
276 int(os.environ.get('SUDO_GID', '65534')),
277 # Should we find a better home?
278 '/tmp/portage',
279 )
280
281 @staticmethod
282 @contextlib.contextmanager
283 def LockDb(db):
284 """Lock an account database.
285
286 We use the same algorithm as shadow/user.eclass. This way we don't race
287 and corrupt things in parallel.
288 """
289 lock = '%s.lock' % db
290 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
291
292 # First try forever to grab the lock.
293 retry = lambda e: e.errno == errno.EEXIST
294 # Retry quickly at first, but slow down over time.
295 try:
296 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700297 except Exception as e:
298 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800299
300 # Yield while holding the lock, but try to clean it no matter what.
301 try:
302 os.unlink(tmplock)
303 yield lock
304 finally:
305 os.unlink(lock)
306
307 def SetupUser(self):
308 """Propogate the user name<->id mapping from outside the chroot.
309
310 Some unittests use getpwnam($USER), as does bash. If the account
311 is not registered in the sysroot, they get back errors.
312 """
313 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
314 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
315 SDK_GECOS = 'ChromeOS Developer'
316
317 user, uid, gid, home = self.GetNonRootAccount()
318 if user == 'nobody':
319 return
320
321 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
322 with self.LockDb(passwd_db):
323 data = osutils.ReadFile(passwd_db)
324 accts = data.splitlines()
325 for acct in accts:
326 passwd = acct.split(':')
327 if passwd[0] == user:
328 # Did the sdk make this account?
329 if passwd[4] == SDK_GECOS:
330 # Don't modify it (see below) since we didn't create it.
331 return
332
333 # Did we make this account?
334 if passwd[4] != MAGIC_GECOS:
335 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
336 (passwd_db, user))
337
338 # Maybe we should see if it needs to be updated? Like if they
339 # changed UIDs? But we don't really check that elsewhere ...
340 return
341
342 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
343 'name': user,
344 'uid': uid,
345 'gid': gid,
346 'gecos': MAGIC_GECOS,
347 'homedir': home,
348 'shell': '/bin/bash',
349 }
350 with open(passwd_db, 'a') as f:
351 if data[-1] != '\n':
352 f.write('\n')
353 f.write('%s\n' % acct)
354
cmticef23cb132015-04-10 15:13:00 -0700355 def _FindRemoteProcess(self, device):
356 """Find a named process (or a pid) running on a remote device."""
357 if not self.remote_process_name and not self.pid:
358 return
cmticeb70801a2014-12-11 14:29:34 -0800359
cmticef23cb132015-04-10 15:13:00 -0700360 if self.remote_process_name:
361 # Look for a process with the specified name on the remote device; if
362 # found, get its pid.
363 pname = self.remote_process_name
364 if pname == 'browser':
365 all_chrome_pids = set(device.GetRunningPids(
366 '/opt/google/chrome/chrome'))
367 sandbox_pids = set(device.GetRunningPids(
368 '/opt/google/chrome/chrome-sandbox'))
369 non_main_chrome_pids = set(device.GetRunningPids('type='))
370 pids = list(all_chrome_pids - sandbox_pids - non_main_chrome_pids)
371 elif pname == 'renderer' or pname == 'gpu-process':
372 pids = device.GetRunningPids('type=%s'% pname)
373 else:
374 pids = device.GetRunningPids(pname)
375
376 if pids:
377 if len(pids) == 1:
378 self.pid = pids[0]
379 else:
380 raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
381 'You must specify the correct pid.'
382 % (pname, repr(pids)))
383 else:
384 raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
385 (pname, self.remote))
386
387 # Find full path for process, from pid (and verify pid).
388 command = [
389 'readlink',
390 '-e', '/proc/%s/exe' % self.pid,
391 ]
392 try:
393 res = device.RunCommand(command, capture_output=True)
394 if res.returncode == 0:
395 self.inf_cmd = res.output.rstrip('\n')
396 except cros_build_lib.RunCommandError:
397 raise GdbCannotFindRemoteProcessError('Unable to find name of process '
398 'with pid %s on %s' %
399 (self.pid, self.remote))
400
401 def GetCrossGdb(self):
402 """Find the appropriate cross-version of gdb for the board."""
403 toolchains = toolchain.GetToolchainsForBoard(self.board)
404 tc = toolchain.FilterToolchains(toolchains, 'default', True).keys()
405 cross_gdb = tc[0] + '-gdb'
406 if not osutils.Which(cross_gdb):
407 raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
408 'setup_board?' % cross_gdb)
409 return cross_gdb
410
Raul E Rangel746c45d2018-05-09 09:27:31 -0600411 def GetGdbInitCommands(self, inferior_cmd, device=None):
cmticef23cb132015-04-10 15:13:00 -0700412 """Generate list of commands with which to initialize the gdb session."""
413 gdb_init_commands = []
414
415 if self.remote:
416 sysroot_var = self.sysroot
417 else:
418 sysroot_var = '/'
419
420 gdb_init_commands = [
421 'set sysroot %s' % sysroot_var,
cmticef23cb132015-04-10 15:13:00 -0700422 'set prompt %s' % self.prompt,
423 ]
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700424 if self.in_chroot:
425 gdb_init_commands += [
426 'set solib-absolute-prefix %s' % sysroot_var,
427 'set solib-search-path %s' % sysroot_var,
428 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
429 ]
cmticef23cb132015-04-10 15:13:00 -0700430
Raul E Rangel746c45d2018-05-09 09:27:31 -0600431 if device:
432 ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
433
434 ssh_cmd.extend(['--', 'gdbserver'])
435
436 if self.pid:
437 ssh_cmd.extend(['--attach', 'stdio', str(self.pid)])
438 target_type = 'remote'
439 elif inferior_cmd:
440 ssh_cmd.extend(['-', inferior_cmd])
441 ssh_cmd.extend(self.inf_args)
442 target_type = 'remote'
443 else:
444 ssh_cmd.extend(['--multi', 'stdio'])
445 target_type = 'extended-remote'
446
447 ssh_cmd = ' '.join(map(pipes.quote, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700448
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700449 if self.in_chroot:
450 if inferior_cmd:
451 gdb_init_commands.append(
452 'file %s' % os.path.join(sysroot_var,
453 inferior_cmd.lstrip(os.sep)))
454 else:
455 gdb_init_commands.append('file %s' % self.GetSimpleChromeBinary())
Raul E Rangel746c45d2018-05-09 09:27:31 -0600456
457 gdb_init_commands.append('target %s | %s' % (target_type, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700458 else:
459 if inferior_cmd:
460 gdb_init_commands.append('file %s ' % inferior_cmd)
461 gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
462
463 return gdb_init_commands
464
465 def RunRemote(self):
466 """Handle remote debugging, via gdbserver & cross debugger."""
467 device = None
468 try:
469 device = remote_access.ChromiumOSDeviceHandler(
470 self.remote,
471 port=self.remote_port,
472 connect_settings=self.ssh_settings,
473 ping=self.ping).device
474 except remote_access.DeviceNotPingableError:
475 raise GdbBadRemoteDeviceError('Remote device %s is not responding to '
476 'ping.' % self.remote)
477
478 self.VerifyAndFinishInitialization(device)
479 gdb_cmd = self.cross_gdb
480
Raul E Rangel746c45d2018-05-09 09:27:31 -0600481 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
482 gdb_args = ['--quiet'] + ['--eval-command=%s' % x for x in gdb_commands]
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600483 gdb_args += self.gdb_args
484
cmticef23cb132015-04-10 15:13:00 -0700485 if self.cgdb:
Raul E Rangel746c45d2018-05-09 09:27:31 -0600486 gdb_args = ['-d', gdb_cmd, '--'] + gdb_args
487 gdb_cmd = 'cgdb'
cmticef23cb132015-04-10 15:13:00 -0700488
Raul E Rangel746c45d2018-05-09 09:27:31 -0600489 logging.debug('Running: %s', [gdb_cmd] + gdb_args)
490
491 os.chdir(self.sysroot)
492 sys.exit(os.execvp(gdb_cmd, gdb_args))
cmticef23cb132015-04-10 15:13:00 -0700493
494 def Run(self):
495 """Runs the debugger in a proper environment (e.g. qemu)."""
496
497 self.VerifyAndFinishInitialization(None)
498 self.SetupUser()
cmtice932e0aa2015-02-27 11:49:12 -0800499 if self.framework == 'qemu':
500 self.qemu.Install(self.sysroot)
501 self.qemu.RegisterBinfmt()
502
cmticeb70801a2014-12-11 14:29:34 -0800503 for mount in self._BIND_MOUNT_PATHS:
504 path = os.path.join(self.sysroot, mount)
505 osutils.SafeMakedirs(path)
506 osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
507
cmticef23cb132015-04-10 15:13:00 -0700508 gdb_cmd = self._GDB
509 inferior_cmd = self.inf_cmd
510
cmtice932e0aa2015-02-27 11:49:12 -0800511 gdb_argv = self.gdb_args[:]
512 if gdb_argv:
cmticef23cb132015-04-10 15:13:00 -0700513 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
cmticeb70801a2014-12-11 14:29:34 -0800514 # Some programs expect to find data files via $CWD, so doing a chroot
515 # and dropping them into / would make them fail.
cmticef23cb132015-04-10 15:13:00 -0700516 cwd = self.RemoveSysrootPrefix(os.getcwd())
cmticeb70801a2014-12-11 14:29:34 -0800517
cmticeb70801a2014-12-11 14:29:34 -0800518 os.chroot(self.sysroot)
519 os.chdir(cwd)
520 # The TERM the user is leveraging might not exist in the sysroot.
521 # Force a sane default that supports standard color sequences.
522 os.environ['TERM'] = 'ansi'
523 # Some progs want this like bash else they get super confused.
524 os.environ['PWD'] = cwd
525 if not self.run_as_root:
526 _, uid, gid, home = self.GetNonRootAccount()
527 os.setgid(gid)
528 os.setuid(uid)
529 os.environ['HOME'] = home
530
cmticef23cb132015-04-10 15:13:00 -0700531 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
cmticeb70801a2014-12-11 14:29:34 -0800532
cmticef23cb132015-04-10 15:13:00 -0700533 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
534 for x in gdb_commands]
cmtice932e0aa2015-02-27 11:49:12 -0800535 gdb_args += self.gdb_args
536
cmtice932e0aa2015-02-27 11:49:12 -0800537 sys.exit(os.execvp(gdb_cmd, gdb_args))
cmticeb70801a2014-12-11 14:29:34 -0800538
539
cmtice932e0aa2015-02-27 11:49:12 -0800540def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
cmticeb70801a2014-12-11 14:29:34 -0800541 """Re-execute gdb as root.
542
543 We often need to do things as root, so make sure we're that. Like chroot
544 for proper library environment or do bind mounts.
545
546 Also unshare the mount namespace so as to ensure that doing bind mounts for
547 tests don't leak out to the normal chroot. Also unshare the UTS namespace
548 so changes to `hostname` do not impact the host.
549 """
550 if os.geteuid() != 0:
551 cmd = ['sudo', '-E', '--'] + argv
552 os.execvp(cmd[0], cmd)
553 else:
cmtice932e0aa2015-02-27 11:49:12 -0800554 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
555
556
cmticef23cb132015-04-10 15:13:00 -0700557def FindInferior(arg_list):
cmtice932e0aa2015-02-27 11:49:12 -0800558 """Look for the name of the inferior (to be debugged) in arg list."""
559
560 program_name = ''
561 new_list = []
562 for item in arg_list:
563 if item[0] == '-':
564 new_list.append(item)
565 elif not program_name:
566 program_name = item
567 else:
568 raise RuntimeError('Found multiple program names: %s %s'
569 % (program_name, item))
570
571 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800572
573
574def main(argv):
575
576 parser = commandline.ArgumentParser(description=__doc__)
577
cmticef23cb132015-04-10 15:13:00 -0700578 parser.add_argument('--board', default=None,
cmticeb70801a2014-12-11 14:29:34 -0800579 help='board to debug for')
cmticef23cb132015-04-10 15:13:00 -0700580 parser.add_argument('-g', '--gdb_args', action='append', default=[],
581 help='Arguments to gdb itself. If multiple arguments are'
582 ' passed, each argument needs a separate \'-g\' flag.')
583 parser.add_argument(
584 '--remote', default=None,
585 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
586 help='Remote device on which to run the binary. Use'
587 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
588 ' already running local virtual machine.')
589 parser.add_argument('--pid', default='',
590 help='Process ID of the (already) running process on the'
591 ' remote device to which to attach.')
592 parser.add_argument('--remote_pid', dest='pid', default='',
593 help='Deprecated alias for --pid.')
594 parser.add_argument('--no-ping', dest='ping', default=True,
595 action='store_false',
596 help='Do not ping remote before attempting to connect.')
597 parser.add_argument('--attach', dest='attach_name', default='',
598 help='Name of existing process to which to attach, on'
599 ' remote device (remote debugging only). "--attach'
600 ' browser" will find the main chrome browser process;'
601 ' "--attach renderer" will find a chrome renderer'
602 ' process; "--attach gpu-process" will find the chrome'
603 ' gpu process.')
604 parser.add_argument('--cgdb', default=False,
605 action='store_true',
606 help='Use cgdb curses interface rather than plain gdb.'
607 'This option is only valid for remote debugging.')
608 parser.add_argument('inf_args', nargs=argparse.REMAINDER,
609 help='Arguments for gdb to pass to the program being'
610 ' debugged. These are positional and must come at the end'
611 ' of the command line. This will not work if attaching'
612 ' to an already running program.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700613 parser.add_argument('--binary', default='',
614 help='full path to the binary being debuged.'
615 ' This is only useful for simple chrome.'
616 ' An example is --bianry /home/out_falco/chrome.')
cmticeb70801a2014-12-11 14:29:34 -0800617
618 options = parser.parse_args(argv)
619 options.Freeze()
620
cmtice932e0aa2015-02-27 11:49:12 -0800621 gdb_args = []
622 inf_args = []
623 inf_cmd = ''
624
cmticef23cb132015-04-10 15:13:00 -0700625 if options.inf_args:
626 inf_cmd = options.inf_args[0]
627 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800628
cmticef23cb132015-04-10 15:13:00 -0700629 if options.gdb_args:
630 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800631
632 if inf_cmd:
633 fname = os.path.join(cros_build_lib.GetSysroot(options.board),
634 inf_cmd.lstrip('/'))
635 if not os.path.exists(fname):
636 cros_build_lib.Die('Cannot find program %s.' % fname)
cmticef23cb132015-04-10 15:13:00 -0700637 else:
638 if inf_args:
639 parser.error('Cannot specify arguments without a program.')
cmtice932e0aa2015-02-27 11:49:12 -0800640
cmticef23cb132015-04-10 15:13:00 -0700641 if inf_args and (options.pid or options.attach_name):
642 parser.error('Cannot pass arguments to an already'
643 ' running process (--remote-pid or --attach).')
644
645 if options.remote:
cmticef23cb132015-04-10 15:13:00 -0700646 if options.attach_name and options.attach_name == 'browser':
647 inf_cmd = '/opt/google/chrome/chrome'
648 else:
649 if options.cgdb:
650 parser.error('--cgdb option can only be used with remote debugging.')
651 if options.pid:
652 parser.error('Must specify a remote device (--remote) if you want '
653 'to attach to a remote pid.')
654 if options.attach_name:
655 parser.error('Must specify remote device (--remote) when using'
656 ' --attach option.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700657 if options.binary:
658 if not os.path.exists(options.binary):
659 parser.error('%s does not exist.' % options.binary)
cmtice932e0aa2015-02-27 11:49:12 -0800660
cmticeb70801a2014-12-11 14:29:34 -0800661 # Once we've finished sanity checking args, make sure we're root.
cmticef23cb132015-04-10 15:13:00 -0700662 if not options.remote:
663 _ReExecuteIfNeeded([sys.argv[0]] + argv)
cmticeb70801a2014-12-11 14:29:34 -0800664
cmticef23cb132015-04-10 15:13:00 -0700665 gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
666 options.remote, options.pid, options.attach_name,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700667 options.cgdb, options.ping, options.binary)
cmticeb70801a2014-12-11 14:29:34 -0800668
cmticef23cb132015-04-10 15:13:00 -0700669 try:
670 if options.remote:
671 gdb.RunRemote()
672 else:
673 gdb.Run()
674
675 except GdbException as e:
676 if options.debug:
677 raise
678 else:
679 raise cros_build_lib.Die(str(e))