blob: 15e6b89fe95eb6c3077bee53316b85d472d1c0df [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
18import sys
19import tempfile
20
21from chromite.lib import commandline
22from chromite.lib import cros_build_lib
cmticef23cb132015-04-10 15:13:00 -070023from chromite.lib import cros_logging as logging
cmticeb70801a2014-12-11 14:29:34 -080024from chromite.lib import namespaces
25from chromite.lib import osutils
Ben Pastenec228d492018-07-02 13:53:58 -070026from chromite.lib import path_util
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:
Mike Frysinger82b059e2018-07-14 00:46:18 -0400161 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700162 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)
Mike Frysinger82b059e2018-07-14 00:46:18 -0400167 if target_binary is None:
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700168 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__)), "../../../.."))
Ben Pastenec228d492018-07-02 13:53:58 -0700190 self.sdk_path = os.path.join(
191 path_util.FindCacheDir(), 'chrome-sdk/tarballs/')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700192 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
Mike Frysinger266e4ff2018-07-14 00:41:05 -0400226 if ' not stripped' not in stripped_info:
cmticef23cb132015-04-10 15:13:00 -0700227 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
Lann Martinffb95162018-08-28 12:02:54 -0600235 # pylint: disable=logging-not-lazy
cmticef23cb132015-04-10 15:13:00 -0700236 logging.info(self._MISSING_DEBUG_INFO_MSG % {
237 'board': self.board,
238 'inf_cmd': self.inf_cmd,
239 'package': package,
240 'debug_file': debug_file})
241 answer = cros_build_lib.BooleanPrompt()
242 if not answer:
243 raise GdbEarlyExitError('Exiting early, at user request.')
244
245 # Set up qemu, if appropriate.
246 qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
cmtice932e0aa2015-02-27 11:49:12 -0800247 if qemu_arch is None:
248 self.framework = 'ldso'
249 else:
250 self.framework = 'qemu'
251 self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
cmticeb70801a2014-12-11 14:29:34 -0800252
cmticef23cb132015-04-10 15:13:00 -0700253 if self.remote:
254 # Verify cgdb flag info.
255 if self.cgdb:
256 if osutils.Which('cgdb') is None:
257 raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
258 'cgdb first.')
cmticeb70801a2014-12-11 14:29:34 -0800259
cmticef23cb132015-04-10 15:13:00 -0700260 def RemoveSysrootPrefix(self, path):
cmticeb70801a2014-12-11 14:29:34 -0800261 """Returns the given path with any sysroot prefix removed."""
262 # If the sysroot is /, then the paths are already normalized.
263 if self.sysroot != '/' and path.startswith(self.sysroot):
264 path = path.replace(self.sysroot, '', 1)
cmticeb70801a2014-12-11 14:29:34 -0800265 return path
266
267 @staticmethod
268 def GetNonRootAccount():
269 """Return details about the non-root account we want to use.
270
271 Returns:
272 A tuple of (username, uid, gid, home).
273 """
274 return (
275 os.environ.get('SUDO_USER', 'nobody'),
276 int(os.environ.get('SUDO_UID', '65534')),
277 int(os.environ.get('SUDO_GID', '65534')),
278 # Should we find a better home?
279 '/tmp/portage',
280 )
281
282 @staticmethod
283 @contextlib.contextmanager
284 def LockDb(db):
285 """Lock an account database.
286
287 We use the same algorithm as shadow/user.eclass. This way we don't race
288 and corrupt things in parallel.
289 """
290 lock = '%s.lock' % db
291 _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
292
293 # First try forever to grab the lock.
294 retry = lambda e: e.errno == errno.EEXIST
295 # Retry quickly at first, but slow down over time.
296 try:
297 retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
cmticef23cb132015-04-10 15:13:00 -0700298 except Exception as e:
299 raise Exception('Could not grab lock %s. %s' % (lock, e))
cmticeb70801a2014-12-11 14:29:34 -0800300
301 # Yield while holding the lock, but try to clean it no matter what.
302 try:
303 os.unlink(tmplock)
304 yield lock
305 finally:
306 os.unlink(lock)
307
308 def SetupUser(self):
309 """Propogate the user name<->id mapping from outside the chroot.
310
311 Some unittests use getpwnam($USER), as does bash. If the account
312 is not registered in the sysroot, they get back errors.
313 """
314 MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
315 # This is kept in sync with what sdk_lib/make_chroot.sh generates.
316 SDK_GECOS = 'ChromeOS Developer'
317
318 user, uid, gid, home = self.GetNonRootAccount()
319 if user == 'nobody':
320 return
321
322 passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
323 with self.LockDb(passwd_db):
324 data = osutils.ReadFile(passwd_db)
325 accts = data.splitlines()
326 for acct in accts:
327 passwd = acct.split(':')
328 if passwd[0] == user:
329 # Did the sdk make this account?
330 if passwd[4] == SDK_GECOS:
331 # Don't modify it (see below) since we didn't create it.
332 return
333
334 # Did we make this account?
335 if passwd[4] != MAGIC_GECOS:
336 raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
337 (passwd_db, user))
338
339 # Maybe we should see if it needs to be updated? Like if they
340 # changed UIDs? But we don't really check that elsewhere ...
341 return
342
343 acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
344 'name': user,
345 'uid': uid,
346 'gid': gid,
347 'gecos': MAGIC_GECOS,
348 'homedir': home,
349 'shell': '/bin/bash',
350 }
351 with open(passwd_db, 'a') as f:
352 if data[-1] != '\n':
353 f.write('\n')
354 f.write('%s\n' % acct)
355
cmticef23cb132015-04-10 15:13:00 -0700356 def _FindRemoteProcess(self, device):
357 """Find a named process (or a pid) running on a remote device."""
358 if not self.remote_process_name and not self.pid:
359 return
cmticeb70801a2014-12-11 14:29:34 -0800360
cmticef23cb132015-04-10 15:13:00 -0700361 if self.remote_process_name:
362 # Look for a process with the specified name on the remote device; if
363 # found, get its pid.
364 pname = self.remote_process_name
365 if pname == 'browser':
366 all_chrome_pids = set(device.GetRunningPids(
367 '/opt/google/chrome/chrome'))
368 sandbox_pids = set(device.GetRunningPids(
369 '/opt/google/chrome/chrome-sandbox'))
370 non_main_chrome_pids = set(device.GetRunningPids('type='))
371 pids = list(all_chrome_pids - sandbox_pids - non_main_chrome_pids)
372 elif pname == 'renderer' or pname == 'gpu-process':
373 pids = device.GetRunningPids('type=%s'% pname)
374 else:
375 pids = device.GetRunningPids(pname)
376
377 if pids:
378 if len(pids) == 1:
379 self.pid = pids[0]
380 else:
381 raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
382 'You must specify the correct pid.'
383 % (pname, repr(pids)))
384 else:
385 raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
386 (pname, self.remote))
387
388 # Find full path for process, from pid (and verify pid).
389 command = [
390 'readlink',
391 '-e', '/proc/%s/exe' % self.pid,
392 ]
393 try:
394 res = device.RunCommand(command, capture_output=True)
395 if res.returncode == 0:
396 self.inf_cmd = res.output.rstrip('\n')
397 except cros_build_lib.RunCommandError:
398 raise GdbCannotFindRemoteProcessError('Unable to find name of process '
399 'with pid %s on %s' %
400 (self.pid, self.remote))
401
402 def GetCrossGdb(self):
403 """Find the appropriate cross-version of gdb for the board."""
404 toolchains = toolchain.GetToolchainsForBoard(self.board)
405 tc = toolchain.FilterToolchains(toolchains, 'default', True).keys()
406 cross_gdb = tc[0] + '-gdb'
407 if not osutils.Which(cross_gdb):
408 raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
409 'setup_board?' % cross_gdb)
410 return cross_gdb
411
Raul E Rangel746c45d2018-05-09 09:27:31 -0600412 def GetGdbInitCommands(self, inferior_cmd, device=None):
cmticef23cb132015-04-10 15:13:00 -0700413 """Generate list of commands with which to initialize the gdb session."""
414 gdb_init_commands = []
415
416 if self.remote:
417 sysroot_var = self.sysroot
418 else:
419 sysroot_var = '/'
420
421 gdb_init_commands = [
422 'set sysroot %s' % sysroot_var,
cmticef23cb132015-04-10 15:13:00 -0700423 'set prompt %s' % self.prompt,
424 ]
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700425 if self.in_chroot:
426 gdb_init_commands += [
427 'set solib-absolute-prefix %s' % sysroot_var,
428 'set solib-search-path %s' % sysroot_var,
429 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
430 ]
cmticef23cb132015-04-10 15:13:00 -0700431
Raul E Rangel746c45d2018-05-09 09:27:31 -0600432 if device:
433 ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
434
435 ssh_cmd.extend(['--', 'gdbserver'])
436
437 if self.pid:
438 ssh_cmd.extend(['--attach', 'stdio', str(self.pid)])
439 target_type = 'remote'
440 elif inferior_cmd:
441 ssh_cmd.extend(['-', inferior_cmd])
442 ssh_cmd.extend(self.inf_args)
443 target_type = 'remote'
444 else:
445 ssh_cmd.extend(['--multi', 'stdio'])
446 target_type = 'extended-remote'
447
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600448 ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
cmticef23cb132015-04-10 15:13:00 -0700449
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700450 if self.in_chroot:
451 if inferior_cmd:
452 gdb_init_commands.append(
453 'file %s' % os.path.join(sysroot_var,
454 inferior_cmd.lstrip(os.sep)))
455 else:
Yunlian Jiange51d6a52018-06-18 14:02:11 -0700456 binary = self.GetSimpleChromeBinary()
457 gdb_init_commands += [
458 'set debug-file-directory %s' % os.path.dirname(binary),
459 'file %s' % binary
460 ]
Raul E Rangel746c45d2018-05-09 09:27:31 -0600461
462 gdb_init_commands.append('target %s | %s' % (target_type, ssh_cmd))
cmticef23cb132015-04-10 15:13:00 -0700463 else:
464 if inferior_cmd:
465 gdb_init_commands.append('file %s ' % inferior_cmd)
466 gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
467
468 return gdb_init_commands
469
470 def RunRemote(self):
471 """Handle remote debugging, via gdbserver & cross debugger."""
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600472 with remote_access.ChromiumOSDeviceHandler(
473 self.remote,
474 port=self.remote_port,
475 connect_settings=self.ssh_settings,
476 ping=self.ping) as device:
cmticef23cb132015-04-10 15:13:00 -0700477
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600478 self.VerifyAndFinishInitialization(device)
479 gdb_cmd = self.cross_gdb
cmticef23cb132015-04-10 15:13:00 -0700480
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600481 gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
482 gdb_args = ['--quiet'] + ['--eval-command=%s' % x for x in gdb_commands]
483 gdb_args += self.gdb_args
Raul E Rangelabd74fe2018-04-26 09:17:41 -0600484
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600485 if self.cgdb:
486 gdb_args = ['-d', gdb_cmd, '--'] + gdb_args
487 gdb_cmd = 'cgdb'
cmticef23cb132015-04-10 15:13:00 -0700488
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600489 cros_build_lib.RunCommand(
490 [gdb_cmd] + gdb_args,
491 ignore_sigint=True,
492 print_cmd=True,
493 cwd=self.sysroot)
cmticef23cb132015-04-10 15:13:00 -0700494
495 def Run(self):
496 """Runs the debugger in a proper environment (e.g. qemu)."""
497
498 self.VerifyAndFinishInitialization(None)
499 self.SetupUser()
cmtice932e0aa2015-02-27 11:49:12 -0800500 if self.framework == 'qemu':
501 self.qemu.Install(self.sysroot)
502 self.qemu.RegisterBinfmt()
503
cmticeb70801a2014-12-11 14:29:34 -0800504 for mount in self._BIND_MOUNT_PATHS:
505 path = os.path.join(self.sysroot, mount)
506 osutils.SafeMakedirs(path)
507 osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
508
cmticef23cb132015-04-10 15:13:00 -0700509 gdb_cmd = self._GDB
510 inferior_cmd = self.inf_cmd
511
cmtice932e0aa2015-02-27 11:49:12 -0800512 gdb_argv = self.gdb_args[:]
513 if gdb_argv:
cmticef23cb132015-04-10 15:13:00 -0700514 gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
cmticeb70801a2014-12-11 14:29:34 -0800515 # Some programs expect to find data files via $CWD, so doing a chroot
516 # and dropping them into / would make them fail.
cmticef23cb132015-04-10 15:13:00 -0700517 cwd = self.RemoveSysrootPrefix(os.getcwd())
cmticeb70801a2014-12-11 14:29:34 -0800518
cmticeb70801a2014-12-11 14:29:34 -0800519 os.chroot(self.sysroot)
520 os.chdir(cwd)
521 # The TERM the user is leveraging might not exist in the sysroot.
522 # Force a sane default that supports standard color sequences.
523 os.environ['TERM'] = 'ansi'
524 # Some progs want this like bash else they get super confused.
525 os.environ['PWD'] = cwd
526 if not self.run_as_root:
527 _, uid, gid, home = self.GetNonRootAccount()
528 os.setgid(gid)
529 os.setuid(uid)
530 os.environ['HOME'] = home
531
cmticef23cb132015-04-10 15:13:00 -0700532 gdb_commands = self.GetGdbInitCommands(inferior_cmd)
cmticeb70801a2014-12-11 14:29:34 -0800533
cmticef23cb132015-04-10 15:13:00 -0700534 gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
535 for x in gdb_commands]
cmtice932e0aa2015-02-27 11:49:12 -0800536 gdb_args += self.gdb_args
537
Raul E Rangel73d24dd2018-05-29 12:30:33 -0600538 os.execvp(gdb_cmd, gdb_args)
cmticeb70801a2014-12-11 14:29:34 -0800539
540
cmtice932e0aa2015-02-27 11:49:12 -0800541def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
cmticeb70801a2014-12-11 14:29:34 -0800542 """Re-execute gdb as root.
543
544 We often need to do things as root, so make sure we're that. Like chroot
545 for proper library environment or do bind mounts.
546
547 Also unshare the mount namespace so as to ensure that doing bind mounts for
548 tests don't leak out to the normal chroot. Also unshare the UTS namespace
549 so changes to `hostname` do not impact the host.
550 """
551 if os.geteuid() != 0:
552 cmd = ['sudo', '-E', '--'] + argv
553 os.execvp(cmd[0], cmd)
554 else:
cmtice932e0aa2015-02-27 11:49:12 -0800555 namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
556
557
cmticef23cb132015-04-10 15:13:00 -0700558def FindInferior(arg_list):
cmtice932e0aa2015-02-27 11:49:12 -0800559 """Look for the name of the inferior (to be debugged) in arg list."""
560
561 program_name = ''
562 new_list = []
563 for item in arg_list:
564 if item[0] == '-':
565 new_list.append(item)
566 elif not program_name:
567 program_name = item
568 else:
569 raise RuntimeError('Found multiple program names: %s %s'
570 % (program_name, item))
571
572 return program_name, new_list
cmticeb70801a2014-12-11 14:29:34 -0800573
574
575def main(argv):
576
577 parser = commandline.ArgumentParser(description=__doc__)
578
cmticef23cb132015-04-10 15:13:00 -0700579 parser.add_argument('--board', default=None,
cmticeb70801a2014-12-11 14:29:34 -0800580 help='board to debug for')
cmticef23cb132015-04-10 15:13:00 -0700581 parser.add_argument('-g', '--gdb_args', action='append', default=[],
582 help='Arguments to gdb itself. If multiple arguments are'
583 ' passed, each argument needs a separate \'-g\' flag.')
584 parser.add_argument(
585 '--remote', default=None,
586 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
587 help='Remote device on which to run the binary. Use'
588 ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
589 ' already running local virtual machine.')
590 parser.add_argument('--pid', default='',
591 help='Process ID of the (already) running process on the'
592 ' remote device to which to attach.')
593 parser.add_argument('--remote_pid', dest='pid', default='',
594 help='Deprecated alias for --pid.')
595 parser.add_argument('--no-ping', dest='ping', default=True,
596 action='store_false',
597 help='Do not ping remote before attempting to connect.')
598 parser.add_argument('--attach', dest='attach_name', default='',
599 help='Name of existing process to which to attach, on'
600 ' remote device (remote debugging only). "--attach'
601 ' browser" will find the main chrome browser process;'
602 ' "--attach renderer" will find a chrome renderer'
603 ' process; "--attach gpu-process" will find the chrome'
604 ' gpu process.')
605 parser.add_argument('--cgdb', default=False,
606 action='store_true',
607 help='Use cgdb curses interface rather than plain gdb.'
608 'This option is only valid for remote debugging.')
609 parser.add_argument('inf_args', nargs=argparse.REMAINDER,
610 help='Arguments for gdb to pass to the program being'
611 ' debugged. These are positional and must come at the end'
612 ' of the command line. This will not work if attaching'
613 ' to an already running program.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700614 parser.add_argument('--binary', default='',
615 help='full path to the binary being debuged.'
616 ' This is only useful for simple chrome.'
617 ' An example is --bianry /home/out_falco/chrome.')
cmticeb70801a2014-12-11 14:29:34 -0800618
619 options = parser.parse_args(argv)
620 options.Freeze()
621
cmtice932e0aa2015-02-27 11:49:12 -0800622 gdb_args = []
623 inf_args = []
624 inf_cmd = ''
625
cmticef23cb132015-04-10 15:13:00 -0700626 if options.inf_args:
627 inf_cmd = options.inf_args[0]
628 inf_args = options.inf_args[1:]
cmtice932e0aa2015-02-27 11:49:12 -0800629
cmticef23cb132015-04-10 15:13:00 -0700630 if options.gdb_args:
631 gdb_args = options.gdb_args
cmtice932e0aa2015-02-27 11:49:12 -0800632
633 if inf_cmd:
634 fname = os.path.join(cros_build_lib.GetSysroot(options.board),
635 inf_cmd.lstrip('/'))
636 if not os.path.exists(fname):
637 cros_build_lib.Die('Cannot find program %s.' % fname)
cmticef23cb132015-04-10 15:13:00 -0700638 else:
639 if inf_args:
640 parser.error('Cannot specify arguments without a program.')
cmtice932e0aa2015-02-27 11:49:12 -0800641
cmticef23cb132015-04-10 15:13:00 -0700642 if inf_args and (options.pid or options.attach_name):
643 parser.error('Cannot pass arguments to an already'
644 ' running process (--remote-pid or --attach).')
645
646 if options.remote:
cmticef23cb132015-04-10 15:13:00 -0700647 if options.attach_name and options.attach_name == 'browser':
648 inf_cmd = '/opt/google/chrome/chrome'
649 else:
650 if options.cgdb:
651 parser.error('--cgdb option can only be used with remote debugging.')
652 if options.pid:
653 parser.error('Must specify a remote device (--remote) if you want '
654 'to attach to a remote pid.')
655 if options.attach_name:
656 parser.error('Must specify remote device (--remote) when using'
657 ' --attach option.')
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700658 if options.binary:
659 if not os.path.exists(options.binary):
660 parser.error('%s does not exist.' % options.binary)
cmtice932e0aa2015-02-27 11:49:12 -0800661
cmticeb70801a2014-12-11 14:29:34 -0800662 # Once we've finished sanity checking args, make sure we're root.
cmticef23cb132015-04-10 15:13:00 -0700663 if not options.remote:
664 _ReExecuteIfNeeded([sys.argv[0]] + argv)
cmticeb70801a2014-12-11 14:29:34 -0800665
cmticef23cb132015-04-10 15:13:00 -0700666 gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
667 options.remote, options.pid, options.attach_name,
Yunlian Jiangfdd7e082018-05-21 16:30:49 -0700668 options.cgdb, options.ping, options.binary)
cmticeb70801a2014-12-11 14:29:34 -0800669
cmticef23cb132015-04-10 15:13:00 -0700670 try:
671 if options.remote:
672 gdb.RunRemote()
673 else:
674 gdb.Run()
675
676 except GdbException as e:
677 if options.debug:
678 raise
679 else:
680 raise cros_build_lib.Die(str(e))