| #!/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 () { | 
 |   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 $? |