blob: b0a309c663f7abfde6c8528f5c8f65dfe0d7b993 [file] [log] [blame]
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""cros debug: Debug the applications on the target device."""
import logging
import os
from chromite.cli import command
from chromite.lib import commandline
from chromite.lib import cros_build_lib
from chromite.lib import remote_access
class DebugCommand(command.CliCommand):
"""Use GDB to debug a process running on the target device.
This command starts a GDB session to debug a remote process running on the
target device. The remote process can either be an existing process or newly
started by calling this command.
This command can also be used to find out information about all running
processes of an executable on the target device.
EPILOG = """
To list all running processes of an executable:
cros debug device --list --exe=/path/to/executable
To debug an executable:
cros debug device --exe=/path/to/executable
To debug a process by its pid:
cros debug device --pid=1234
def __init__(self, options):
"""Initialize DebugCommand."""
# SSH connection settings.
self.ssh_hostname = None
self.ssh_port = None
self.ssh_username = None
self.ssh_private_key = None
# The board name of the target device.
self.board = None
# Settings of the process to debug.
self.list = False
self.exe = None = None
# The command for starting gdb.
self.gdb_cmd = None
def AddParser(cls, parser):
"""Add parser arguments."""
super(cls, DebugCommand).AddParser(parser)
cls.AddDeviceArgument(parser, positional=True)
'--board', default=None, help='The board to use. By default it is '
'automatically detected. You can override the detected board with '
'this option.')
'--private-key', type='path', default=None,
help='SSH identity file (private key).')
'-l', '--list', action='store_true', default=False,
help='List running processes of the executable on the target device.')
'--exe', help='Full path of the executable on the target device.')
'-p', '--pid', type=int,
help='The pid of the process on the target device.')
def ProcessOptions(cls, parser, options):
"""Post process options."""
if not ( or options.exe):
parser.error('Must use --exe or --pid to specify the process to debug.')
if and (options.list or options.exe):
parser.error('--list and --exe are disallowed when --pid is used.')
if not options.exe.startswith('/'):
parser.error('--exe must have a full pathname.')
def _ListProcesses(self, device, pids):
"""Provided with a list of pids, print out information of the processes."""
if not pids:
'No running process of %s on device %s', self.exe, self.ssh_hostname)
result = device.BaseRunCommand(['ps', 'aux'])
lines = result.stdout.splitlines()
header, procs = lines[0], lines[1:]
info = os.linesep.join([p for p in procs if int(p.split()[1]) in pids])
except ValueError:
cros_build_lib.Die('Parsing output failed:\n%s', result.stdout)
print('\nList running processes of %s on device %s:\n%s\n%s' %
(self.exe, self.ssh_hostname, header, info))
except cros_build_lib.RunCommandError:
'Failed to find any running process on device %s', self.ssh_hostname)
def _DebugNewProcess(self):
"""Start a new process on the target device and attach gdb to it."""
'Ready to start and debug %s on device %s', self.exe, self.ssh_hostname) + ['--remote_file', self.exe])
def _DebugRunningProcess(self, pid):
"""Start gdb and attach it to the remote running process with |pid|."""
'Ready to debug process %d on device %s', pid, self.ssh_hostname) + ['--remote_pid', str(pid)])
def _ReadOptions(self):
"""Process options and set variables."""
if self.options.device:
self.ssh_hostname = self.options.device.hostname
self.ssh_username = self.options.device.username
self.ssh_port = self.options.device.port
self.ssh_private_key = self.options.private_key
self.list = self.options.list
self.exe = self.options.exe =
def Run(self):
"""Run cros debug."""
with remote_access.ChromiumOSDeviceHandler(
self.ssh_hostname, port=self.ssh_port, username=self.ssh_username,
private_key=self.ssh_private_key) as device:
self.board = cros_build_lib.GetBoard(device_board=device.board,
strict=True)'Board is %s', self.board)
self.gdb_cmd = [
'gdb_remote', '--ssh',
'--board', self.board,
'--remote', self.ssh_hostname,
if self.ssh_port:
self.gdb_cmd.extend(['--ssh_port', str(self.ssh_port)])
logging.debug('Executable path is %s', self.exe)
if not device.IsFileExecutable(self.exe):
'File path "%s" does not exist or is not executable on device %s',
self.exe, self.ssh_hostname)
pids = device.GetRunningPids(self.exe)
self._ListProcesses(device, pids)
if self.list:
# If '--list' flag is on, do not launch GDB.
if pids:
choices = ['Start a new process under GDB']
idx = cros_build_lib.GetChoice(
'Please select the process pid to debug (select [0] to start a '
'new process):', choices)
if idx == 0:
self._DebugRunningProcess(pids[idx - 1])