Chromium Code Reviews| Index: build/android/adb_gdb |
| diff --git a/build/android/adb_gdb b/build/android/adb_gdb |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..885c46ce26d4dd61cd164a3323ed3c871eb18bc6 |
| --- /dev/null |
| +++ b/build/android/adb_gdb |
| @@ -0,0 +1,906 @@ |
| +#!/bin/bash |
| +# |
| +# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| +# |
| + |
| +# A generic script used to attach to a running Chromium process and |
| +# debug it. Most users should not use this directly, but one of the |
| +# wrapper scripts like adb_gdb_content_shell, or adb_gdb_drt |
| +# |
| +# Use --help to print full usage instructions. |
| +# |
| + |
| +PROGNAME=$(basename "$0") |
| +PROGDIR=$(dirname "$0") |
| + |
| +# Location of Chromium-top-level sources. |
| +CHROMIUM_SRC=$(cd "$PROGDIR"/../.. && pwd 2>/dev/null) |
| + |
| +TMPDIR= |
| +GDBSERVER_PIDFILE= |
| +TARGET_GDBSERVER= |
| + |
| +clean_exit () { |
|
bulach
2012/10/17 14:20:04
these functions here (up to 54:log) have the "{" i
digit1
2012/10/18 12:33:26
Done.
|
| + if [ "$TMPDIR" ]; then |
| + GDBSERVER_PID=$(cat $GDBSERVER_PIDFILE 2>/dev/null) |
| + if [ "$GDBSERVER_PID" ]; then |
| + log "Killing background gdbserver process: $GDBSERVER_PID" |
| + kill -9 $GDBSERVER_PID >/dev/null 2>&1 |
| + fi |
| + if [ "$TARGET_GDBSERVER" ]; then |
| + log "Removing target gdbserver binary: $TARGET_GDBSERVER." |
| + "$ADB" shell rm "$TARGET_GDBSERVER" >/dev/null 2>&1 |
| + fi |
| + log "Cleaning up: $TMPDIR" |
| + rm -rf "$TMPDIR" |
| + fi |
| + exit $1 |
| +} |
| + |
| +# Ensure clean exit on Ctrl-C. |
| +trap "clean_exit 1" INT |
| + |
| +panic () { |
| + echo "ERROR: $@" >&2 |
| + clean_exit 1 |
| +} |
| + |
| +fail_panic () { |
| + if [ $? != 0 ]; then panic "$@"; fi |
| +} |
| + |
| +log () { |
| + if [ "$VERBOSE" -gt 0 ]; then |
| + echo "$@" |
| + fi |
| +} |
| + |
| +DEFAULT_PULL_LIBS_DIR=/tmp/$USER-adb-gdb-libs |
| + |
| +# NOTE: Allow wrapper scripts to set various default through ADB_GDB_XXX |
| +# environment variables. This is only for cosmetic reasons, i.e. to |
| +# display proper |
| + |
| +# Allow wrapper scripts to set the default activity through |
| +# the ADB_GDB_ACTIVITY variable. Users are still able to change the |
| +# final activity name through --activity=<name> option. |
| +# |
| +# This is only for cosmetic reasons, i.e. to display the proper default |
| +# in the --help output. |
| +# |
| +DEFAULT_ACTIVITY=${ADB_GDB_ACTIVITY:-".Main"} |
| + |
| +# Allow wrapper scripts to set the program name through ADB_GDB_PROGNAME |
| +PROGNAME=${ADB_GDB_PROGNAME:-$(basename "$0")} |
| + |
| +ACTIVITY=$DEFAULT_ACTIVITY |
| +ADB= |
| +ANNOTATE= |
| +# Note: Ignore BUILDTYPE variable, because the Ninja build doesn't use it. |
| +BUILDTYPE= |
| +FORCE= |
| +GDBINIT= |
| +GDBSERVER= |
| +HELP= |
| +NDK_DIR= |
| +NO_PULL_LIBS= |
| +PACKAGE_NAME= |
| +PID= |
| +PROGRAM_NAME="activity" |
| +PULL_LIBS= |
| +PULL_LIBS_DIR= |
| +SANDBOXED= |
| +SANDBOXED_INDEX= |
| +START= |
| +SYMBOL_DIR= |
| +TARGET_ARCH= |
| +TOOLCHAIN= |
| +VERBOSE=0 |
| + |
| +for opt; do |
| + optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') |
| + case $opt in |
| + --adb=*) |
| + ADB=$optarg |
| + ;; |
| + --activity=*) |
| + ACTIVITY=$optarg |
| + ;; |
| + --annotate=3) |
| + ANNOTATE=$optarg |
| + ;; |
| + --force) |
| + FORCE=true |
| + ;; |
| + --gdbserver=*) |
| + GDBSERVER=$optarg |
| + ;; |
| + --help|-h|-?) |
| + HELP=true |
| + ;; |
| + --ndk-dir=*) |
| + NDK_DIR=$optarg |
| + ;; |
| + --no-pull-libs) |
| + NO_PULL_LIBS=true |
| + ;; |
| + --package-name=*) |
| + PACKAGE_NAME=$optarg |
| + ;; |
| + --pid=*) |
| + PID=$optarg |
| + ;; |
| + --program-name=*) |
| + PROGRAM_NAME=$optarg |
| + ;; |
| + --pull-libs) |
| + PULL_LIBS=true |
| + ;; |
| + --pull-libs-dir=*) |
| + PULL_LIBS_DIR=$optarg |
| + ;; |
| + --sandboxed) |
| + SANDBOXED=true |
| + ;; |
| + --sandboxed=*) |
| + SANDBOXED=true |
| + SANDBOXED_INDEX=$optarg |
| + ;; |
| + --script=*) |
| + GDBINIT=$optarg |
| + ;; |
| + --start) |
| + START=true |
| + ;; |
| + --symbol-dir=*) |
| + SYMBOL_DIR=$optarg |
| + ;; |
| + --target-arch=*) |
| + TARGET_ARCH=$optarg |
| + ;; |
| + --toolchain=*) |
| + TOOLCHAIN=$optarg |
| + ;; |
| + --verbose) |
| + VERBOSE=$(( $VERBOSE + 1 )) |
| + ;; |
| + --debug) |
| + BUILDTYPE=Debug |
| + ;; |
| + --release) |
| + BUILDTYPE=Release |
| + ;; |
| + -*) |
| + panic "Unknown option $OPT, see --help." >&2 |
| + ;; |
| + *) |
| + if [ "$PACKAGE_NAME" ]; then |
| + panic "You can only provide a single package name as argument!\ |
| + See --help." |
| + fi |
| + PACKAGE_NAME=$opt |
| + ;; |
| + esac |
| +done |
| + |
| +print_help_options () |
| +{ |
| + cat <<EOF |
| +EOF |
| +} |
| + |
| +if [ "$HELP" ]; then |
| + if [ "$ADB_GDB_PROGNAME" ]; then |
| + # Assume wrapper scripts all provide a default package name. |
| + cat <<EOF |
| +Usage: $PROGNAME [options] |
| + |
| +Attach gdb to a running Android $PROGRAM_NAME process. |
| +EOF |
| + else |
| + # Assume this is a direct call to adb_gdb |
| + cat <<EOF |
| +Usage: $PROGNAME [options] [<package-name>] |
| + |
| +Attach gdb to a running Android $PROGRAM_NAME process. |
| + |
| +If provided, <package-name> must be the name of the Android application's |
| +package name to be debugged. You can also use --package-name=<name> to |
| +specify it. |
| +EOF |
| + fi |
| + |
| + cat <<EOF |
| + |
| +This script is used to debug a running $PROGRAM_NAME process. |
| +This can be a regular Android application process, or a sandboxed |
| +service, if you use the --sandboxed or --sandboxed=<num> option. |
| + |
| +This script needs several things to work properly. It will try to pick |
| +them up automatically for you though: |
| + |
| + - target gdbserver binary |
| + - host gdb client (e.g. arm-linux-androideabi-gdb) |
| + - directory with symbolic version of $PROGRAM_NAME's shared libraries. |
| + |
| +If you have sourced Chromium's build/android/envsetup.sh, this script will |
| +find all of them automatically. This is the recommended way to use it. |
| + |
| +Otherwise, if you have ANDROID_NDK_ROOT defined in your environment, |
| +the script will use it to find the gdb and gdbserver binaries. You can |
| +also use --ndk-dir=<path> to specify an alternative NDK installation |
| +directory. |
| + |
| +The script tries to find the most recent version of the debug version of |
| +shared libraries under one of the following directories: |
| + |
| + \$CHROMIUM_SRC/out/Release/lib/ (used by Ninja builds) |
| + \$CHROMIUM_SRC/out/Debug/lib/ (used by Ninja builds) |
| + \$CHROMIUM_SRC/out/Release/lib.target/ (used by Make builds) |
| + \$CHROMIUM_SRC/out/Debug/lib.target/ (used by Make builds) |
| + |
| +You can restrict this search by using --release or --debug to specify the |
| +build type, or simply use --symbol-dir=<path> to specify the file manually. |
| + |
| +The script tries to extract the target architecture from your GYP_DEFINES, |
| +but if this fails, will default to 'arm'. Use --target-arch=<name> to force |
| +its value. |
| + |
| +Otherwise, the script will complain, but you can use the --gdbserver, |
| +--gdb and --symbol-lib options to specify everything manually. |
| + |
| +An alternative to --gdb=<file> is to use --toollchain=<path> to specify |
| +the path to the host target-specific cross-toolchain. |
| + |
| +You will also need the 'adb' tool in your path. Otherwise, use the --adb |
| +option. The script will complain if there is more than one device connected |
| +and ANDROID_SERIAL is not defined. |
| + |
| +The first time you use it on a device, the script will pull many system |
| +libraries required by the process into a temporary directory. This |
| +is done to strongly improve the debugging experience, like allowing |
| +readable thread stacks and more. The libraries are copied to the following |
| +directory by default: |
| + |
| + $DEFAULT_PULL_LIBS_DIR/ |
| + |
| +But you can use the --pull-libs-dir=<path> option to specify an |
| +alternative. The script can detect when you change the connected device, |
| +and will re-pull the libraries only in this case. You can however force it |
| +with the --pull-libs option. |
| + |
| +Any local .gdbinit script will be ignored, but it is possible to pass a |
| +gdb command script with the --script=<file> option. Note that its commands |
| +will be passed to gdb after the remote connection and library symbol |
| +loading have completed. |
| + |
| +Valid options: |
| + --help|-h|-? Print this message. |
| + --verbose Increase verbosity. |
| + |
| + --sandboxed Debug first sandboxed process we find. |
| + --sandboxed=<num> Debug specific sandboxed process. |
| + --symbol-dir=<path> Specify directory with symbol shared libraries. |
| + --package-name=<name> Specify package name (alternative to 1st argument). |
| + --program-name=<name> Specify program name (cosmetic only). |
| + --pid=<pid> Specify application process pid. |
| + --force Kill any previous debugging session, if any. |
| + --start Start package's activity on device. |
| + --activity=<name> Activity name for --start [$DEFAULT_ACTIVITY]. |
| + --annotate=<num> Enable gdb annotation. |
| + --script=<file> Specify extra GDB init script. |
| + |
| + --gdbserver=<file> Specify targer gdbserver binary. |
| + --gdb=<program> Specify host gdb client binary. |
| + --target-arch=<name> Specify NDK target arch. |
| + --adb=<program> Specify host ADB binary. |
| + |
| + --pull-libs Force system libraries extraction. |
| + --no-pull-libs Do not extract any system library. |
| + --libs-dir=<path> Specify system libraries extraction directory. |
| + |
| + --debug Use libraries under out/Debug. |
| + --release Use libraries under out/Release. |
| + |
| +EOF |
| + exit 0 |
| +fi |
| + |
| +if [ -z "$PACKAGE_NAME" ]; then |
| + panic "Please specify a package name on the command line. See --help." |
| +fi |
| + |
| +if [ -z "$NDK_DIR" ]; then |
| + if [ -z "$ANDROID_NDK_ROOT" ]; then |
| + panic "Can't find NDK directory, please source \ |
| +build/android/envsetup.sh!" |
| + fi |
| +else |
| + if [ ! -d "$NDK_DIR" ]; then |
| + panic "Invalid directory: $NDK_DIR" |
| + fi |
| + if [ ! -f "$NDK_DIR/ndk-build" ]; then |
| + panic "Not a valid NDK directory: $NDK_DIR" |
| + fi |
| + ANDROID_NDK_ROOT=$NDK_DIR |
| +fi |
| + |
| +if [ "$GDBINIT" -a ! -f "$GDBINIT" ]; then |
| + panic "Unknown --script file: $GDBINIT" |
| +fi |
| + |
| +# Find the target architecture from our $GYP_DEFINES |
| +# This returns an NDK-compatible architecture name. |
| +# out: NDK Architecture name, or empty string. |
| +get_gyp_target_arch () |
| +{ |
| + local ARCH=$(echo $GYP_DEFINES | tr ' ' '\n' | grep '^target_arch=' |\ |
| + cut -d= -f2) |
| + case $ARCH in |
| + ia32|i?86|x86) echo "x86";; |
| + mips|arm) echo "$ARCH";; |
| + *) echo ""; |
| + esac |
| +} |
| + |
| +if [ -z "$TARGET_ARCH" ]; then |
| + TARGET_ARCH=$(get_gyp_target_arch) |
| + if [ -z "$TARGET_ARCH" ]; then |
| + TARGET_ARCH=arm |
| + fi |
| +else |
| + # Nit: accept Chromium's 'ia32' as a valid target architecture. This |
| + # script prefers the NDK 'x86' name instead because it uses it to find |
| + # NDK-specific files (host gdb) with it. |
| + if [ "$TARGET_ARCH" = "ia32" ]; then |
| + TARGET_ARCH=x86 |
| + log "Auto-config: --arch=$TARGET_ARCH (equivalent to ia32)" |
| + fi |
| +fi |
| + |
| +# Detect the NDK system tag, i.e. the name used to identify the host. |
| +# out: NDK system tag (e.g. 'linux-x86'). |
| +get_ndk_host_tag () |
| +{ |
| + if [ -z "$NDK_HOST_TAG" ]; then |
| + case $(uname -s) in |
| + Linux) NDK_HOST_TAG=linux-x86;; |
| + Darwin) NDK_HOST_TAG=darwin-x86;; |
| + *) panic "You can't run this script on this system: $uname -a";; |
| + esac |
| + fi |
| + echo "$NDK_HOST_TAG" |
| +} |
| + |
| +# Convert an NDK architecture name into a GNU configure triplet. |
| +# $1: NDK architecture name (e.g. 'arm') |
| +# Out: Android GNU configure triplet (e.g. 'arm-linux-androideabi') |
| +get_arch_gnu_config () |
| +{ |
| + case $1 in |
| + arm) |
| + echo "arm-linux-androideabi" |
| + ;; |
| + x86) |
| + echo "i686-linux-android" |
| + ;; |
| + mips) |
| + echo "mipsel-linux-android" |
| + ;; |
| + *) |
| + echo "$ARCH-linux-android" |
| + ;; |
| + esac |
| +} |
| + |
| +# Convert an NDK architecture name into a toolchain name prefix |
| +# $1: NDK architecture name (e.g. 'arm') |
| +# Out: NDK toolchain name prefix (e.g. 'arm-linux-androideabi') |
| +get_arch_toolchain_prefix () |
| +{ |
| + # Return the configure triplet, except for x86! |
| + if [ "$1" = "x86" ]; then |
| + echo "$1" |
| + else |
| + get_arch_gnu_config $1 |
| + fi |
| +} |
| + |
| +# Find a NDK toolchain prebuilt file or sub-directory. |
| +# This will probe the various arch-specific toolchain directories |
| +# in the NDK for the needed file. |
| +# $1: NDK install path |
| +# $2: NDK architecture name |
| +# $3: prebuilt sub-path to look for. |
| +# Out: file path, or empty if none is found. |
| +get_ndk_toolchain_prebuilt () |
| +{ |
| + local NDK_DIR="${1%/}" |
| + local ARCH="$2" |
| + local SUBPATH="$3" |
| + local NAME="$(get_arch_toolchain_prefix $ARCH)" |
| + local FILE TARGET |
| + FILE=$NDK_DIR/toolchains/$NAME-4.6/prebuilt/$SUBPATH |
| + if [ ! -f "$FILE" ]; then |
| + FILE=$NDK_DIR/toolchains/$NAME-4.4.3/prebuilt/$SUBPATH |
| + if [ ! -f "$FILE" ]; then |
| + FILE= |
| + fi |
| + fi |
| + echo "$FILE" |
| +} |
| + |
| +# Find the path to an NDK's toolchain full prefix for a given architecture |
| +# $1: NDK install path |
| +# $2: NDK architecture name |
| +# Out: install path + binary prefix (e.g. |
| +# ".../path/to/bin/arm-linux-androideabi-") |
| +get_ndk_toolchain_fullprefix () |
| +{ |
| + local NDK_DIR="$1" |
| + local ARCH="$2" |
| + local TARGET NAME HOST GCC CONFIG |
| + |
| + # NOTE: This will need to be updated if the NDK changes the names or moves |
| + # the location of its prebuilt toolchains. |
| + # |
| + GCC= |
| + HOST=$(get_ndk_host_tag) |
| + CONFIG=$(get_arch_gnu_config $ARCH) |
| + GCC=$(get_ndk_toolchain_prebuilt \ |
| + "$NDK_DIR" "$ARCH" "$HOST/bin/$CONFIG-gcc") |
| + if [ ! -f "$GCC" -a "$ARCH" = "x86" ]; then |
| + # Special case, the x86 toolchain used to be incorrectly |
| + # named i686-android-linux-gcc! |
| + GCC=$(get_ndk_toolchain_prebuilt \ |
| + "$NDK_DIR" "$ARCH" "$HOST/bin/i686-android-linux-gcc") |
| + fi |
| + if [ -z "$GCC" ]; then |
| + panic "Cannot find Android NDK toolchain for '$ARCH' architecture. \ |
| +Please verify your NDK installation!" |
| + fi |
| + echo "${GCC%%gcc}" |
| +} |
| + |
| +# $1: NDK install path |
| +# $2: target architecture. |
| +get_ndk_gdbserver () |
| +{ |
| + local NDK_DIR="$1" |
| + local ARCH=$2 |
| + local HOST=$(get_ndk_host_tag) |
| + local BINARY |
| + |
| + # The location has moved after NDK r8 |
| + BINARY=$NDK_DIR/prebuilt/android-$ARCH/gdbserver/gdbserver |
| + if [ ! -f "$BINARY" ]; then |
| + BINARY=$(get_ndk_toolchain_prebuilt "$NDK_DIR" "$ARCH" gdbserver) |
| + fi |
| + echo "$BINARY" |
| +} |
| + |
| +# Check/probe the path to the Android toolchain installation. Always |
| +# use the NDK versions of gdb and gdbserver. They must match to avoid |
| +# issues when both binaries do not speak the same wire protocol. |
| +# |
| +if [ -z "$TOOLCHAIN" ]; then |
| + ANDROID_TOOLCHAIN=$(get_ndk_toolchain_fullprefix \ |
| + "$ANDROID_NDK_ROOT" "$TARGET_ARCH") |
| + ANDROID_TOOLCHAIN=$(dirname "$ANDROID_TOOLCHAIN") |
| + log "Auto-config: --toolchain=$ANDROID_TOOLCHAIN" |
| +else |
| + # Be flexible, allow one to specify either the install path or the bin |
| + # sub-directory in --toolchain: |
| + # |
| + if [ -d "$TOOLCHAIN/bin" ]; then |
| + TOOLCHAIN=$TOOLCHAIN/bin |
| + fi |
| + ANDROID_TOOLCHAIN=$TOOLCHAIN |
| +fi |
| + |
| +# Cosmetic: Remove trailing directory separator. |
| +ANDROID_TOOLCHAIN=${ANDROID_TOOLCHAIN%/} |
| + |
| +# Find host GDB client binary |
| +GDB=$(which $ANDROID_TOOLCHAIN/*-gdb 2>/dev/null | head -1) |
| +if [ -z "$GDB" ]; then |
| + panic "Can't find Android gdb client in your path, check your \ |
| +--toolchain path." |
| +fi |
| +log "Host gdb client: $GDB" |
| + |
| +# Find gdbserver binary, we will later push it to /data/local/tmp |
| +# This ensures that both gdbserver and $GDB talk the same binary protocol, |
| +# otherwise weird problems will appear. |
| +# |
| +if [ -z "$GDBSERVER" ]; then |
| + GDBSERVER=$(get_ndk_gdbserver "$ANDROID_NDK_ROOT" "$TARGET_ARCH") |
| + if [ -z "$GDBSERVER" ]; then |
| + panic "Can't find NDK gdbserver binary. use --gdbserver to specify \ |
| +valid one!" |
| + fi |
| + log "Auto-config: --gdbserver=$GDBSERVER" |
| +fi |
| + |
| + |
| + |
| +# Check that ADB is in our path |
| +if [ -z "$ADB" ]; then |
| + ADB=$(which adb 2>/dev/null) |
| + if [ -z "$ADB" ]; then |
| + panic "Can't find 'adb' tool in your path. Install it or use \ |
| +--adb=<file>" |
| + fi |
| + log "Auto-config: --adb=$ADB" |
| +fi |
| + |
| +# Check that it works minimally |
| +ADB_VERSION=$($ADB version 2>/dev/null) |
| +echo "$ADB_VERSION" | fgrep -q -e "Android Debug Bridge" |
| +if [ $? != 0 ]; then |
| + panic "Your 'adb' tool seems invalid, use --adb=<file> to specify a \ |
| +different one: $ADB" |
| +fi |
| + |
| +# If there are more than one device connected, and ANDROID_SERIAL is not |
| +# defined, print an error message. |
| +NUM_DEVICES_PLUS2=$($ADB devices 2>/dev/null | wc -l) |
| +if [ "$NUM_DEVICES_PLUS2" -lt 3 -a -z "$ANDROID_SERIAL" ]; then |
| + echo "ERROR: There is more than one Android device connected to ADB." |
| + echo "Please define ANDROID_SERIAL to specify which one to use." |
| + exit 1 |
| +fi |
| + |
| +# A unique ID for this script's session. This needs to be the same in all |
| +# sub-shell commands we're going to launch, so take the PID of the launcher |
| +# process. |
| +TMP_ID=$$ |
| + |
| +# Temporary directory, will get cleaned up on exit. |
| +TMPDIR=/tmp/$USER-adb-gdb-tmp-$TMP_ID |
| +mkdir -p "$TMPDIR" && rm -rf "$TMPDIR"/* |
| + |
| +GDBSERVER_PIDFILE="$TMPDIR"/gdbserver-$TMP_ID.pid |
| + |
| +# Run a command through adb shell, strip the extra \r from the output |
| +# and return the correct status code to detect failures. This assumes |
| +# that the adb shell command prints a final \n to stdout. |
| +# $1+: command to run |
| +# Out: command's stdout |
| +# Return: command's status |
| +# Note: the command's stderr is lost |
| +adb_shell () |
| +{ |
| + local TMPOUT="$(mktemp)" |
| + local LASTLINE RET |
| + local ADB=${ADB:-adb} |
| + |
| + # The weird sed rule is to strip the final \r on each output line |
| + # Since 'adb shell' never returns the command's proper exit/status code, |
| + # we force it to print it as '%%<status>' in the temporary output file, |
| + # which we will later strip from it. |
| + $ADB shell $@ ";" echo "%%\$?" 2>/dev/null | \ |
| + sed -e 's![[:cntrl:]]!!g' > $TMPOUT |
| + # Get last line in log, which contains the exit code from the command |
| + LASTLINE=$(sed -e '$!d' $TMPOUT) |
| + # Extract the status code from the end of the line, which must |
| + # be '%%<code>'. |
| + RET=$(echo "$LASTLINE" | \ |
| + awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,RSTART+2); } }') |
| + # Remove the status code from the last line. Note that this may result |
| + # in an empty line. |
| + LASTLINE=$(echo "$LASTLINE" | \ |
| + awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,1,RSTART-1); } }') |
| + # The output itself: all lines except the status code. |
| + sed -e '$d' $TMPOUT && printf "%s" "$LASTLINE" |
| + # Remove temp file. |
| + rm -f $TMPOUT |
| + # Exit with the appropriate status. |
| + return $RET |
| +} |
| + |
| +# If --force is specified, try to kill any gdbserver process started by the |
| +# same user on the device. Normally, these are killed automatically by the |
| +# script on exit, but there are a few corner cases where this would still |
| +# be needed. |
| +if [ "$FORCE" ]; then |
| + GDBSERVER_PIDS=$(adb_shell ps | awk '$9 ~ /gdbserver/ { print $2; }') |
| + for GDB_PID in $GDBSERVER_PIDS; do |
| + log "Killing previous gdbserver (PID=$GDB_PID)" |
| + adb_shell kill -9 $GDB_PID |
| + done |
| +fi |
| + |
| +if [ "$START" ]; then |
| + log "Starting $PROGRAM_NAME on device." |
| + adb_shell am start -n $PACKAGE_NAME/$ACTIVITY 2>/dev/null |
| + adb_shell ps | grep -q $PACKAGE_NAME |
| + fail_panic "Could not start $PROGRAM_NAME on device. Are you sure the \ |
| +package is installed?" |
| +fi |
| + |
| +# Return the timestamp of a given time, as number of seconds since epoch. |
| +# $1: file path |
| +# Out: file timestamp |
| +get_file_timestamp () |
| +{ |
| + stat -c %Y "$1" 2>/dev/null |
| +} |
| + |
| +# Detect the build type and symbol directory. This is done by finding |
| +# the most recent sub-directory containing debug shared libraries under |
| +# $CHROMIUM_SRC/out/ |
| +# |
| +# $1: $BUILDTYPE value, can be empty |
| +# Out: nothing, but this sets SYMBOL_DIR |
| +# |
| +detect_symbol_dir () { |
| + local SUBDIRS SUBDIR LIST DIR DIR_LIBS TSTAMP |
| + # Note: Ninja places debug libraries under out/$BUILDTYPE/lib/, while |
| + # Make places then under out/$BUILDTYPE/lib.target. |
| + if [ "$1" ]; then |
| + SUBDIRS="$1/lib $1/lib.target" |
| + else |
| + SUBDIRS="Release/lib Debug/lib Release/lib.target Debug/lib.target" |
| + fi |
| + LIST=$TMPDIR/scan-subdirs-$$.txt |
| + printf "" > "$LIST" |
| + for SUBDIR in $SUBDIRS; do |
| + DIR=$CHROMIUM_SRC/out/$SUBDIR |
| + if [ -d "$DIR" ]; then |
| + # Ignore build directories that don't contain symbol versions |
| + # of the shared libraries. |
| + DIR_LIBS=$(ls "$DIR"/lib*.so 2>/dev/null) |
| + if [ -z "$DIR_LIBS" ]; then |
| + echo "No shared libs: $DIR" |
| + continue |
| + fi |
| + TSTAMP=$(get_file_timestamp "$DIR") |
| + printf "%s %s\n" "$TSTAMP" "$SUBDIR" >> "$LIST" |
| + fi |
| + done |
| + SUBDIR=$(cat $LIST | sort -r | head -1 | cut -d" " -f2) |
| + rm -f "$LIST" |
| + |
| + if [ -z "$SUBDIR" ]; then |
| + if [ -z "$1" ]; then |
| + panic "Could not find any build directory under \ |
| +$CHROMIUM_SRC/out. Please build the program first!" |
| + else |
| + panic "Could not find any $1 directory under \ |
| +$CHROMIUM_SRC/out. Check your build type!" |
| + fi |
| + fi |
| + |
| + SYMBOL_DIR=$CHROMIUM_SRC/out/$SUBDIR |
| + log "Auto-config: --symbol-dir=$SYMBOL_DIR" |
| +} |
| + |
| +if [ -z "$SYMBOL_DIR" ]; then |
| + detect_symbol_dir "$BUILDTYPE" |
| +fi |
| + |
| +# Allow several concurrent debugging sessions |
| +TARGET_GDBSERVER=/data/local/tmp/gdbserver-adb-gdb-$TMP_ID |
| + |
| +# Return the build fingerprint contained in a build.prop file. |
| +# $1: path to build.prop file |
| +get_build_fingerprint_from () |
| +{ |
| + cat "$1" | grep -e '^ro.build.fingerprint=' | cut -d= -f2 |
| +} |
| + |
| + |
| +ORG_PULL_LIBS_DIR=$PULL_LIBS_DIR |
| +PULL_LIBS_DIR=${PULL_LIBS_DIR:-$DEFAULT_PULL_LIBS_DIR} |
| + |
| +HOST_FINGERPRINT= |
| +DEVICE_FINGERPRINT=$(adb_shell getprop ro.build.fingerprint) |
| +log "Device build fingerprint: $DEVICE_FINGERPRINT" |
| + |
| +# If --pull-libs-dir is not specified, and this is a platform build, look |
| +# if we can use the symbolic libraries under $ANDROID_PRODUCT_OUT/symbols/ |
| +# directly, if the build fingerprint matches the device. |
| +if [ -z "$ORG_PULL_LIBS_DIR" -a \ |
| + "$ANDROID_PRODUCT_OUT" -a \ |
| + -f "$ANDROID_PRODUCT_OUT/system/build.prop" ]; then |
| + ANDROID_FINGERPRINT=$(get_build_fingerprint_from \ |
| + "$ANDROID_PRODUCT_OUT"/system/build.prop) |
| + log "Android build fingerprint: $ANDROID_FINGERPRINT" |
| + if [ "$ANDROID_FINGERPRINT" = "$DEVICE_FINGERPRINT" ]; then |
| + log "Perfect match!" |
| + PULL_LIBS_DIR=$ANDROID_PRODUCT_OUT/symbols |
| + HOST_FINGERPRINT=$ANDROID_FINGERPRINT |
| + if [ "$PULL_LIBS" ]; then |
| + log "Ignoring --pull-libs since the device and platform build \ |
| +fingerprints match." |
| + NO_PULL_LIBS=true |
| + fi |
| + fi |
| +fi |
| + |
| +# If neither --pull-libs an --no-pull-libs were specified, check the build |
| +# fingerprints of the device, and the cached system libraries on the host. |
| +# |
| +if [ -z "$NO_PULL_LIBS" -a -z "$PULL_LIBS" ]; then |
| + if [ ! -f "$PULL_LIBS_DIR/build.prop" ]; then |
| + log "Auto-config: --pull-libs (no cached libraries)" |
| + PULL_LIBS=true |
| + else |
| + HOST_FINGERPRINT=$(get_build_fingerprint_from "$PULL_LIBS_DIR/build.prop") |
| + log "Host build fingerprint: $HOST_FINGERPRINT" |
| + if [ "$HOST_FINGERPRINT" == "$DEVICE_FINGERPRINT" ]; then |
| + log "Auto-config: --no-pull-libs (fingerprint match)" |
| + NO_PULL_LIBS=true |
| + else |
| + log "Auto-config: --pull-libs (fingerprint mismatch)" |
| + PULL_LIBS=true |
| + fi |
| + fi |
| +fi |
| + |
| +# Extract the system libraries from the device if necessary. |
| +if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then |
| + echo "Extracting system libraries into: $PULL_LIBS_DIR" |
| +fi |
| + |
| +mkdir -p "$PULL_LIBS_DIR" |
| +fail_panic "Can't create --libs-dir directory: $PULL_LIBS_DIR" |
| + |
| +# If requested, work for M-x gdb. The gdb indirections make it |
| +# difficult to pass --annotate=3 to the gdb binary itself. |
| +GDB_ARGS= |
| +if [ "$ANNOTATE" ]; then |
| + GDB_ARGS=$GDB_ARGS" --annotate=$ANNOTATE" |
| +fi |
| + |
| +# Get the PID from the first argument or else find the PID of the |
| +# browser process. |
| +if [ -z "$PID" ]; then |
| + PROCESSNAME=$PACKAGE_NAME |
| + if [ "$SANDBOXED_INDEX" ]; then |
| + PROCESSNAME=$PROCESSNAME:sandboxed_process$SANDBOXED_INDEX |
| + elif [ "$SANDBOXED" ]; then |
| + PROCESSNAME=$PROCESSNAME:sandboxed_process |
| + PID=$(adb_shell ps | \ |
| + awk '$9 ~ /^'$PROCESSNAME'/ { print $2; }' | head -1) |
| + fi |
| + if [ -z "$PID" ]; then |
| + PID=$(adb_shell ps | \ |
| + awk '$9 == "'$PROCESSNAME'" { print $2; }' | head -1) |
| + fi |
| + if [ -z "$PID" ]; then |
| + if [ "$START" ]; then |
| + panic "Can't find application process PID, did it crash?" |
| + else |
| + panic "Can't find application process PID, are you sure it is \ |
| +running? Try using --start." |
| + fi |
| + fi |
| + log "Found process PID: $PID" |
| +elif [ "$SANDBOXED" ]; then |
| + echo "WARNING: --sandboxed option ignored due to use of --pid." |
| +fi |
| + |
| +# Determine if 'adb shell' runs as root or not. |
| +# If so, we can launch gdbserver directly, otherwise, we have to |
| +# use run-as $PACKAGE_NAME ..., which requires the package to be debuggable. |
| +# |
| +SHELL_UID=$(adb shell cat /proc/self/status | \ |
| + awk '$1 == "Uid:" { print $2; }') |
| +log "Shell UID: $SHELL_UID" |
| +COMMAND_PREFIX= |
| +if [ "$SHELL_UID" != 0 -o -n "$NO_ROOT" ]; then |
| + log "Using run-as $PACKAGE_NAME to run without root." |
| + COMMAND_PREFIX="run-as $PACKAGE_NAME" |
| +fi |
| + |
| +# Pull device's system libraries that are mapped by our process. |
| +# Pulling all system libraries is too long, so determine which ones |
| +# we need by looking at /proc/$PID/maps instead |
| +if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then |
| + echo "Extracting system libraries into: $PULL_LIBS_DIR" |
| + SYSTEM_LIBS=$(adb_shell $COMMAND_PREFIX cat /proc/$PID/maps | \ |
| + awk '$6 ~ /\/system\/.*\.so$/ { print $6; }' | sort -u) |
| + for SYSLIB in /system/bin/linker $SYSTEM_LIBS; do |
| + echo "Pulling from device: $SYSLIB" |
| + DST_FILE=$PULL_LIBS_DIR$SYSLIB |
| + DST_DIR=$(dirname "$DST_FILE") |
| + mkdir -p "$DST_DIR" && adb pull $SYSLIB "$DST_FILE" 2>/dev/null |
| + fail_panic "Could not pull $SYSLIB from device !?" |
| + done |
| + echo "Pulling device build.prop" |
| + adb pull /system/build.prop $PULL_LIBS_DIR/build.prop |
| + fail_panic "Could not pull device build.prop !?" |
| +fi |
| + |
| +# Find all the sub-directories of $PULL_LIBS_DIR, up to depth 4 |
| +# so we can add them to solib-search-path later. |
| +SOLIB_DIRS=$(find $PULL_LIBS_DIR -mindepth 1 -maxdepth 4 -type d | \ |
| + grep -v "^$" | tr '\n' ':') |
| + |
| +# This is a re-implementation of gdbclient, where we use compatible |
| +# versions of gdbserver and $GDBNAME to ensure that everything works |
| +# properly. |
| +# |
| + |
| +# Push gdbserver to the device |
| +log "Pushing gdbserver to $TARGET_GDBSERVER" |
| +adb push $GDBSERVER $TARGET_GDBSERVER &>/dev/null |
| +fail_panic "Could not copy gdbserver to the device!" |
| + |
| +PORT=5039 |
| +HOST_PORT=$PORT |
| +TARGET_PORT=$PORT |
| + |
| +# Pull the app_process binary from the device |
| +GDBEXEC=app_process |
| +log "Pulling $GDBEXEC from device" |
| +adb pull /system/bin/$GDBEXEC "$TMPDIR"/$GDBEXEC &>/dev/null |
| +fail_panic "Could not retrieve $GDBEXEC from the device!" |
| + |
| +# Setup network redirection |
| +log "Setting network redirection (host:$HOST_PORT -> device:$TARGET_PORT)" |
| +adb forward tcp:$HOST_PORT tcp:$TARGET_PORT |
| +fail_panic "Could not setup network redirection from \ |
| +host:localhost:$HOST_PORT to device:localhost:$TARGET_PORT!" |
| + |
| +# Start gdbserver in the background |
| +# Note that using run-as requires the package to be debuggable. |
| +# |
| +# If not, this will fail horribly. The alternative is to run the |
| +# program as root, which requires of course root privileges. |
| +# Maybe we should add a --root option to enable this? |
| +# |
| +log "Starting gdbserver in the background:" |
| +GDBSERVER_LOG=$TMPDIR/gdbserver-$TMP_ID.log |
| +log "adb shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \ |
| +--attach $PID" |
| +("$ADB" shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \ |
| + --attach $PID > $GDBSERVER_LOG 2>&1) & |
| +GDBSERVER_PID=$! |
| +echo "$GDBSERVER_PID" > $GDBSERVER_PIDFILE |
| +log "background job pid: $GDBSERVER_PID" |
| + |
| +# Check that it is still running after a few seconds. If not, this means we |
| +# could not properly attach to it |
| +sleep 2 |
| +log "Job control: $(jobs -l)" |
| +STATE=$(jobs -l | awk '$2 == "'$GDBSERVER_PID'" { print $3; }') |
| +if [ "$STATE" != "Running" ]; then |
| + echo "ERROR: GDBServer could not attach to PID $PID!" |
| + echo "Failure log (use --verbose for more information):" |
| + cat $GDBSERVER_LOG |
| + exit 1 |
| +fi |
| + |
| +# Generate a file containing useful GDB initialization commands |
| +readonly COMMANDS=$TMPDIR/gdb.init |
| +log "Generating GDB initialization commands file: $COMMANDS" |
| +echo -n "" > $COMMANDS |
| +echo "file $TMPDIR/$GDBEXEC" >> $COMMANDS |
| +echo "directory $CHROMIUM_SRC" >> $COMMANDS |
| +echo "set solib-absolute-prefix $PULL_LIBS_DIR" >> $COMMANDS |
| +echo "set solib-search-path $SOLIB_DIRS:$PULL_LIBS_DIR:$SYMBOL_DIR" \ |
| + >> $COMMANDS |
| +echo "echo Attaching and reading symbols, this may take a while.." \ |
| + >> $COMMANDS |
| +echo "target remote :$HOST_PORT" >> $COMMANDS |
| + |
| +if [ "$GDBINIT" ]; then |
| + cat "$GDBINIT" >> $COMMANDS |
| +fi |
| + |
| +if [ "$VERBOSE" -gt 0 ]; then |
| + echo "### START $COMMANDS" |
| + cat $COMMANDS |
| + echo "### END $COMMANDS" |
| +fi |
| + |
| +log "Launching gdb client: $GDB $GDBARGS -x $COMMANDS" |
| +$GDB $GDBARGS -x $COMMANDS && |
| +rm -f "$GDBSERVER_PIDFILE" |
| + |
| +clean_exit $? |