|  | #!/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." | 
|  |  | 
|  | # 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} | 
|  |  | 
|  | # 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" | 
|  |  | 
|  | run_remote_command () | 
|  | { | 
|  | ssh -i ${TMP_DIR}/testing_rsa \ | 
|  | ${REMOTE_SSH_FLAGS} root@${FLAGS_remote} ${VM_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. | 
|  |  | 
|  | if [ ! -x "${BOARD_ROOT}${FLAGS_remote_file}" ]; then | 
|  | echo | 
|  | warn "${BOARD_ROOT}${FLAGS_remote_file} does not exist on your local" | 
|  | warn "machine or is not executable.  You may need to re-run build_packages" | 
|  | warn "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 | 
|  | } | 
|  |  | 
|  | 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}" | 
|  | 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. | 
|  |  | 
|  | 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 = "target remote " + remote_ip_address + ":" + remote_port | 
|  | 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... | 
|  |  | 
|  | VM_PORT="" | 
|  | PORT_FORWARDING="" | 
|  |  | 
|  | if [[ "${FLAGS_remote}" == "localhost" || | 
|  | "${FLAGS_remote}" == "127.0.0.1" ]] ; then | 
|  | VM_PORT=" -p 9222" | 
|  | PORT_FORWARDING=" -L ${FLAGS_port}:localhost:${FLAGS_port}" | 
|  | else | 
|  | 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 "${VM_PORT}" ]] ; 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} ${VM_PORT} ${PORT_FORWARDING} & | 
|  | SSH_PID=$! | 
|  | fi | 
|  |  | 
|  | start_remote_gdbserver | 
|  |  | 
|  | echo "gdbserver is now running remotely.  Output will be written to " | 
|  | echo "/tmp/gdbserver.out on your remote device." | 
|  |  | 
|  | 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. | 
|  |  | 
|  | ${CHOST}-gdb | 
|  |  | 
|  | cleanup |