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