Home | History | Annotate | Download | only in android
      1 #!/bin/bash
      2 #
      3 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 #
      7 
      8 # A generic script used to attach to a running Chromium process and
      9 # debug it. Most users should not use this directly, but one of the
     10 # wrapper scripts like adb_gdb_content_shell, or adb_gdb_drt
     11 #
     12 # Use --help to print full usage instructions.
     13 #
     14 
     15 PROGNAME=$(basename "$0")
     16 PROGDIR=$(dirname "$0")
     17 
     18 # Location of Chromium-top-level sources.
     19 CHROMIUM_SRC=$(cd "$PROGDIR"/../.. && pwd 2>/dev/null)
     20 
     21 TMPDIR=
     22 GDBSERVER_PIDFILE=
     23 TARGET_GDBSERVER=
     24 
     25 clean_exit () {
     26   if [ "$TMPDIR" ]; then
     27     GDBSERVER_PID=$(cat $GDBSERVER_PIDFILE 2>/dev/null)
     28     if [ "$GDBSERVER_PID" ]; then
     29       log "Killing background gdbserver process: $GDBSERVER_PID"
     30       kill -9 $GDBSERVER_PID >/dev/null 2>&1
     31     fi
     32     if [ "$TARGET_GDBSERVER" ]; then
     33       log "Removing target gdbserver binary: $TARGET_GDBSERVER."
     34       "$ADB" shell rm "$TARGET_GDBSERVER" >/dev/null 2>&1
     35     fi
     36     log "Cleaning up: $TMPDIR"
     37     rm -rf "$TMPDIR"
     38   fi
     39   trap "" EXIT
     40   exit $1
     41 }
     42 
     43 # Ensure clean exit on Ctrl-C or normal exit.
     44 trap "clean_exit 1" INT HUP QUIT TERM
     45 trap "clean_exit \$?" EXIT
     46 
     47 panic () {
     48   echo "ERROR: $@" >&2
     49   exit 1
     50 }
     51 
     52 fail_panic () {
     53   if [ $? != 0 ]; then panic "$@"; fi
     54 }
     55 
     56 log () {
     57   if [ "$VERBOSE" -gt 0 ]; then
     58     echo "$@"
     59   fi
     60 }
     61 
     62 DEFAULT_PULL_LIBS_DIR=/tmp/$USER-adb-gdb-libs
     63 
     64 # NOTE: Allow wrapper scripts to set various default through ADB_GDB_XXX
     65 # environment variables. This is only for cosmetic reasons, i.e. to
     66 # display proper
     67 
     68 # Allow wrapper scripts to set the default activity through
     69 # the ADB_GDB_ACTIVITY variable. Users are still able to change the
     70 # final activity name through --activity=<name> option.
     71 #
     72 # This is only for cosmetic reasons, i.e. to display the proper default
     73 # in the --help output.
     74 #
     75 DEFAULT_ACTIVITY=${ADB_GDB_ACTIVITY:-".Main"}
     76 
     77 # Allow wrapper scripts to set the program name through ADB_GDB_PROGNAME
     78 PROGNAME=${ADB_GDB_PROGNAME:-$(basename "$0")}
     79 
     80 ACTIVITY=$DEFAULT_ACTIVITY
     81 ADB=
     82 ANNOTATE=
     83 # Note: Ignore BUILDTYPE variable, because the Ninja build doesn't use it.
     84 BUILDTYPE=
     85 FORCE=
     86 GDBEXEPOSTFIX=gdb
     87 GDBINIT=
     88 GDBSERVER=
     89 HELP=
     90 NDK_DIR=
     91 NO_PULL_LIBS=
     92 PACKAGE_NAME=
     93 PID=
     94 PROGRAM_NAME="activity"
     95 PULL_LIBS=
     96 PULL_LIBS_DIR=
     97 SANDBOXED=
     98 SANDBOXED_INDEX=
     99 START=
    100 SU_PREFIX=
    101 SYMBOL_DIR=
    102 TARGET_ARCH=
    103 TOOLCHAIN=
    104 VERBOSE=0
    105 
    106 for opt; do
    107   optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)')
    108   case $opt in
    109     --adb=*)
    110       ADB=$optarg
    111       ;;
    112     --activity=*)
    113       ACTIVITY=$optarg
    114       ;;
    115     --annotate=3)
    116       ANNOTATE=$optarg
    117       ;;
    118     --force)
    119       FORCE=true
    120       ;;
    121     --gdbserver=*)
    122       GDBSERVER=$optarg
    123       ;;
    124     --help|-h|-?)
    125       HELP=true
    126       ;;
    127     --ndk-dir=*)
    128       NDK_DIR=$optarg
    129       ;;
    130     --no-pull-libs)
    131       NO_PULL_LIBS=true
    132       ;;
    133     --package-name=*)
    134       PACKAGE_NAME=$optarg
    135       ;;
    136     --pid=*)
    137       PID=$optarg
    138       ;;
    139     --program-name=*)
    140       PROGRAM_NAME=$optarg
    141       ;;
    142     --pull-libs)
    143       PULL_LIBS=true
    144       ;;
    145     --pull-libs-dir=*)
    146       PULL_LIBS_DIR=$optarg
    147       ;;
    148     --sandboxed)
    149       SANDBOXED=true
    150       ;;
    151     --sandboxed=*)
    152       SANDBOXED=true
    153       SANDBOXED_INDEX=$optarg
    154       ;;
    155     --script=*)
    156       GDBINIT=$optarg
    157       ;;
    158     --start)
    159       START=true
    160       ;;
    161     --su-prefix=*)
    162       SU_PREFIX=$optarg
    163       ;;
    164     --symbol-dir=*)
    165       SYMBOL_DIR=$optarg
    166       ;;
    167     --target-arch=*)
    168       TARGET_ARCH=$optarg
    169       ;;
    170     --toolchain=*)
    171       TOOLCHAIN=$optarg
    172       ;;
    173     --ui)
    174       GDBEXEPOSTFIX=gdbtui
    175       ;;
    176     --verbose)
    177       VERBOSE=$(( $VERBOSE + 1 ))
    178       ;;
    179     --debug)
    180       BUILDTYPE=Debug
    181       ;;
    182     --release)
    183       BUILDTYPE=Release
    184       ;;
    185     -*)
    186       panic "Unknown option $OPT, see --help." >&2
    187       ;;
    188     *)
    189       if [ "$PACKAGE_NAME" ]; then
    190         panic "You can only provide a single package name as argument!\
    191  See --help."
    192       fi
    193       PACKAGE_NAME=$opt
    194       ;;
    195   esac
    196 done
    197 
    198 print_help_options () {
    199   cat <<EOF
    200 EOF
    201 }
    202 
    203 if [ "$HELP" ]; then
    204   if [ "$ADB_GDB_PROGNAME" ]; then
    205     # Assume wrapper scripts all provide a default package name.
    206     cat <<EOF
    207 Usage: $PROGNAME [options]
    208 
    209 Attach gdb to a running Android $PROGRAM_NAME process.
    210 EOF
    211   else
    212     # Assume this is a direct call to adb_gdb
    213   cat <<EOF
    214 Usage: $PROGNAME [options] [<package-name>]
    215 
    216 Attach gdb to a running Android $PROGRAM_NAME process.
    217 
    218 If provided, <package-name> must be the name of the Android application's
    219 package name to be debugged. You can also use --package-name=<name> to
    220 specify it.
    221 EOF
    222   fi
    223 
    224   cat <<EOF
    225 
    226 This script is used to debug a running $PROGRAM_NAME process.
    227 This can be a regular Android application process, or a sandboxed
    228 service, if you use the --sandboxed or --sandboxed=<num> option.
    229 
    230 This script needs several things to work properly. It will try to pick
    231 them up automatically for you though:
    232 
    233    - target gdbserver binary
    234    - host gdb client (e.g. arm-linux-androideabi-gdb)
    235    - directory with symbolic version of $PROGRAM_NAME's shared libraries.
    236 
    237 If you have sourced Chromium's build/android/envsetup.sh, this script will
    238 find all of them automatically. This is the recommended way to use it.
    239 
    240 Otherwise, if you have ANDROID_NDK_ROOT defined in your environment,
    241 the script will use it to find the gdb and gdbserver binaries. You can
    242 also use --ndk-dir=<path> to specify an alternative NDK installation
    243 directory.
    244 
    245 The script tries to find the most recent version of the debug version of
    246 shared libraries under one of the following directories:
    247 
    248   \$CHROMIUM_SRC/out/Release/lib/           (used by Ninja builds)
    249   \$CHROMIUM_SRC/out/Debug/lib/             (used by Ninja builds)
    250   \$CHROMIUM_SRC/out/Release/lib.target/    (used by Make builds)
    251   \$CHROMIUM_SRC/out/Debug/lib.target/      (used by Make builds)
    252 
    253 You can restrict this search by using --release or --debug to specify the
    254 build type, or simply use --symbol-dir=<path> to specify the file manually.
    255 
    256 The script tries to extract the target architecture from your GYP_DEFINES,
    257 but if this fails, will default to 'arm'. Use --target-arch=<name> to force
    258 its value.
    259 
    260 Otherwise, the script will complain, but you can use the --gdbserver,
    261 --gdb and --symbol-lib options to specify everything manually.
    262 
    263 An alternative to --gdb=<file> is to use --toollchain=<path> to specify
    264 the path to the host target-specific cross-toolchain.
    265 
    266 You will also need the 'adb' tool in your path. Otherwise, use the --adb
    267 option. The script will complain if there is more than one device connected
    268 and ANDROID_SERIAL is not defined.
    269 
    270 The first time you use it on a device, the script will pull many system
    271 libraries required by the process into a temporary directory. This
    272 is done to strongly improve the debugging experience, like allowing
    273 readable thread stacks and more. The libraries are copied to the following
    274 directory by default:
    275 
    276   $DEFAULT_PULL_LIBS_DIR/
    277 
    278 But you can use the --pull-libs-dir=<path> option to specify an
    279 alternative. The script can detect when you change the connected device,
    280 and will re-pull the libraries only in this case. You can however force it
    281 with the --pull-libs option.
    282 
    283 Any local .gdbinit script will be ignored, but it is possible to pass a
    284 gdb command script with the --script=<file> option. Note that its commands
    285 will be passed to gdb after the remote connection and library symbol
    286 loading have completed.
    287 
    288 Valid options:
    289   --help|-h|-?          Print this message.
    290   --verbose             Increase verbosity.
    291 
    292   --sandboxed           Debug first sandboxed process we find.
    293   --sandboxed=<num>     Debug specific sandboxed process.
    294   --symbol-dir=<path>   Specify directory with symbol shared libraries.
    295   --package-name=<name> Specify package name (alternative to 1st argument).
    296   --program-name=<name> Specify program name (cosmetic only).
    297   --pid=<pid>           Specify application process pid.
    298   --force               Kill any previous debugging session, if any.
    299   --start               Start package's activity on device.
    300   --ui                  Use gdbtui instead of gdb
    301   --activity=<name>     Activity name for --start [$DEFAULT_ACTIVITY].
    302   --annotate=<num>      Enable gdb annotation.
    303   --script=<file>       Specify extra GDB init script.
    304 
    305   --gdbserver=<file>    Specify targer gdbserver binary.
    306   --gdb=<program>       Specify host gdb client binary.
    307   --target-arch=<name>  Specify NDK target arch.
    308   --adb=<program>       Specify host ADB binary.
    309 
    310   --su-prefix=<prefix>  Prepend <prefix> to 'adb shell' commands that are
    311                         run by this script. This can be useful to use
    312                         the 'su' program on rooted production devices.
    313 
    314   --pull-libs           Force system libraries extraction.
    315   --no-pull-libs        Do not extract any system library.
    316   --libs-dir=<path>     Specify system libraries extraction directory.
    317 
    318   --debug               Use libraries under out/Debug.
    319   --release             Use libraries under out/Release.
    320 
    321 EOF
    322   exit 0
    323 fi
    324 
    325 if [ -z "$PACKAGE_NAME" ]; then
    326   panic "Please specify a package name on the command line. See --help."
    327 fi
    328 
    329 if [ -z "$NDK_DIR" ]; then
    330   if [ -z "$ANDROID_NDK_ROOT" ]; then
    331     panic "Can't find NDK directory, please source \
    332 build/android/envsetup.sh!"
    333   fi
    334 else
    335   if [ ! -d "$NDK_DIR" ]; then
    336     panic "Invalid directory: $NDK_DIR"
    337   fi
    338   if [ ! -f "$NDK_DIR/ndk-build" ]; then
    339     panic "Not a valid NDK directory: $NDK_DIR"
    340   fi
    341   ANDROID_NDK_ROOT=$NDK_DIR
    342 fi
    343 
    344 if [ "$GDBINIT" -a ! -f "$GDBINIT" ]; then
    345   panic "Unknown --script file: $GDBINIT"
    346 fi
    347 
    348 # Find the target architecture from our $GYP_DEFINES
    349 # This returns an NDK-compatible architecture name.
    350 # out: NDK Architecture name, or empty string.
    351 get_gyp_target_arch () {
    352   local ARCH=$(echo $GYP_DEFINES | tr ' ' '\n' | grep '^target_arch=' |\
    353                cut -d= -f2)
    354   case $ARCH in
    355     ia32|i?86|x86) echo "x86";;
    356     mips|arm) echo "$ARCH";;
    357     *) echo "";
    358   esac
    359 }
    360 
    361 if [ -z "$TARGET_ARCH" ]; then
    362   TARGET_ARCH=$(get_gyp_target_arch)
    363   if [ -z "$TARGET_ARCH" ]; then
    364     TARGET_ARCH=arm
    365   fi
    366 else
    367   # Nit: accept Chromium's 'ia32' as a valid target architecture. This
    368   # script prefers the NDK 'x86' name instead because it uses it to find
    369   # NDK-specific files (host gdb) with it.
    370   if [ "$TARGET_ARCH" = "ia32" ]; then
    371     TARGET_ARCH=x86
    372     log "Auto-config: --arch=$TARGET_ARCH  (equivalent to ia32)"
    373   fi
    374 fi
    375 
    376 # Detect the NDK system name, i.e. the name used to identify the host.
    377 # out: NDK system name (e.g. 'linux' or 'darwin')
    378 get_ndk_host_system () {
    379   local HOST_OS
    380   if [ -z "$NDK_HOST_SYSTEM" ]; then
    381     HOST_OS=$(uname -s)
    382     case $HOST_OS in
    383       Linux) NDK_HOST_SYSTEM=linux;;
    384       Darwin) NDK_HOST_SYSTEM=darwin;;
    385       *) panic "You can't run this script on this system: $HOST_OS";;
    386     esac
    387   fi
    388   echo "$NDK_HOST_SYSTEM"
    389 }
    390 
    391 # Detect the NDK host architecture name.
    392 # out: NDK arch name (e.g. 'x86' or 'x86_64')
    393 get_ndk_host_arch () {
    394   local HOST_ARCH HOST_OS
    395   if [ -z "$NDK_HOST_ARCH" ]; then
    396     HOST_OS=$(get_ndk_host_system)
    397     HOST_ARCH=$(uname -p)
    398     case $HOST_ARCH in
    399       i?86) NDK_HOST_ARCH=x86;;
    400       x86_64|amd64) NDK_HOST_ARCH=x86_64;;
    401       *) panic "You can't run this script on this host architecture: $HOST_ARCH";;
    402     esac
    403     # Darwin trick: "uname -p" always returns i386 on 64-bit installations.
    404     if [ "$HOST_OS" = darwin -a "$NDK_HOST_ARCH" = "x86" ]; then
    405       # Use '/usr/bin/file', not just 'file' to avoid buggy MacPorts
    406       # implementations of the tool. See http://b.android.com/53769
    407       HOST_64BITS=$(/usr/bin/file -L "$SHELL" | grep -e "x86[_-]64")
    408       if [ "$HOST_64BITS" ]; then
    409         NDK_HOST_ARCH=x86_64
    410       fi
    411     fi
    412   fi
    413   echo "$NDK_HOST_ARCH"
    414 }
    415 
    416 # Convert an NDK architecture name into a GNU configure triplet.
    417 # $1: NDK architecture name (e.g. 'arm')
    418 # Out: Android GNU configure triplet (e.g. 'arm-linux-androideabi')
    419 get_arch_gnu_config () {
    420   case $1 in
    421     arm)
    422       echo "arm-linux-androideabi"
    423       ;;
    424     x86)
    425       echo "i686-linux-android"
    426       ;;
    427     mips)
    428       echo "mipsel-linux-android"
    429       ;;
    430     *)
    431       echo "$ARCH-linux-android"
    432       ;;
    433   esac
    434 }
    435 
    436 # Convert an NDK architecture name into a toolchain name prefix
    437 # $1: NDK architecture name (e.g. 'arm')
    438 # Out: NDK toolchain name prefix (e.g. 'arm-linux-androideabi')
    439 get_arch_toolchain_prefix () {
    440   # Return the configure triplet, except for x86!
    441   if [ "$1" = "x86" ]; then
    442     echo "$1"
    443   else
    444     get_arch_gnu_config $1
    445   fi
    446 }
    447 
    448 # Find a NDK toolchain prebuilt file or sub-directory.
    449 # This will probe the various arch-specific toolchain directories
    450 # in the NDK for the needed file.
    451 # $1: NDK install path
    452 # $2: NDK architecture name
    453 # $3: prebuilt sub-path to look for.
    454 # Out: file path, or empty if none is found.
    455 get_ndk_toolchain_prebuilt () {
    456   local NDK_DIR="${1%/}"
    457   local ARCH="$2"
    458   local SUBPATH="$3"
    459   local NAME="$(get_arch_toolchain_prefix $ARCH)"
    460   local FILE TARGET
    461   FILE=$NDK_DIR/toolchains/$NAME-4.6/prebuilt/$SUBPATH
    462   if [ ! -f "$FILE" ]; then
    463     FILE=$NDK_DIR/toolchains/$NAME-4.4.3/prebuilt/$SUBPATH
    464     if [ ! -f "$FILE" ]; then
    465       FILE=
    466     fi
    467   fi
    468   echo "$FILE"
    469 }
    470 
    471 # Find the path to an NDK's toolchain full prefix for a given architecture
    472 # $1: NDK install path
    473 # $2: NDK target architecture name
    474 # Out: install path + binary prefix (e.g.
    475 #      ".../path/to/bin/arm-linux-androideabi-")
    476 get_ndk_toolchain_fullprefix () {
    477   local NDK_DIR="$1"
    478   local ARCH="$2"
    479   local TARGET NAME HOST_OS HOST_ARCH GCC CONFIG
    480 
    481   # NOTE: This will need to be updated if the NDK changes the names or moves
    482   #        the location of its prebuilt toolchains.
    483   #
    484   GCC=
    485   HOST_OS=$(get_ndk_host_system)
    486   HOST_ARCH=$(get_ndk_host_arch)
    487   CONFIG=$(get_arch_gnu_config $ARCH)
    488   GCC=$(get_ndk_toolchain_prebuilt \
    489         "$NDK_DIR" "$ARCH" "$HOST_OS-$HOST_ARCH/bin/$CONFIG-gcc")
    490   if [ -z "$GCC" -a "$HOST_ARCH" = "x86_64" ]; then
    491     GCC=$(get_ndk_toolchain_prebuilt \
    492           "$NDK_DIR" "$ARCH" "$HOST_OS-x86/bin/$CONFIG-gcc")
    493   fi
    494   if [ ! -f "$GCC" -a "$ARCH" = "x86" ]; then
    495     # Special case, the x86 toolchain used to be incorrectly
    496     # named i686-android-linux-gcc!
    497     GCC=$(get_ndk_toolchain_prebuilt \
    498           "$NDK_DIR" "$ARCH" "$HOST_OS-x86/bin/i686-android-linux-gcc")
    499   fi
    500   if [ -z "$GCC" ]; then
    501     panic "Cannot find Android NDK toolchain for '$ARCH' architecture. \
    502 Please verify your NDK installation!"
    503   fi
    504   echo "${GCC%%gcc}"
    505 }
    506 
    507 # $1: NDK install path
    508 # $2: target architecture.
    509 get_ndk_gdbserver () {
    510   local NDK_DIR="$1"
    511   local ARCH=$2
    512   local BINARY
    513 
    514   # The location has moved after NDK r8
    515   BINARY=$NDK_DIR/prebuilt/android-$ARCH/gdbserver/gdbserver
    516   if [ ! -f "$BINARY" ]; then
    517     BINARY=$(get_ndk_toolchain_prebuilt "$NDK_DIR" "$ARCH" gdbserver)
    518   fi
    519   echo "$BINARY"
    520 }
    521 
    522 # Check/probe the path to the Android toolchain installation. Always
    523 # use the NDK versions of gdb and gdbserver. They must match to avoid
    524 # issues when both binaries do not speak the same wire protocol.
    525 #
    526 if [ -z "$TOOLCHAIN" ]; then
    527   ANDROID_TOOLCHAIN=$(get_ndk_toolchain_fullprefix \
    528                       "$ANDROID_NDK_ROOT" "$TARGET_ARCH")
    529   ANDROID_TOOLCHAIN=$(dirname "$ANDROID_TOOLCHAIN")
    530   log "Auto-config: --toolchain=$ANDROID_TOOLCHAIN"
    531 else
    532   # Be flexible, allow one to specify either the install path or the bin
    533   # sub-directory in --toolchain:
    534   #
    535   if [ -d "$TOOLCHAIN/bin" ]; then
    536     TOOLCHAIN=$TOOLCHAIN/bin
    537   fi
    538   ANDROID_TOOLCHAIN=$TOOLCHAIN
    539 fi
    540 
    541 # Cosmetic: Remove trailing directory separator.
    542 ANDROID_TOOLCHAIN=${ANDROID_TOOLCHAIN%/}
    543 
    544 # Find host GDB client binary
    545 GDB=$(which $ANDROID_TOOLCHAIN/*-$GDBEXEPOSTFIX 2>/dev/null | head -1)
    546 if [ -z "$GDB" ]; then
    547   panic "Can't find Android gdb client in your path, check your \
    548 --toolchain path."
    549 fi
    550 log "Host gdb client: $GDB"
    551 
    552 # Find gdbserver binary, we will later push it to /data/local/tmp
    553 # This ensures that both gdbserver and $GDB talk the same binary protocol,
    554 # otherwise weird problems will appear.
    555 #
    556 if [ -z "$GDBSERVER" ]; then
    557   GDBSERVER=$(get_ndk_gdbserver "$ANDROID_NDK_ROOT" "$TARGET_ARCH")
    558   if [ -z "$GDBSERVER" ]; then
    559     panic "Can't find NDK gdbserver binary. use --gdbserver to specify \
    560 valid one!"
    561   fi
    562   log "Auto-config: --gdbserver=$GDBSERVER"
    563 fi
    564 
    565 
    566 
    567 # Check that ADB is in our path
    568 if [ -z "$ADB" ]; then
    569   ADB=$(which adb 2>/dev/null)
    570   if [ -z "$ADB" ]; then
    571     panic "Can't find 'adb' tool in your path. Install it or use \
    572 --adb=<file>"
    573   fi
    574   log "Auto-config: --adb=$ADB"
    575 fi
    576 
    577 # Check that it works minimally
    578 ADB_VERSION=$($ADB version 2>/dev/null)
    579 echo "$ADB_VERSION" | fgrep -q -e "Android Debug Bridge"
    580 if [ $? != 0 ]; then
    581   panic "Your 'adb' tool seems invalid, use --adb=<file> to specify a \
    582 different one: $ADB"
    583 fi
    584 
    585 # If there are more than one device connected, and ANDROID_SERIAL is not
    586 # defined, print an error message.
    587 NUM_DEVICES_PLUS2=$($ADB devices 2>/dev/null | wc -l)
    588 if [ "$NUM_DEVICES_PLUS2" -lt 3 -a -z "$ANDROID_SERIAL" ]; then
    589   echo "ERROR: There is more than one Android device connected to ADB."
    590   echo "Please define ANDROID_SERIAL to specify which one to use."
    591   exit 1
    592 fi
    593 
    594 # A unique ID for this script's session. This needs to be the same in all
    595 # sub-shell commands we're going to launch, so take the PID of the launcher
    596 # process.
    597 TMP_ID=$$
    598 
    599 # Temporary directory, will get cleaned up on exit.
    600 TMPDIR=/tmp/$USER-adb-gdb-tmp-$TMP_ID
    601 mkdir -p "$TMPDIR" && rm -rf "$TMPDIR"/*
    602 
    603 GDBSERVER_PIDFILE="$TMPDIR"/gdbserver-$TMP_ID.pid
    604 
    605 # Run a command through adb shell, strip the extra \r from the output
    606 # and return the correct status code to detect failures. This assumes
    607 # that the adb shell command prints a final \n to stdout.
    608 # $1+: command to run
    609 # Out: command's stdout
    610 # Return: command's status
    611 # Note: the command's stderr is lost
    612 adb_shell () {
    613   local TMPOUT="$(mktemp)"
    614   local LASTLINE RET
    615   local ADB=${ADB:-adb}
    616 
    617   # The weird sed rule is to strip the final \r on each output line
    618   # Since 'adb shell' never returns the command's proper exit/status code,
    619   # we force it to print it as '%%<status>' in the temporary output file,
    620   # which we will later strip from it.
    621   $ADB shell $@ ";" echo "%%\$?" 2>/dev/null | \
    622       sed -e 's![[:cntrl:]]!!g' > $TMPOUT
    623   # Get last line in log, which contains the exit code from the command
    624   LASTLINE=$(sed -e '$!d' $TMPOUT)
    625   # Extract the status code from the end of the line, which must
    626   # be '%%<code>'.
    627   RET=$(echo "$LASTLINE" | \
    628     awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,RSTART+2); } }')
    629   # Remove the status code from the last line. Note that this may result
    630   # in an empty line.
    631   LASTLINE=$(echo "$LASTLINE" | \
    632     awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,1,RSTART-1); } }')
    633   # The output itself: all lines except the status code.
    634   sed -e '$d' $TMPOUT && printf "%s" "$LASTLINE"
    635   # Remove temp file.
    636   rm -f $TMPOUT
    637   # Exit with the appropriate status.
    638   return $RET
    639 }
    640 
    641 # If --force is specified, try to kill any gdbserver process started by the
    642 # same user on the device. Normally, these are killed automatically by the
    643 # script on exit, but there are a few corner cases where this would still
    644 # be needed.
    645 if [ "$FORCE" ]; then
    646   GDBSERVER_PIDS=$(adb_shell ps | awk '$9 ~ /gdbserver/ { print $2; }')
    647   for GDB_PID in $GDBSERVER_PIDS; do
    648     log "Killing previous gdbserver (PID=$GDB_PID)"
    649     adb_shell kill -9 $GDB_PID
    650   done
    651 fi
    652 
    653 if [ "$START" ]; then
    654   log "Starting $PROGRAM_NAME on device."
    655   adb_shell am start -n $PACKAGE_NAME/$ACTIVITY 2>/dev/null
    656   adb_shell ps | grep -q $PACKAGE_NAME
    657   fail_panic "Could not start $PROGRAM_NAME on device. Are you sure the \
    658 package is installed?"
    659 fi
    660 
    661 # Return the timestamp of a given time, as number of seconds since epoch.
    662 # $1: file path
    663 # Out: file timestamp
    664 get_file_timestamp () {
    665   stat -c %Y "$1" 2>/dev/null
    666 }
    667 
    668 # Detect the build type and symbol directory. This is done by finding
    669 # the most recent sub-directory containing debug shared libraries under
    670 # $CHROMIUM_SRC/out/
    671 #
    672 # $1: $BUILDTYPE value, can be empty
    673 # Out: nothing, but this sets SYMBOL_DIR
    674 #
    675 detect_symbol_dir () {
    676   local SUBDIRS SUBDIR LIST DIR DIR_LIBS TSTAMP
    677   # Note: Ninja places debug libraries under out/$BUILDTYPE/lib/, while
    678   # Make places then under out/$BUILDTYPE/lib.target.
    679   if [ "$1" ]; then
    680     SUBDIRS="$1/lib $1/lib.target"
    681   else
    682     SUBDIRS="Release/lib Debug/lib Release/lib.target Debug/lib.target"
    683   fi
    684   LIST=$TMPDIR/scan-subdirs-$$.txt
    685   printf "" > "$LIST"
    686   for SUBDIR in $SUBDIRS; do
    687     DIR=$CHROMIUM_SRC/out/$SUBDIR
    688     if [ -d "$DIR" ]; then
    689       # Ignore build directories that don't contain symbol versions
    690       # of the shared libraries.
    691       DIR_LIBS=$(ls "$DIR"/lib*.so 2>/dev/null)
    692       if [ -z "$DIR_LIBS" ]; then
    693         echo "No shared libs: $DIR"
    694         continue
    695       fi
    696       TSTAMP=$(get_file_timestamp "$DIR")
    697       printf "%s %s\n" "$TSTAMP" "$SUBDIR" >> "$LIST"
    698     fi
    699   done
    700   SUBDIR=$(cat $LIST | sort -r | head -1 | cut -d" " -f2)
    701   rm -f "$LIST"
    702 
    703   if [ -z "$SUBDIR" ]; then
    704     if [ -z "$1" ]; then
    705       panic "Could not find any build directory under \
    706 $CHROMIUM_SRC/out. Please build the program first!"
    707     else
    708       panic "Could not find any $1 directory under \
    709 $CHROMIUM_SRC/out. Check your build type!"
    710     fi
    711   fi
    712 
    713   SYMBOL_DIR=$CHROMIUM_SRC/out/$SUBDIR
    714   log "Auto-config: --symbol-dir=$SYMBOL_DIR"
    715 }
    716 
    717 if [ -z "$SYMBOL_DIR" ]; then
    718   detect_symbol_dir "$BUILDTYPE"
    719 fi
    720 
    721 # Allow several concurrent debugging sessions
    722 TARGET_GDBSERVER=/data/local/tmp/gdbserver-adb-gdb-$TMP_ID
    723 
    724 # Return the build fingerprint contained in a build.prop file.
    725 # $1: path to build.prop file
    726 get_build_fingerprint_from () {
    727   cat "$1" | grep -e '^ro.build.fingerprint=' | cut -d= -f2
    728 }
    729 
    730 
    731 ORG_PULL_LIBS_DIR=$PULL_LIBS_DIR
    732 PULL_LIBS_DIR=${PULL_LIBS_DIR:-$DEFAULT_PULL_LIBS_DIR}
    733 
    734 HOST_FINGERPRINT=
    735 DEVICE_FINGERPRINT=$(adb_shell getprop ro.build.fingerprint)
    736 log "Device build fingerprint: $DEVICE_FINGERPRINT"
    737 
    738 # If --pull-libs-dir is not specified, and this is a platform build, look
    739 # if we can use the symbolic libraries under $ANDROID_PRODUCT_OUT/symbols/
    740 # directly, if the build fingerprint matches the device.
    741 if [ -z "$ORG_PULL_LIBS_DIR" -a \
    742      "$ANDROID_PRODUCT_OUT" -a \
    743      -f "$ANDROID_PRODUCT_OUT/system/build.prop" ]; then
    744   ANDROID_FINGERPRINT=$(get_build_fingerprint_from \
    745                         "$ANDROID_PRODUCT_OUT"/system/build.prop)
    746   log "Android build fingerprint:  $ANDROID_FINGERPRINT"
    747   if [ "$ANDROID_FINGERPRINT" = "$DEVICE_FINGERPRINT" ]; then
    748     log "Perfect match!"
    749     PULL_LIBS_DIR=$ANDROID_PRODUCT_OUT/symbols
    750     HOST_FINGERPRINT=$ANDROID_FINGERPRINT
    751     if [ "$PULL_LIBS" ]; then
    752       log "Ignoring --pull-libs since the device and platform build \
    753 fingerprints match."
    754       NO_PULL_LIBS=true
    755     fi
    756   fi
    757 fi
    758 
    759 # If neither --pull-libs an --no-pull-libs were specified, check the build
    760 # fingerprints of the device, and the cached system libraries on the host.
    761 #
    762 if [ -z "$NO_PULL_LIBS" -a -z "$PULL_LIBS" ]; then
    763   if [ ! -f "$PULL_LIBS_DIR/build.prop" ]; then
    764     log "Auto-config: --pull-libs  (no cached libraries)"
    765     PULL_LIBS=true
    766   else
    767     HOST_FINGERPRINT=$(get_build_fingerprint_from "$PULL_LIBS_DIR/build.prop")
    768     log "Host build fingerprint:   $HOST_FINGERPRINT"
    769     if [ "$HOST_FINGERPRINT" == "$DEVICE_FINGERPRINT" ]; then
    770       log "Auto-config: --no-pull-libs (fingerprint match)"
    771       NO_PULL_LIBS=true
    772     else
    773       log "Auto-config: --pull-libs  (fingerprint mismatch)"
    774       PULL_LIBS=true
    775     fi
    776   fi
    777 fi
    778 
    779 # Extract the system libraries from the device if necessary.
    780 if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then
    781   echo "Extracting system libraries into: $PULL_LIBS_DIR"
    782 fi
    783 
    784 mkdir -p "$PULL_LIBS_DIR"
    785 fail_panic "Can't create --libs-dir directory: $PULL_LIBS_DIR"
    786 
    787 # If requested, work for M-x gdb.  The gdb indirections make it
    788 # difficult to pass --annotate=3 to the gdb binary itself.
    789 GDB_ARGS=
    790 if [ "$ANNOTATE" ]; then
    791   GDB_ARGS=$GDB_ARGS" --annotate=$ANNOTATE"
    792 fi
    793 
    794 # Get the PID from the first argument or else find the PID of the
    795 # browser process.
    796 if [ -z "$PID" ]; then
    797   PROCESSNAME=$PACKAGE_NAME
    798   if [ "$SANDBOXED_INDEX" ]; then
    799     PROCESSNAME=$PROCESSNAME:sandboxed_process$SANDBOXED_INDEX
    800   elif [ "$SANDBOXED" ]; then
    801     PROCESSNAME=$PROCESSNAME:sandboxed_process
    802     PID=$(adb_shell ps | \
    803           awk '$9 ~ /^'$PROCESSNAME'/ { print $2; }' | head -1)
    804   fi
    805   if [ -z "$PID" ]; then
    806     PID=$(adb_shell ps | \
    807           awk '$9 == "'$PROCESSNAME'" { print $2; }' | head -1)
    808   fi
    809   if [ -z "$PID" ]; then
    810     if [ "$START" ]; then
    811       panic "Can't find application process PID, did it crash?"
    812     else
    813       panic "Can't find application process PID, are you sure it is \
    814 running? Try using --start."
    815     fi
    816   fi
    817   log "Found process PID: $PID"
    818 elif [ "$SANDBOXED" ]; then
    819   echo "WARNING: --sandboxed option ignored due to use of --pid."
    820 fi
    821 
    822 # Determine if 'adb shell' runs as root or not.
    823 # If so, we can launch gdbserver directly, otherwise, we have to
    824 # use run-as $PACKAGE_NAME ..., which requires the package to be debuggable.
    825 #
    826 if [ "$SU_PREFIX" ]; then
    827   # Need to check that this works properly.
    828   SU_PREFIX_TEST_LOG=$TMPDIR/su-prefix.log
    829   adb_shell $SU_PREFIX echo "foo" > $SU_PREFIX_TEST_LOG 2>&1
    830   if [ $? != 0 -o "$(cat $SU_PREFIX_TEST_LOG)" != "foo" ]; then
    831     echo "ERROR: Cannot use '$SU_PREFIX' as a valid su prefix:"
    832     echo "$ adb shell $SU_PREFIX echo foo"
    833     cat $SU_PREFIX_TEST_LOG
    834     exit 1
    835   fi
    836   COMMAND_PREFIX=$SU_PREFIX
    837 else
    838   SHELL_UID=$(adb shell cat /proc/self/status | \
    839               awk '$1 == "Uid:" { print $2; }')
    840   log "Shell UID: $SHELL_UID"
    841   if [ "$SHELL_UID" != 0 -o -n "$NO_ROOT" ]; then
    842     COMMAND_PREFIX="run-as $PACKAGE_NAME"
    843   else
    844     COMMAND_PREFIX=
    845   fi
    846 fi
    847 log "Command prefix: '$COMMAND_PREFIX'"
    848 
    849 # Pull device's system libraries that are mapped by our process.
    850 # Pulling all system libraries is too long, so determine which ones
    851 # we need by looking at /proc/$PID/maps instead
    852 if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then
    853   echo "Extracting system libraries into: $PULL_LIBS_DIR"
    854   rm -f $PULL_LIBS_DIR/build.prop
    855   MAPPINGS=$(adb_shell $COMMAND_PREFIX cat /proc/$PID/maps)
    856   if [ $? != 0 ]; then
    857     echo "ERROR: Could not list process's memory mappings."
    858     if [ "$SU_PREFIX" ]; then
    859       panic "Are you sure your --su-prefix is correct?"
    860     else
    861       panic "Use --su-prefix if the application is not debuggable."
    862     fi
    863   fi
    864   SYSTEM_LIBS=$(echo "$MAPPINGS" | \
    865       awk '$6 ~ /\/system\/.*\.so$/ { print $6; }' | sort -u)
    866   for SYSLIB in /system/bin/linker $SYSTEM_LIBS; do
    867     echo "Pulling from device: $SYSLIB"
    868     DST_FILE=$PULL_LIBS_DIR$SYSLIB
    869     DST_DIR=$(dirname "$DST_FILE")
    870     mkdir -p "$DST_DIR" && adb pull $SYSLIB "$DST_FILE" 2>/dev/null
    871     fail_panic "Could not pull $SYSLIB from device !?"
    872   done
    873   echo "Pulling device build.prop"
    874   adb pull /system/build.prop $PULL_LIBS_DIR/build.prop
    875   fail_panic "Could not pull device build.prop !?"
    876 fi
    877 
    878 # Find all the sub-directories of $PULL_LIBS_DIR, up to depth 4
    879 # so we can add them to solib-search-path later.
    880 SOLIB_DIRS=$(find $PULL_LIBS_DIR -mindepth 1 -maxdepth 4 -type d | \
    881              grep -v "^$" | tr '\n' ':')
    882 
    883 # This is a re-implementation of gdbclient, where we use compatible
    884 # versions of gdbserver and $GDBNAME to ensure that everything works
    885 # properly.
    886 #
    887 
    888 # Push gdbserver to the device
    889 log "Pushing gdbserver to $TARGET_GDBSERVER"
    890 adb push $GDBSERVER $TARGET_GDBSERVER &>/dev/null
    891 fail_panic "Could not copy gdbserver to the device!"
    892 
    893 PORT=5039
    894 HOST_PORT=$PORT
    895 TARGET_PORT=$PORT
    896 
    897 # Pull the app_process binary from the device
    898 GDBEXEC=app_process
    899 log "Pulling $GDBEXEC from device"
    900 adb pull /system/bin/$GDBEXEC "$TMPDIR"/$GDBEXEC &>/dev/null
    901 fail_panic "Could not retrieve $GDBEXEC from the device!"
    902 
    903 # Setup network redirection
    904 log "Setting network redirection (host:$HOST_PORT -> device:$TARGET_PORT)"
    905 adb forward tcp:$HOST_PORT tcp:$TARGET_PORT
    906 fail_panic "Could not setup network redirection from \
    907 host:localhost:$HOST_PORT to device:localhost:$TARGET_PORT!"
    908 
    909 # Start gdbserver in the background
    910 # Note that using run-as requires the package to be debuggable.
    911 #
    912 # If not, this will fail horribly. The alternative is to run the
    913 # program as root, which requires of course root privileges.
    914 # Maybe we should add a --root option to enable this?
    915 #
    916 log "Starting gdbserver in the background:"
    917 GDBSERVER_LOG=$TMPDIR/gdbserver-$TMP_ID.log
    918 log "adb shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \
    919 --attach $PID"
    920 ("$ADB" shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \
    921  --attach $PID > $GDBSERVER_LOG 2>&1) &
    922 GDBSERVER_PID=$!
    923 echo "$GDBSERVER_PID" > $GDBSERVER_PIDFILE
    924 log "background job pid: $GDBSERVER_PID"
    925 
    926 # Check that it is still running after a few seconds. If not, this means we
    927 # could not properly attach to it
    928 sleep 2
    929 log "Job control: $(jobs -l)"
    930 STATE=$(jobs -l | awk '$2 == "'$GDBSERVER_PID'" { print $3; }')
    931 if [ "$STATE" != "Running" ]; then
    932   echo "ERROR: GDBServer could not attach to PID $PID!"
    933   echo "Failure log (use --verbose for more information):"
    934   cat $GDBSERVER_LOG
    935   exit 1
    936 fi
    937 
    938 # Generate a file containing useful GDB initialization commands
    939 readonly COMMANDS=$TMPDIR/gdb.init
    940 log "Generating GDB initialization commands file: $COMMANDS"
    941 echo -n "" > $COMMANDS
    942 echo "file $TMPDIR/$GDBEXEC" >> $COMMANDS
    943 echo "directory $CHROMIUM_SRC" >> $COMMANDS
    944 echo "set solib-absolute-prefix $PULL_LIBS_DIR" >> $COMMANDS
    945 echo "set solib-search-path $SOLIB_DIRS:$PULL_LIBS_DIR:$SYMBOL_DIR" \
    946     >> $COMMANDS
    947 echo "echo Attaching and reading symbols, this may take a while.." \
    948     >> $COMMANDS
    949 echo "target remote :$HOST_PORT" >> $COMMANDS
    950 
    951 if [ "$GDBINIT" ]; then
    952   cat "$GDBINIT" >> $COMMANDS
    953 fi
    954 
    955 if [ "$VERBOSE" -gt 0 ]; then
    956   echo "### START $COMMANDS"
    957   cat $COMMANDS
    958   echo "### END $COMMANDS"
    959 fi
    960 
    961 log "Launching gdb client: $GDB $GDBARGS -x $COMMANDS"
    962 $GDB $GDBARGS -x $COMMANDS &&
    963 rm -f "$GDBSERVER_PIDFILE"
    964