Formatting: Format all python code with black.
This CL is probably not what you're looking for, it's only
automated formatting. Ignore it with
`git blame --ignore-rev <revision>` for this commit.
BUG=b:233893248
TEST=CQ
Change-Id: I66591d7a738d241aed3290138c0f68065ab10a6d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/3879174
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Tested-by: Alex Klein <saklein@chromium.org>
diff --git a/scripts/cros_gdb.py b/scripts/cros_gdb.py
index c6313c6..9ea800b 100644
--- a/scripts/cros_gdb.py
+++ b/scripts/cros_gdb.py
@@ -31,58 +31,60 @@
class GdbException(Exception):
- """Base exception for this module."""
+ """Base exception for this module."""
class GdbBadRemoteDeviceError(GdbException):
- """Raised when remote device does not exist or is not responding."""
+ """Raised when remote device does not exist or is not responding."""
class GdbMissingSysrootError(GdbException):
- """Raised when path to sysroot cannot be found in chroot."""
+ """Raised when path to sysroot cannot be found in chroot."""
class GdbMissingInferiorError(GdbException):
- """Raised when the binary to be debugged cannot be found."""
+ """Raised when the binary to be debugged cannot be found."""
class GdbMissingDebuggerError(GdbException):
- """Raised when cannot find correct version of debugger."""
+ """Raised when cannot find correct version of debugger."""
class GdbCannotFindRemoteProcessError(GdbException):
- """Raised when cannot find requested executing process on remote device."""
+ """Raised when cannot find requested executing process on remote device."""
class GdbUnableToStartGdbserverError(GdbException):
- """Raised when error occurs trying to start gdbserver on remote device."""
+ """Raised when error occurs trying to start gdbserver on remote device."""
class GdbTooManyPidsError(GdbException):
- """Raised when more than one matching pid is found running on device."""
+ """Raised when more than one matching pid is found running on device."""
class GdbEarlyExitError(GdbException):
- """Raised when user requests to exit early."""
+ """Raised when user requests to exit early."""
class GdbCannotDetectBoardError(GdbException):
- """Raised when board isn't specified and can't be automatically determined."""
+ """Raised when board isn't specified and can't be automatically determined."""
+
class GdbSimpleChromeBinaryError(GdbException):
- """Raised when none or multiple chrome binaries are under out_${board} dir."""
+ """Raised when none or multiple chrome binaries are under out_${board} dir."""
+
class BoardSpecificGdb(object):
- """Framework for running gdb."""
+ """Framework for running gdb."""
- _BIND_MOUNT_PATHS = ('dev', 'dev/pts', 'proc', 'mnt/host/source', 'sys')
- _GDB = '/usr/bin/gdb'
- _EXTRA_SSH_SETTINGS = {
- 'CheckHostIP': 'no',
- 'BatchMode': 'yes',
- 'LogLevel': 'QUIET'
- }
- _MISSING_DEBUG_INFO_MSG = """
+ _BIND_MOUNT_PATHS = ("dev", "dev/pts", "proc", "mnt/host/source", "sys")
+ _GDB = "/usr/bin/gdb"
+ _EXTRA_SSH_SETTINGS = {
+ "CheckHostIP": "no",
+ "BatchMode": "yes",
+ "LogLevel": "QUIET",
+ }
+ _MISSING_DEBUG_INFO_MSG = """
%(inf_cmd)s is stripped and %(debug_file)s does not exist on your local machine.
The debug symbols for that package may not be installed. To install the debug
symbols for %(package)s only, run:
@@ -93,588 +95,701 @@
cros_install_debug_syms --board=%(board)s --all"""
- def __init__(self, board, gdb_args, inf_cmd, inf_args, remote, pid,
- remote_process_name, cgdb_flag, ping, binary):
- self.board = board
- self.sysroot = None
- self.prompt = '(gdb) '
- self.inf_cmd = inf_cmd
- self.run_as_root = False
- self.gdb_args = gdb_args
- self.inf_args = inf_args
- self.remote = remote.hostname if remote else None
- self.pid = pid
- self.remote_process_name = remote_process_name
- # Port used for sending ssh commands to DUT.
- self.remote_port = remote.port if remote else None
- # Port for communicating between gdb & gdbserver.
- self.gdbserver_port = remote_access.GetUnusedPort()
- self.ssh_settings = remote_access.CompileSSHConnectSettings(
- **self._EXTRA_SSH_SETTINGS)
- self.cgdb = cgdb_flag
- self.framework = 'auto'
- self.qemu = None
- self.device = None
- self.cross_gdb = None
- self.ping = ping
- self.binary = binary
- self.in_chroot = None
- self.chrome_path = None
- self.sdk_path = None
+ def __init__(
+ self,
+ board,
+ gdb_args,
+ inf_cmd,
+ inf_args,
+ remote,
+ pid,
+ remote_process_name,
+ cgdb_flag,
+ ping,
+ binary,
+ ):
+ self.board = board
+ self.sysroot = None
+ self.prompt = "(gdb) "
+ self.inf_cmd = inf_cmd
+ self.run_as_root = False
+ self.gdb_args = gdb_args
+ self.inf_args = inf_args
+ self.remote = remote.hostname if remote else None
+ self.pid = pid
+ self.remote_process_name = remote_process_name
+ # Port used for sending ssh commands to DUT.
+ self.remote_port = remote.port if remote else None
+ # Port for communicating between gdb & gdbserver.
+ self.gdbserver_port = remote_access.GetUnusedPort()
+ self.ssh_settings = remote_access.CompileSSHConnectSettings(
+ **self._EXTRA_SSH_SETTINGS
+ )
+ self.cgdb = cgdb_flag
+ self.framework = "auto"
+ self.qemu = None
+ self.device = None
+ self.cross_gdb = None
+ self.ping = ping
+ self.binary = binary
+ self.in_chroot = None
+ self.chrome_path = None
+ self.sdk_path = None
- def IsInChroot(self):
- """Decide whether we are in chroot or chrome-sdk."""
- return os.path.exists('/mnt/host/source/chromite/')
+ def IsInChroot(self):
+ """Decide whether we are in chroot or chrome-sdk."""
+ return os.path.exists("/mnt/host/source/chromite/")
- def SimpleChromeGdb(self):
- """Get the name of the cross gdb based on board name."""
- bin_path = cros_chrome_sdk.SDKFetcher.GetCachePath(
- cros_chrome_sdk.SDKFetcher.TARGET_TOOLCHAIN_KEY,
- self.sdk_path, self.board)
- bin_path = os.path.join(bin_path, 'bin')
- for f in os.listdir(bin_path):
- if f.endswith('gdb'):
- return os.path.join(bin_path, f)
- raise GdbMissingDebuggerError('Cannot find cross gdb for %s.' % self.board)
+ def SimpleChromeGdb(self):
+ """Get the name of the cross gdb based on board name."""
+ bin_path = cros_chrome_sdk.SDKFetcher.GetCachePath(
+ cros_chrome_sdk.SDKFetcher.TARGET_TOOLCHAIN_KEY,
+ self.sdk_path,
+ self.board,
+ )
+ bin_path = os.path.join(bin_path, "bin")
+ for f in os.listdir(bin_path):
+ if f.endswith("gdb"):
+ return os.path.join(bin_path, f)
+ raise GdbMissingDebuggerError(
+ "Cannot find cross gdb for %s." % self.board
+ )
- def SimpleChromeSysroot(self):
- """Get the sysroot in simple chrome."""
- sysroot = cros_chrome_sdk.SDKFetcher.GetCachePath(
- constants.CHROME_SYSROOT_TAR, self.sdk_path, self.board)
- if not sysroot:
- raise GdbMissingSysrootError('Cannot find sysroot for %s at %s'
- % (self.board, self.sdk_path))
- return sysroot
+ def SimpleChromeSysroot(self):
+ """Get the sysroot in simple chrome."""
+ sysroot = cros_chrome_sdk.SDKFetcher.GetCachePath(
+ constants.CHROME_SYSROOT_TAR, self.sdk_path, self.board
+ )
+ if not sysroot:
+ raise GdbMissingSysrootError(
+ "Cannot find sysroot for %s at %s" % (self.board, self.sdk_path)
+ )
+ return sysroot
- def GetSimpleChromeBinary(self):
- """Get path to the binary in simple chrome."""
- if self.binary:
- return self.binary
+ def GetSimpleChromeBinary(self):
+ """Get path to the binary in simple chrome."""
+ if self.binary:
+ return self.binary
- output_dir = os.path.join(self.chrome_path, 'src',
- f'out_{self.board}')
- target_binary = None
- binary_name = os.path.basename(self.inf_cmd)
- for root, _, files in os.walk(output_dir):
- for f in files:
- if f == binary_name:
- if target_binary is None:
- target_binary = os.path.join(root, f)
- else:
+ output_dir = os.path.join(self.chrome_path, "src", f"out_{self.board}")
+ target_binary = None
+ binary_name = os.path.basename(self.inf_cmd)
+ for root, _, files in os.walk(output_dir):
+ for f in files:
+ if f == binary_name:
+ if target_binary is None:
+ target_binary = os.path.join(root, f)
+ else:
+ raise GdbSimpleChromeBinaryError(
+ "There are multiple %s under %s. Please specify the path to "
+ "the binary via --binary"
+ % (binary_name, output_dir)
+ )
+ if target_binary is None:
raise GdbSimpleChromeBinaryError(
- 'There are multiple %s under %s. Please specify the path to '
- 'the binary via --binary' % (binary_name, output_dir))
- if target_binary is None:
- raise GdbSimpleChromeBinaryError('There is no %s under %s.'
- % (binary_name, output_dir))
- return target_binary
+ "There is no %s under %s." % (binary_name, output_dir)
+ )
+ return target_binary
- def VerifyAndFinishInitialization(self, device):
- """Verify files/processes exist and flags are correct."""
- if not self.board:
- if self.remote:
- self.board = cros_build_lib.GetBoard(device_board=device.board,
- override_board=self.board,
- strict=True)
- else:
- raise GdbCannotDetectBoardError('Cannot determine which board to use. '
- 'Please specify the with --board flag.')
- self.in_chroot = self.IsInChroot()
- self.prompt = '(%s-gdb) ' % self.board
- if self.in_chroot:
- self.sysroot = build_target_lib.get_default_sysroot_path(self.board)
- self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
- self.cross_gdb = self.GetCrossGdb()
- else:
- self.chrome_path = os.path.realpath(os.path.join(os.path.dirname(
- os.path.realpath(__file__)), '../../../..'))
- self.sdk_path = path_util.FindCacheDir()
- self.sysroot = self.SimpleChromeSysroot()
- self.cross_gdb = self.SimpleChromeGdb()
+ def VerifyAndFinishInitialization(self, device):
+ """Verify files/processes exist and flags are correct."""
+ if not self.board:
+ if self.remote:
+ self.board = cros_build_lib.GetBoard(
+ device_board=device.board,
+ override_board=self.board,
+ strict=True,
+ )
+ else:
+ raise GdbCannotDetectBoardError(
+ "Cannot determine which board to use. "
+ "Please specify the with --board flag."
+ )
+ self.in_chroot = self.IsInChroot()
+ self.prompt = "(%s-gdb) " % self.board
+ if self.in_chroot:
+ self.sysroot = build_target_lib.get_default_sysroot_path(self.board)
+ self.inf_cmd = self.RemoveSysrootPrefix(self.inf_cmd)
+ self.cross_gdb = self.GetCrossGdb()
+ else:
+ self.chrome_path = os.path.realpath(
+ os.path.join(
+ os.path.dirname(os.path.realpath(__file__)), "../../../.."
+ )
+ )
+ self.sdk_path = path_util.FindCacheDir()
+ self.sysroot = self.SimpleChromeSysroot()
+ self.cross_gdb = self.SimpleChromeGdb()
- if self.remote:
+ if self.remote:
- # If given remote process name, find pid & inf_cmd on remote device.
- if self.remote_process_name or self.pid:
- self._FindRemoteProcess(device)
+ # If given remote process name, find pid & inf_cmd on remote device.
+ if self.remote_process_name or self.pid:
+ self._FindRemoteProcess(device)
- # Verify that sysroot is valid (exists).
- if not os.path.isdir(self.sysroot):
- raise GdbMissingSysrootError('Sysroot does not exist: %s' %
- self.sysroot)
+ # Verify that sysroot is valid (exists).
+ if not os.path.isdir(self.sysroot):
+ raise GdbMissingSysrootError(
+ "Sysroot does not exist: %s" % self.sysroot
+ )
- self.device = device
- if not self.in_chroot:
- return
-
- sysroot_inf_cmd = ''
- if self.inf_cmd:
- sysroot_inf_cmd = os.path.join(self.sysroot,
- self.inf_cmd.lstrip('/'))
-
- # Verify that inf_cmd, if given, exists.
- if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
- raise GdbMissingInferiorError('Cannot find file %s (in sysroot).' %
- sysroot_inf_cmd)
-
- # Check to see if inf_cmd is stripped, and if so, check to see if debug file
- # exists. If not, tell user and give them the option of quitting & getting
- # the debug info.
- if sysroot_inf_cmd:
- stripped_info = cros_build_lib.run(['file', sysroot_inf_cmd],
- capture_output=True,
- encoding='utf-8').stdout
- if ' not stripped' not in stripped_info:
- debug_file = os.path.join(self.sysroot, 'usr/lib/debug',
- self.inf_cmd.lstrip('/'))
- debug_file += '.debug'
- if not os.path.exists(debug_file):
- equery = 'equery-%s' % self.board
- package = cros_build_lib.run([equery, '-q', 'b', self.inf_cmd],
- capture_output=True,
- encoding='utf-8').stdout
- # pylint: disable=logging-not-lazy
- logging.info(self._MISSING_DEBUG_INFO_MSG % {
- 'board': self.board,
- 'inf_cmd': self.inf_cmd,
- 'package': package,
- 'debug_file': debug_file})
- answer = cros_build_lib.BooleanPrompt()
- if not answer:
- raise GdbEarlyExitError('Exiting early, at user request.')
-
- # Set up qemu, if appropriate.
- qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
- if qemu_arch is None:
- self.framework = 'ldso'
- else:
- self.framework = 'qemu'
- self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
-
- if self.remote:
- # Verify cgdb flag info.
- if self.cgdb:
- if osutils.Which('cgdb') is None:
- raise GdbMissingDebuggerError('Cannot find cgdb. Please install '
- 'cgdb first.')
-
- def RemoveSysrootPrefix(self, path):
- """Returns the given path with any sysroot prefix removed."""
- # If the sysroot is /, then the paths are already normalized.
- if self.sysroot != '/' and path.startswith(self.sysroot):
- path = path.replace(self.sysroot, '', 1)
- return path
-
- @staticmethod
- def GetNonRootAccount():
- """Return details about the non-root account we want to use.
-
- Returns:
- A tuple of (username, uid, gid, home).
- """
- return (
- os.environ.get('SUDO_USER', 'nobody'),
- int(os.environ.get('SUDO_UID', '65534')),
- int(os.environ.get('SUDO_GID', '65534')),
- # Should we find a better home?
- '/tmp/portage',
- )
-
- @staticmethod
- @contextlib.contextmanager
- def LockDb(db):
- """Lock an account database.
-
- We use the same algorithm as shadow/user.eclass. This way we don't race
- and corrupt things in parallel.
- """
- lock = '%s.lock' % db
- _, tmplock = tempfile.mkstemp(prefix='%s.platform.' % lock)
-
- # First try forever to grab the lock.
- retry = lambda e: e.errno == errno.EEXIST
- # Retry quickly at first, but slow down over time.
- try:
- retry_util.GenericRetry(retry, 60, os.link, tmplock, lock, sleep=0.1)
- except Exception as e:
- raise Exception('Could not grab lock %s. %s' % (lock, e))
-
- # Yield while holding the lock, but try to clean it no matter what.
- try:
- os.unlink(tmplock)
- yield lock
- finally:
- os.unlink(lock)
-
- def SetupUser(self):
- """Propogate the user name<->id mapping from outside the chroot.
-
- Some unittests use getpwnam($USER), as does bash. If the account
- is not registered in the sysroot, they get back errors.
- """
- MAGIC_GECOS = 'Added by your friendly platform test helper; do not modify'
- # This is kept in sync with what sdk_lib/make_chroot.sh generates.
- SDK_GECOS = 'ChromeOS Developer'
-
- user, uid, gid, home = self.GetNonRootAccount()
- if user == 'nobody':
- return
-
- passwd_db = os.path.join(self.sysroot, 'etc', 'passwd')
- with self.LockDb(passwd_db):
- data = osutils.ReadFile(passwd_db)
- accts = data.splitlines()
- for acct in accts:
- passwd = acct.split(':')
- if passwd[0] == user:
- # Did the sdk make this account?
- if passwd[4] == SDK_GECOS:
- # Don't modify it (see below) since we didn't create it.
+ self.device = device
+ if not self.in_chroot:
return
- # Did we make this account?
- if passwd[4] != MAGIC_GECOS:
- raise RuntimeError('your passwd db (%s) has unmanaged acct %s' %
- (passwd_db, user))
+ sysroot_inf_cmd = ""
+ if self.inf_cmd:
+ sysroot_inf_cmd = os.path.join(
+ self.sysroot, self.inf_cmd.lstrip("/")
+ )
- # Maybe we should see if it needs to be updated? Like if they
- # changed UIDs? But we don't really check that elsewhere ...
- return
+ # Verify that inf_cmd, if given, exists.
+ if sysroot_inf_cmd and not os.path.exists(sysroot_inf_cmd):
+ raise GdbMissingInferiorError(
+ "Cannot find file %s (in sysroot)." % sysroot_inf_cmd
+ )
- acct = '%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s' % {
- 'name': user,
- 'uid': uid,
- 'gid': gid,
- 'gecos': MAGIC_GECOS,
- 'homedir': home,
- 'shell': '/bin/bash',
- }
- with open(passwd_db, 'a') as f:
- if data[-1] != '\n':
- f.write('\n')
- f.write('%s\n' % acct)
+ # Check to see if inf_cmd is stripped, and if so, check to see if debug file
+ # exists. If not, tell user and give them the option of quitting & getting
+ # the debug info.
+ if sysroot_inf_cmd:
+ stripped_info = cros_build_lib.run(
+ ["file", sysroot_inf_cmd], capture_output=True, encoding="utf-8"
+ ).stdout
+ if " not stripped" not in stripped_info:
+ debug_file = os.path.join(
+ self.sysroot, "usr/lib/debug", self.inf_cmd.lstrip("/")
+ )
+ debug_file += ".debug"
+ if not os.path.exists(debug_file):
+ equery = "equery-%s" % self.board
+ package = cros_build_lib.run(
+ [equery, "-q", "b", self.inf_cmd],
+ capture_output=True,
+ encoding="utf-8",
+ ).stdout
+ # pylint: disable=logging-not-lazy
+ logging.info(
+ self._MISSING_DEBUG_INFO_MSG
+ % {
+ "board": self.board,
+ "inf_cmd": self.inf_cmd,
+ "package": package,
+ "debug_file": debug_file,
+ }
+ )
+ answer = cros_build_lib.BooleanPrompt()
+ if not answer:
+ raise GdbEarlyExitError(
+ "Exiting early, at user request."
+ )
- def _FindRemoteProcess(self, device):
- """Find a named process (or a pid) running on a remote device."""
- if not self.remote_process_name and not self.pid:
- return
-
- if self.remote_process_name:
- # Look for a process with the specified name on the remote device; if
- # found, get its pid.
- pname = self.remote_process_name
- if pname == 'browser':
- all_chrome_pids = set(device.GetRunningPids(
- '/opt/google/chrome/chrome'))
- non_main_chrome_pids = set(device.GetRunningPids('type='))
- pids = list(all_chrome_pids - non_main_chrome_pids)
- elif pname == 'renderer' or pname == 'gpu-process':
- pids = device.GetRunningPids('type=%s'% pname)
- else:
- pids = device.GetRunningPids(pname)
-
- if pids:
- if len(pids) == 1:
- self.pid = pids[0]
+ # Set up qemu, if appropriate.
+ qemu_arch = qemu.Qemu.DetectArch(self._GDB, self.sysroot)
+ if qemu_arch is None:
+ self.framework = "ldso"
else:
- raise GdbTooManyPidsError('Multiple pids found for %s process: %s. '
- 'You must specify the correct pid.'
- % (pname, repr(pids)))
- else:
- raise GdbCannotFindRemoteProcessError('Cannot find pid for "%s" on %s' %
- (pname, self.remote))
+ self.framework = "qemu"
+ self.qemu = qemu.Qemu(self.sysroot, arch=qemu_arch)
- # Find full path for process, from pid (and verify pid).
- command = [
- 'readlink',
- '-e', '/proc/%s/exe' % self.pid,
- ]
- try:
- res = device.run(command, capture_output=True)
- if res.returncode == 0:
- self.inf_cmd = res.stdout.rstrip('\n')
- except cros_build_lib.RunCommandError:
- raise GdbCannotFindRemoteProcessError('Unable to find name of process '
- 'with pid %s on %s' %
- (self.pid, self.remote))
+ if self.remote:
+ # Verify cgdb flag info.
+ if self.cgdb:
+ if osutils.Which("cgdb") is None:
+ raise GdbMissingDebuggerError(
+ "Cannot find cgdb. Please install " "cgdb first."
+ )
- def GetCrossGdb(self):
- """Find the appropriate cross-version of gdb for the board."""
- toolchains = toolchain.GetToolchainsForBoard(self.board)
- tc = list(toolchain.FilterToolchains(toolchains, 'default', True))
- cross_gdb = tc[0] + '-gdb'
- if not osutils.Which(cross_gdb):
- raise GdbMissingDebuggerError('Cannot find %s; do you need to run '
- 'setup_board?' % cross_gdb)
- return cross_gdb
+ def RemoveSysrootPrefix(self, path):
+ """Returns the given path with any sysroot prefix removed."""
+ # If the sysroot is /, then the paths are already normalized.
+ if self.sysroot != "/" and path.startswith(self.sysroot):
+ path = path.replace(self.sysroot, "", 1)
+ return path
- def GetGdbInitCommands(self, inferior_cmd, device=None):
- """Generate list of commands with which to initialize the gdb session."""
- gdb_init_commands = []
+ @staticmethod
+ def GetNonRootAccount():
+ """Return details about the non-root account we want to use.
- if self.remote:
- sysroot_var = self.sysroot
- else:
- sysroot_var = '/'
+ Returns:
+ A tuple of (username, uid, gid, home).
+ """
+ return (
+ os.environ.get("SUDO_USER", "nobody"),
+ int(os.environ.get("SUDO_UID", "65534")),
+ int(os.environ.get("SUDO_GID", "65534")),
+ # Should we find a better home?
+ "/tmp/portage",
+ )
- gdb_init_commands = [
- 'set sysroot %s' % sysroot_var,
- 'set prompt %s' % self.prompt,
- ]
- if self.in_chroot:
- gdb_init_commands += [
- 'set solib-absolute-prefix %s' % sysroot_var,
- 'set solib-search-path %s' % sysroot_var,
- 'set debug-file-directory %s/usr/lib/debug' % sysroot_var,
- ]
+ @staticmethod
+ @contextlib.contextmanager
+ def LockDb(db):
+ """Lock an account database.
- if device:
- ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
+ We use the same algorithm as shadow/user.eclass. This way we don't race
+ and corrupt things in parallel.
+ """
+ lock = "%s.lock" % db
+ _, tmplock = tempfile.mkstemp(prefix="%s.platform." % lock)
- ssh_cmd.extend(['--', 'gdbserver'])
+ # First try forever to grab the lock.
+ retry = lambda e: e.errno == errno.EEXIST
+ # Retry quickly at first, but slow down over time.
+ try:
+ retry_util.GenericRetry(
+ retry, 60, os.link, tmplock, lock, sleep=0.1
+ )
+ except Exception as e:
+ raise Exception("Could not grab lock %s. %s" % (lock, e))
- if self.pid:
- ssh_cmd.extend(['--attach', 'stdio', str(self.pid)])
- target_type = 'remote'
- elif inferior_cmd:
- ssh_cmd.extend(['-', inferior_cmd])
- ssh_cmd.extend(self.inf_args)
- target_type = 'remote'
- else:
- ssh_cmd.extend(['--multi', 'stdio'])
- target_type = 'extended-remote'
+ # Yield while holding the lock, but try to clean it no matter what.
+ try:
+ os.unlink(tmplock)
+ yield lock
+ finally:
+ os.unlink(lock)
- ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
+ def SetupUser(self):
+ """Propogate the user name<->id mapping from outside the chroot.
- if self.in_chroot:
- if inferior_cmd:
- gdb_init_commands.append(
- 'file %s' % os.path.join(sysroot_var,
- inferior_cmd.lstrip(os.sep)))
- else:
- binary = self.GetSimpleChromeBinary()
- gdb_init_commands += [
- 'set debug-file-directory %s' % os.path.dirname(binary),
- 'file %s' % binary
+ Some unittests use getpwnam($USER), as does bash. If the account
+ is not registered in the sysroot, they get back errors.
+ """
+ MAGIC_GECOS = (
+ "Added by your friendly platform test helper; do not modify"
+ )
+ # This is kept in sync with what sdk_lib/make_chroot.sh generates.
+ SDK_GECOS = "ChromeOS Developer"
+
+ user, uid, gid, home = self.GetNonRootAccount()
+ if user == "nobody":
+ return
+
+ passwd_db = os.path.join(self.sysroot, "etc", "passwd")
+ with self.LockDb(passwd_db):
+ data = osutils.ReadFile(passwd_db)
+ accts = data.splitlines()
+ for acct in accts:
+ passwd = acct.split(":")
+ if passwd[0] == user:
+ # Did the sdk make this account?
+ if passwd[4] == SDK_GECOS:
+ # Don't modify it (see below) since we didn't create it.
+ return
+
+ # Did we make this account?
+ if passwd[4] != MAGIC_GECOS:
+ raise RuntimeError(
+ "your passwd db (%s) has unmanaged acct %s"
+ % (passwd_db, user)
+ )
+
+ # Maybe we should see if it needs to be updated? Like if they
+ # changed UIDs? But we don't really check that elsewhere ...
+ return
+
+ acct = (
+ "%(name)s:x:%(uid)s:%(gid)s:%(gecos)s:%(homedir)s:%(shell)s"
+ % {
+ "name": user,
+ "uid": uid,
+ "gid": gid,
+ "gecos": MAGIC_GECOS,
+ "homedir": home,
+ "shell": "/bin/bash",
+ }
+ )
+ with open(passwd_db, "a") as f:
+ if data[-1] != "\n":
+ f.write("\n")
+ f.write("%s\n" % acct)
+
+ def _FindRemoteProcess(self, device):
+ """Find a named process (or a pid) running on a remote device."""
+ if not self.remote_process_name and not self.pid:
+ return
+
+ if self.remote_process_name:
+ # Look for a process with the specified name on the remote device; if
+ # found, get its pid.
+ pname = self.remote_process_name
+ if pname == "browser":
+ all_chrome_pids = set(
+ device.GetRunningPids("/opt/google/chrome/chrome")
+ )
+ non_main_chrome_pids = set(device.GetRunningPids("type="))
+ pids = list(all_chrome_pids - non_main_chrome_pids)
+ elif pname == "renderer" or pname == "gpu-process":
+ pids = device.GetRunningPids("type=%s" % pname)
+ else:
+ pids = device.GetRunningPids(pname)
+
+ if pids:
+ if len(pids) == 1:
+ self.pid = pids[0]
+ else:
+ raise GdbTooManyPidsError(
+ "Multiple pids found for %s process: %s. "
+ "You must specify the correct pid."
+ % (pname, repr(pids))
+ )
+ else:
+ raise GdbCannotFindRemoteProcessError(
+ 'Cannot find pid for "%s" on %s' % (pname, self.remote)
+ )
+
+ # Find full path for process, from pid (and verify pid).
+ command = [
+ "readlink",
+ "-e",
+ "/proc/%s/exe" % self.pid,
]
+ try:
+ res = device.run(command, capture_output=True)
+ if res.returncode == 0:
+ self.inf_cmd = res.stdout.rstrip("\n")
+ except cros_build_lib.RunCommandError:
+ raise GdbCannotFindRemoteProcessError(
+ "Unable to find name of process "
+ "with pid %s on %s" % (self.pid, self.remote)
+ )
- gdb_init_commands.append('target %s | %s' % (target_type, ssh_cmd))
- else:
- if inferior_cmd:
- gdb_init_commands.append('file %s ' % inferior_cmd)
- gdb_init_commands.append('set args %s' % ' '.join(self.inf_args))
+ def GetCrossGdb(self):
+ """Find the appropriate cross-version of gdb for the board."""
+ toolchains = toolchain.GetToolchainsForBoard(self.board)
+ tc = list(toolchain.FilterToolchains(toolchains, "default", True))
+ cross_gdb = tc[0] + "-gdb"
+ if not osutils.Which(cross_gdb):
+ raise GdbMissingDebuggerError(
+ "Cannot find %s; do you need to run " "setup_board?" % cross_gdb
+ )
+ return cross_gdb
- return gdb_init_commands
+ def GetGdbInitCommands(self, inferior_cmd, device=None):
+ """Generate list of commands with which to initialize the gdb session."""
+ gdb_init_commands = []
- def RunRemote(self):
- """Handle remote debugging, via gdbserver & cross debugger."""
- with remote_access.ChromiumOSDeviceHandler(
- self.remote,
- port=self.remote_port,
- connect_settings=self.ssh_settings,
- ping=self.ping) as device:
+ if self.remote:
+ sysroot_var = self.sysroot
+ else:
+ sysroot_var = "/"
- self.VerifyAndFinishInitialization(device)
- gdb_cmd = self.cross_gdb
+ gdb_init_commands = [
+ "set sysroot %s" % sysroot_var,
+ "set prompt %s" % self.prompt,
+ ]
+ if self.in_chroot:
+ gdb_init_commands += [
+ "set solib-absolute-prefix %s" % sysroot_var,
+ "set solib-search-path %s" % sysroot_var,
+ "set debug-file-directory %s/usr/lib/debug" % sysroot_var,
+ ]
- gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
- gdb_args = ['--quiet'] + ['--eval-command=%s' % x for x in gdb_commands]
- gdb_args += self.gdb_args
+ if device:
+ ssh_cmd = device.GetAgent().GetSSHCommand(self.ssh_settings)
- if self.cgdb:
- gdb_args = ['-d', gdb_cmd, '--'] + gdb_args
- gdb_cmd = 'cgdb'
+ ssh_cmd.extend(["--", "gdbserver"])
- cros_build_lib.run(
- [gdb_cmd] + gdb_args,
- ignore_sigint=True,
- print_cmd=True,
- cwd=self.sysroot)
+ if self.pid:
+ ssh_cmd.extend(["--attach", "stdio", str(self.pid)])
+ target_type = "remote"
+ elif inferior_cmd:
+ ssh_cmd.extend(["-", inferior_cmd])
+ ssh_cmd.extend(self.inf_args)
+ target_type = "remote"
+ else:
+ ssh_cmd.extend(["--multi", "stdio"])
+ target_type = "extended-remote"
- def Run(self):
- """Runs the debugger in a proper environment (e.g. qemu)."""
+ ssh_cmd = cros_build_lib.CmdToStr(ssh_cmd)
- self.VerifyAndFinishInitialization(None)
- self.SetupUser()
- if self.framework == 'qemu':
- self.qemu.Install(self.sysroot)
- self.qemu.RegisterBinfmt()
+ if self.in_chroot:
+ if inferior_cmd:
+ gdb_init_commands.append(
+ "file %s"
+ % os.path.join(sysroot_var, inferior_cmd.lstrip(os.sep))
+ )
+ else:
+ binary = self.GetSimpleChromeBinary()
+ gdb_init_commands += [
+ "set debug-file-directory %s" % os.path.dirname(binary),
+ "file %s" % binary,
+ ]
- for mount in self._BIND_MOUNT_PATHS:
- path = os.path.join(self.sysroot, mount)
- osutils.SafeMakedirs(path)
- osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)
+ gdb_init_commands.append("target %s | %s" % (target_type, ssh_cmd))
+ else:
+ if inferior_cmd:
+ gdb_init_commands.append("file %s " % inferior_cmd)
+ gdb_init_commands.append(
+ "set args %s" % " ".join(self.inf_args)
+ )
- gdb_cmd = self._GDB
- inferior_cmd = self.inf_cmd
+ return gdb_init_commands
- gdb_argv = self.gdb_args[:]
- if gdb_argv:
- gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
- # Some programs expect to find data files via $CWD, so doing a chroot
- # and dropping them into / would make them fail.
- cwd = self.RemoveSysrootPrefix(os.getcwd())
+ def RunRemote(self):
+ """Handle remote debugging, via gdbserver & cross debugger."""
+ with remote_access.ChromiumOSDeviceHandler(
+ self.remote,
+ port=self.remote_port,
+ connect_settings=self.ssh_settings,
+ ping=self.ping,
+ ) as device:
- os.chroot(self.sysroot)
- os.chdir(cwd)
- # The TERM the user is leveraging might not exist in the sysroot.
- # Force a reasonable default that supports standard color sequences.
- os.environ['TERM'] = 'ansi'
- # Some progs want this like bash else they get super confused.
- os.environ['PWD'] = cwd
- if not self.run_as_root:
- _, uid, gid, home = self.GetNonRootAccount()
- os.setgid(gid)
- os.setuid(uid)
- os.environ['HOME'] = home
+ self.VerifyAndFinishInitialization(device)
+ gdb_cmd = self.cross_gdb
- gdb_commands = self.GetGdbInitCommands(inferior_cmd)
+ gdb_commands = self.GetGdbInitCommands(self.inf_cmd, device)
+ gdb_args = ["--quiet"] + [
+ "--eval-command=%s" % x for x in gdb_commands
+ ]
+ gdb_args += self.gdb_args
- gdb_args = [gdb_cmd, '--quiet'] + ['--eval-command=%s' % x
- for x in gdb_commands]
- gdb_args += self.gdb_args
+ if self.cgdb:
+ gdb_args = ["-d", gdb_cmd, "--"] + gdb_args
+ gdb_cmd = "cgdb"
- os.execvp(gdb_cmd, gdb_args)
+ cros_build_lib.run(
+ [gdb_cmd] + gdb_args,
+ ignore_sigint=True,
+ print_cmd=True,
+ cwd=self.sysroot,
+ )
+
+ def Run(self):
+ """Runs the debugger in a proper environment (e.g. qemu)."""
+
+ self.VerifyAndFinishInitialization(None)
+ self.SetupUser()
+ if self.framework == "qemu":
+ self.qemu.Install(self.sysroot)
+ self.qemu.RegisterBinfmt()
+
+ for mount in self._BIND_MOUNT_PATHS:
+ path = os.path.join(self.sysroot, mount)
+ osutils.SafeMakedirs(path)
+ osutils.Mount("/" + mount, path, "none", osutils.MS_BIND)
+
+ gdb_cmd = self._GDB
+ inferior_cmd = self.inf_cmd
+
+ gdb_argv = self.gdb_args[:]
+ if gdb_argv:
+ gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
+ # Some programs expect to find data files via $CWD, so doing a chroot
+ # and dropping them into / would make them fail.
+ cwd = self.RemoveSysrootPrefix(os.getcwd())
+
+ os.chroot(self.sysroot)
+ os.chdir(cwd)
+ # The TERM the user is leveraging might not exist in the sysroot.
+ # Force a reasonable default that supports standard color sequences.
+ os.environ["TERM"] = "ansi"
+ # Some progs want this like bash else they get super confused.
+ os.environ["PWD"] = cwd
+ if not self.run_as_root:
+ _, uid, gid, home = self.GetNonRootAccount()
+ os.setgid(gid)
+ os.setuid(uid)
+ os.environ["HOME"] = home
+
+ gdb_commands = self.GetGdbInitCommands(inferior_cmd)
+
+ gdb_args = [gdb_cmd, "--quiet"] + [
+ "--eval-command=%s" % x for x in gdb_commands
+ ]
+ gdb_args += self.gdb_args
+
+ os.execvp(gdb_cmd, gdb_args)
def _ReExecuteIfNeeded(argv, ns_net=False, ns_pid=False):
- """Re-execute gdb as root.
+ """Re-execute gdb as root.
- We often need to do things as root, so make sure we're that. Like chroot
- for proper library environment or do bind mounts.
+ We often need to do things as root, so make sure we're that. Like chroot
+ for proper library environment or do bind mounts.
- Also unshare the mount namespace so as to ensure that doing bind mounts for
- tests don't leak out to the normal chroot. Also unshare the UTS namespace
- so changes to `hostname` do not impact the host.
- """
- if osutils.IsNonRootUser():
- cmd = ['sudo', '-E', '--'] + argv
- os.execvp(cmd[0], cmd)
- else:
- namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
+ Also unshare the mount namespace so as to ensure that doing bind mounts for
+ tests don't leak out to the normal chroot. Also unshare the UTS namespace
+ so changes to `hostname` do not impact the host.
+ """
+ if osutils.IsNonRootUser():
+ cmd = ["sudo", "-E", "--"] + argv
+ os.execvp(cmd[0], cmd)
+ else:
+ namespaces.SimpleUnshare(net=ns_net, pid=ns_pid)
def FindInferior(arg_list):
- """Look for the name of the inferior (to be debugged) in arg list."""
+ """Look for the name of the inferior (to be debugged) in arg list."""
- program_name = ''
- new_list = []
- for item in arg_list:
- if item[0] == '-':
- new_list.append(item)
- elif not program_name:
- program_name = item
- else:
- raise RuntimeError('Found multiple program names: %s %s'
- % (program_name, item))
+ program_name = ""
+ new_list = []
+ for item in arg_list:
+ if item[0] == "-":
+ new_list.append(item)
+ elif not program_name:
+ program_name = item
+ else:
+ raise RuntimeError(
+ "Found multiple program names: %s %s" % (program_name, item)
+ )
- return program_name, new_list
+ return program_name, new_list
def main(argv):
- parser = commandline.ArgumentParser(description=__doc__)
+ parser = commandline.ArgumentParser(description=__doc__)
- parser.add_argument('--board', default=None,
- help='board to debug for')
- parser.add_argument('-g', '--gdb_args', action='append', default=[],
- help='Arguments to gdb itself. If multiple arguments are'
- " passed, each argument needs a separate '-g' flag.")
- parser.add_argument(
- '--remote', default=None,
- type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
- help='Remote device on which to run the binary. Use'
- ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
- ' already running local virtual machine.')
- parser.add_argument('--pid', default='',
- help='Process ID of the (already) running process on the'
- ' remote device to which to attach.')
- parser.add_argument('--remote_pid', dest='pid', default='',
- help='Deprecated alias for --pid.')
- parser.add_argument('--no-ping', dest='ping', default=True,
- action='store_false',
- help='Do not ping remote before attempting to connect.')
- parser.add_argument('--attach', dest='attach_name', default='',
- help='Name of existing process to which to attach, on'
- ' remote device (remote debugging only). "--attach'
- ' browser" will find the main chrome browser process;'
- ' "--attach renderer" will find a chrome renderer'
- ' process; "--attach gpu-process" will find the chrome'
- ' gpu process.')
- parser.add_argument('--cgdb', default=False,
- action='store_true',
- help='Use cgdb curses interface rather than plain gdb.'
- 'This option is only valid for remote debugging.')
- parser.add_argument('inf_args', nargs=argparse.REMAINDER,
- help='Arguments for gdb to pass to the program being'
- ' debugged. These are positional and must come at the end'
- ' of the command line. This will not work if attaching'
- ' to an already running program.')
- parser.add_argument('--binary', default='',
- help='full path to the binary being debuged.'
- ' This is only useful for simple chrome.'
- ' An example is --bianry /home/out_falco/chrome.')
+ parser.add_argument("--board", default=None, help="board to debug for")
+ parser.add_argument(
+ "-g",
+ "--gdb_args",
+ action="append",
+ default=[],
+ help="Arguments to gdb itself. If multiple arguments are"
+ " passed, each argument needs a separate '-g' flag.",
+ )
+ parser.add_argument(
+ "--remote",
+ default=None,
+ type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
+ help="Remote device on which to run the binary. Use"
+ ' "--remote=localhost:9222" to debug in a ChromeOS image in an'
+ " already running local virtual machine.",
+ )
+ parser.add_argument(
+ "--pid",
+ default="",
+ help="Process ID of the (already) running process on the"
+ " remote device to which to attach.",
+ )
+ parser.add_argument(
+ "--remote_pid",
+ dest="pid",
+ default="",
+ help="Deprecated alias for --pid.",
+ )
+ parser.add_argument(
+ "--no-ping",
+ dest="ping",
+ default=True,
+ action="store_false",
+ help="Do not ping remote before attempting to connect.",
+ )
+ parser.add_argument(
+ "--attach",
+ dest="attach_name",
+ default="",
+ help="Name of existing process to which to attach, on"
+ ' remote device (remote debugging only). "--attach'
+ ' browser" will find the main chrome browser process;'
+ ' "--attach renderer" will find a chrome renderer'
+ ' process; "--attach gpu-process" will find the chrome'
+ " gpu process.",
+ )
+ parser.add_argument(
+ "--cgdb",
+ default=False,
+ action="store_true",
+ help="Use cgdb curses interface rather than plain gdb."
+ "This option is only valid for remote debugging.",
+ )
+ parser.add_argument(
+ "inf_args",
+ nargs=argparse.REMAINDER,
+ help="Arguments for gdb to pass to the program being"
+ " debugged. These are positional and must come at the end"
+ " of the command line. This will not work if attaching"
+ " to an already running program.",
+ )
+ parser.add_argument(
+ "--binary",
+ default="",
+ help="full path to the binary being debuged."
+ " This is only useful for simple chrome."
+ " An example is --bianry /home/out_falco/chrome.",
+ )
- options = parser.parse_args(argv)
- options.Freeze()
+ options = parser.parse_args(argv)
+ options.Freeze()
- gdb_args = []
- inf_args = []
- inf_cmd = ''
+ gdb_args = []
+ inf_args = []
+ inf_cmd = ""
- if options.inf_args:
- inf_cmd = options.inf_args[0]
- inf_args = options.inf_args[1:]
+ if options.inf_args:
+ inf_cmd = options.inf_args[0]
+ inf_args = options.inf_args[1:]
- if options.gdb_args:
- gdb_args = options.gdb_args
+ if options.gdb_args:
+ gdb_args = options.gdb_args
- if inf_cmd:
- fname = os.path.join(
- build_target_lib.get_default_sysroot_path(options.board),
- inf_cmd.lstrip('/'))
- if not os.path.exists(fname):
- cros_build_lib.Die('Cannot find program %s.' % fname)
- else:
- if inf_args:
- parser.error('Cannot specify arguments without a program.')
+ if inf_cmd:
+ fname = os.path.join(
+ build_target_lib.get_default_sysroot_path(options.board),
+ inf_cmd.lstrip("/"),
+ )
+ if not os.path.exists(fname):
+ cros_build_lib.Die("Cannot find program %s." % fname)
+ else:
+ if inf_args:
+ parser.error("Cannot specify arguments without a program.")
- if inf_args and (options.pid or options.attach_name):
- parser.error('Cannot pass arguments to an already'
- ' running process (--remote-pid or --attach).')
+ if inf_args and (options.pid or options.attach_name):
+ parser.error(
+ "Cannot pass arguments to an already"
+ " running process (--remote-pid or --attach)."
+ )
- if options.remote:
- if options.attach_name and options.attach_name == 'browser':
- inf_cmd = '/opt/google/chrome/chrome'
- else:
- if options.cgdb:
- parser.error('--cgdb option can only be used with remote debugging.')
- if options.pid:
- parser.error('Must specify a remote device (--remote) if you want '
- 'to attach to a remote pid.')
- if options.attach_name:
- parser.error('Must specify remote device (--remote) when using'
- ' --attach option.')
- if options.binary:
- if not os.path.exists(options.binary):
- parser.error('%s does not exist.' % options.binary)
-
- # Once we've finished checking args, make sure we're root.
- if not options.remote:
- _ReExecuteIfNeeded([sys.argv[0]] + argv)
-
- gdb = BoardSpecificGdb(options.board, gdb_args, inf_cmd, inf_args,
- options.remote, options.pid, options.attach_name,
- options.cgdb, options.ping, options.binary)
-
- try:
if options.remote:
- gdb.RunRemote()
+ if options.attach_name and options.attach_name == "browser":
+ inf_cmd = "/opt/google/chrome/chrome"
else:
- gdb.Run()
+ if options.cgdb:
+ parser.error(
+ "--cgdb option can only be used with remote debugging."
+ )
+ if options.pid:
+ parser.error(
+ "Must specify a remote device (--remote) if you want "
+ "to attach to a remote pid."
+ )
+ if options.attach_name:
+ parser.error(
+ "Must specify remote device (--remote) when using"
+ " --attach option."
+ )
+ if options.binary:
+ if not os.path.exists(options.binary):
+ parser.error("%s does not exist." % options.binary)
- except GdbException as e:
- if options.debug:
- raise
- else:
- raise cros_build_lib.Die(str(e))
+ # Once we've finished checking args, make sure we're root.
+ if not options.remote:
+ _ReExecuteIfNeeded([sys.argv[0]] + argv)
+
+ gdb = BoardSpecificGdb(
+ options.board,
+ gdb_args,
+ inf_cmd,
+ inf_args,
+ options.remote,
+ options.pid,
+ options.attach_name,
+ options.cgdb,
+ options.ping,
+ options.binary,
+ )
+
+ try:
+ if options.remote:
+ gdb.RunRemote()
+ else:
+ gdb.Run()
+
+ except GdbException as e:
+ if options.debug:
+ raise
+ else:
+ raise cros_build_lib.Die(str(e))