blob: e093afa5d75e0f7edad6334446b2e65403aeda57 [file] [log] [blame] [edit]
#!/bin/bash
# Copyright (c) 2011 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.
. /usr/lib/crosutils/common.sh || exit 1
# Flags
DEFINE_string attach "" \
"pgrep for the given command - 'browser' finds the chrome browser process"
DEFINE_string board "${DEFAULT_BOARD}" \
"The board to run debugger on."
DEFINE_string remote "localhost" \
"The IP address of the remote device."
DEFINE_integer port 1234 \
"The port number to use for connecting to remote device."
DEFINE_integer remote_pid 0 \
"Process ID of the running process on the remote device to which to attach."
DEFINE_string remote_file "" \
"Full pathname of the file to be debugged on the remote device."
DEFINE_string remote_args "" \
"Command line arguments to pass to the executable on the remote device."
DEFINE_boolean cgdb ${FLAGS_FALSE} \
"Use cgdb curses interface rather than plain gdb."
DEFINE_boolean ssh ${FLAGS_FALSE} \
"Use ssh stdio forwarding instead of TCP to connect to gdbserver."
DEFINE_integer ssh_port 0 \
"The ssh port number to use for ssh connections to the remote device."
# Parse command line
FLAGS_HELP="usage: $0 [flags]"
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
check_flags_only_and_allow_null_arg "$@" && set --
BOARD=${FLAGS_board}
BOARD_ROOT=/build/${BOARD}
if [ ${FLAGS_cgdb} -eq ${FLAGS_TRUE} ] && ! type -P cgdb >/dev/null; then
die "Please install cgdb first"
fi
# Derive toolchain from $BOARD
CHOST=$(portageq-${BOARD} envvar CHOST)
TMP_DIR=""
SSH_PID=0
CLEAN=0
GDBINIT_FILE=~/.gdbinit
cleanup ()
{
if [ ${CLEAN} -eq 0 ] ; then
rm -rf ${TMP_DIR}
if [ ${SSH_PID} -ne 0 ] ; then
kill ${SSH_PID}
fi
CLEAN=1
fi
rm -rf ${GDBINIT_FILE}
}
# Create a temporary location in which to copy testing_rsa file; make
# sure to clean it up when we exit.
trap 'cleanup' EXIT INT TERM
TMP_DIR=$(mktemp -d)
cp ${SCRIPTS_DIR}/mod_for_test_scripts/ssh_keys/testing_rsa \
${TMP_DIR}/testing_rsa
chmod 0600 ${TMP_DIR}/testing_rsa
REMOTE_SSH_FLAGS=" -o StrictHostKeyChecking=no -o CheckHostIP=no\
-o BatchMode=yes"
SSH_PORT=""
if [[ ${FLAGS_ssh_port} -ne 0 ]] ; then
SSH_PORT=" -p ${FLAGS_ssh_port}"
fi
run_remote_command ()
{
ssh -i ${TMP_DIR}/testing_rsa \
${REMOTE_SSH_FLAGS} root@${FLAGS_remote} ${SSH_PORT} \
"$@"
}
ssh_to_remote_machine ()
{
local command=$1
local error_msg=$2
if ! run_remote_command "${command}" ; then
die "${error_msg}"
fi
}
validate_command_options ()
{
# Verify we have at least a board, toolchain and remote file.
if [[ -z "${BOARD}" ]] ; then
die "--board is required."
fi
if [[ -z "${CHOST}" ]] ; then
die "Unable to determine correct toolchain from board."
fi
if [[ -z "${FLAGS_remote_pid}" ]] ; then
if [[ -z "${FLAGS_remote_file}" ]] ; then
if [[ -z "${FLAGS_attach}" ]] ; then
die "--remote_file is required."
fi
fi
fi
# Verify that the correct cross-gdb has been built first!
if [[ ! -f /usr/bin/${CHOST}-gdb ]] ; then
die "${CHOST}-gdb does not exist. Please run setup_board."
fi
# Verify that the IP Address is currently active.
if [[ -z "${FLAGS_remote}" ]] ; then
die "No IP address specified."
fi
echo "Verifying IP address ${FLAGS_remote} (this will take a few\
seconds)..."
if ! ping -c 3 -q ${FLAGS_remote} > /dev/null ; then
die "${FLAGS_remote} is not currently available."
fi
if [[ -n "${FLAGS_attach}" ]]; then
if [[ "${FLAGS_attach}" == "browser" ]]; then
FLAGS_remote_pid=$(run_remote_command \
"pstree -p|grep session_manager|cut -d\( -f3 | cut -d\) -f1")
if [ -z "${FLAGS_remote_pid}" ]; then
die "Unable to find browser process"
fi
else
FLAGS_remote_pid=$(run_remote_command "pgrep -f '${FLAGS_attach}'")
local count=$(echo ${FLAGS_remote_pid} | wc -w)
if [ ${count} -eq 0 ]; then
die "No process matching ${FLAGS_attach}"
elif [ ${count} -gt 1 ]; then
error "Multiple (${count}) processes matching \"${FLAGS_attach}\":"
local pids=$(echo "${FLAGS_remote_pid}" | tr '\n' ' ')
run_remote_command "ps ${pids}"
exit 1
fi
fi
fi
if [[ ${FLAGS_remote_pid} -ne 0 ]] ; then
local ssh_cmd="readlink -e /proc/${FLAGS_remote_pid}/exe"
local err_msg="${FLAGS_remote_pid} is not a valid PID on\
${FLAGS_remote}"
FLAGS_remote_file=$(run_remote_command "${ssh_cmd}")
if [[ $? -ne 0 ]] ; then
die "${err_msg}"
fi
fi
if [[ ! -z "${FLAGS_remote_file}" ]] ; then
if [[ ${FLAGS_remote_file:0:1} != '/' ]] ; then
die "--remote_file must contain full pathname."
fi
fi
# Verify that the debug version of the remote file exists.
local local_file="${BOARD_ROOT}${FLAGS_remote_file}"
if [[ ! -x "${local_file}" ]]; then
echo
warn "${local_file} does not exist on your local machine or is not"
warn "executable. You may need to re-run build_packages before attempting to debug."
echo
read -p "Do you want to stop now? [y/N] " y_or_n
case "${y_or_n}" in
y|Y) exit 1 ;;
*) ;;
esac
fi
local local_symbols="${BOARD_ROOT}/usr/lib/debug${FLAGS_remote_file}.debug"
local file_info="$(file "${local_file}")"
if [[ "${file_info}" != *"not stripped"* && ! -e "${local_symbols}" ]]; then
echo
local package="$(qfile-${BOARD} -C -q "${FLAGS_remote_file}")"
warn "${local_file} is stripped and ${local_symbols} does not exist"
warn "on your local machine. The debug symbols for that package may not be"
warn "installed."
warn "To install the debug symbols for ${package} only, run:"
warn " cros_install_debug_syms --board=${BOARD} ${package}"
warn "To install the debug symbols for all available packages, run:"
warn " cros_install_debug_syms --board=${BOARD} --all"
echo
read -p "Do you want to stop now? [y/N] " y_or_n
case "${y_or_n}" in
y|Y) exit 1 ;;
*) ;;
esac
fi
}
setup_remote_iptable ()
{
# Update the iptables on the remote device
local ssh_cmd="/sbin/iptables -A INPUT -p tcp --dport ${FLAGS_port}\
-j ACCEPT"
local err_msg="Unable to add port to iptables."
ssh_to_remote_machine "${ssh_cmd}" "${err_msg}"
}
start_remote_gdbserver ()
{
# Start gdbserver on the remote device
local gdbserver_cmd="gdbserver :${FLAGS_port} ${FLAGS_remote_file} ${FLAGS_remote_args}"
if [[ ${FLAGS_remote_pid} -ne 0 ]] ; then
gdbserver_cmd="gdbserver --attach :${FLAGS_port} ${FLAGS_remote_pid}"
fi
echo "Starting up gdbserver on your remote device."
local ssh_cmd="nohup ${gdbserver_cmd} > /tmp/gdbserver.out 2>&1 &"
local err_msg="Unable to ssh into root@${FLAGS_remote}."
ssh_to_remote_machine "${ssh_cmd}" "${err_msg}"
}
generate_gdbinit_file ()
{
# Create board-and-notebook-specific .gdbinit file.
local remote_cmd='"target remote " + remote_ip_address + ":" + remote_port'
if [ ${FLAGS_ssh} -eq ${FLAGS_TRUE} ]; then
local gdbserver_cmd="gdbserver"
if [[ ${FLAGS_remote_pid} -ne 0 ]] ; then
gdbserver_cmd+=" --attach - ${FLAGS_remote_pid}"
else
gdbserver_cmd+=" - ${FLAGS_remote_file} ${FLAGS_remote_args}"
fi
remote_cmd="\"target remote | ssh -T -i ${TMP_DIR}/testing_rsa \
${REMOTE_SSH_FLAGS} -o TCPKeepAlive=no -o UserKnownHostsFile=/dev/null \
root@${FLAGS_remote} ${SSH_PORT} ${gdbserver_cmd}\""
fi
cat <<-EOF > ${GDBINIT_FILE}
define remote_connect
set \$file="${FLAGS_remote_file}"
python import os
python filename = str (gdb.parse_and_eval ("\$file"))
python fullname = os.path.join ("${BOARD_ROOT}", filename)
python file_command = "file " + fullname
python gdb.execute (file_command)
python remote_ip_address = "${FLAGS_remote}"
python remote_port = "${FLAGS_port}"
python remote_cmd = ${remote_cmd}
python gdb.execute (remote_cmd)
end
set sysroot $BOARD_ROOT
set debug-file-directory $BOARD_ROOT/usr/lib/debug
remote_connect
EOF
}
# Some special set up is required for accessing the VM on a local machine...
PORT_FORWARDING=""
if [[ "${FLAGS_remote}" == "localhost" ||
"${FLAGS_remote}" == "127.0.0.1" ]] ; then
if [[ -z ${SSH_PORT} ]] ; then
SSH_PORT=" -p 9222"
fi
PORT_FORWARDING=" -L ${FLAGS_port}:localhost:${FLAGS_port}"
elif [ ${FLAGS_ssh} -eq ${FLAGS_FALSE} ]; then
setup_remote_iptable
fi
validate_command_options
# If accessing the VM on the local machine, need a second ssh session open,
# for port forwarding, so gdb can find gdbserver...
SSHD_PID=0
if [[ -n "${PORT_FORWARDING}" ]] ; then
# Call ssh directly rather than using 'ssh_to_remote_machine' because
# too many things about this particular call are different.
ssh -i ${TMP_DIR}/testing_rsa -N \
${REMOTE_SSH_FLAGS} \
root@${FLAGS_remote} ${SSH_PORT} ${PORT_FORWARDING} &
SSH_PID=$!
fi
if [ ${FLAGS_ssh} -eq ${FLAGS_FALSE} ]; then
start_remote_gdbserver
echo "gdbserver is now running remotely. Output will be written to "
echo "/tmp/gdbserver.out on your remote device."
fi
generate_gdbinit_file
echo "Some helpful GDB commands:"
echo "directory <path> -- causes path to be searched for source files."
echo "info functions <regexp> -- find function matching given regexp."
echo
echo "Some helpful gdb_remote specific commands:"
echo "remote_connect -- reestablish remote connection."
echo
# Start gdb on local machine.
if [ ${FLAGS_cgdb} -eq ${FLAGS_TRUE} ]; then
cgdb -d ${CHOST}-gdb
else
${CHOST}-gdb
fi
cleanup