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"/../.. >/dev/null && 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 You can also use --ndk-dir=<path> to specify an alternative NDK installation
    249 directory.
    250 
    251 The script tries to find the most recent version of the debug version of
    252 shared libraries under one of the following directories:
    253 
    254   \$CHROMIUM_SRC/<out>/Release/lib/           (used by Ninja builds)
    255   \$CHROMIUM_SRC/<out>/Debug/lib/             (used by Ninja builds)
    256   \$CHROMIUM_SRC/<out>/Release/lib.target/    (used by Make builds)
    257   \$CHROMIUM_SRC/<out>/Debug/lib.target/      (used by Make builds)
    258 
    259 Where <out> is 'out' by default, unless the --out=<name> option is used or
    260 the CHROMIUM_OUT_DIR environment variable is defined.
    261 
    262 You can restrict this search by using --release or --debug to specify the
    263 build type, or simply use --symbol-dir=<path> to specify the file manually.
    264 
    265 The script tries to extract the target architecture from your GYP_DEFINES,
    266 but if this fails, will default to 'arm'. Use --target-arch=<name> to force
    267 its value.
    268 
    269 Otherwise, the script will complain, but you can use the --gdbserver,
    270 --gdb and --symbol-lib options to specify everything manually.
    271 
    272 An alternative to --gdb=<file> is to use --toollchain=<path> to specify
    273 the path to the host target-specific cross-toolchain.
    274 
    275 You will also need the 'adb' tool in your path. Otherwise, use the --adb
    276 option. The script will complain if there is more than one device connected
    277 and ANDROID_SERIAL is not defined.
    278 
    279 The first time you use it on a device, the script will pull many system
    280 libraries required by the process into a temporary directory. This
    281 is done to strongly improve the debugging experience, like allowing
    282 readable thread stacks and more. The libraries are copied to the following
    283 directory by default:
    284 
    285   $DEFAULT_PULL_LIBS_DIR/
    286 
    287 But you can use the --pull-libs-dir=<path> option to specify an
    288 alternative. The script can detect when you change the connected device,
    289 and will re-pull the libraries only in this case. You can however force it
    290 with the --pull-libs option.
    291 
    292 Any local .gdbinit script will be ignored, but it is possible to pass a
    293 gdb command script with the --script=<file> option. Note that its commands
    294 will be passed to gdb after the remote connection and library symbol
    295 loading have completed.
    296 
    297 Valid options:
    298   --help|-h|-?          Print this message.
    299   --verbose             Increase verbosity.
    300 
    301   --sandboxed           Debug first sandboxed process we find.
    302   --sandboxed=<num>     Debug specific sandboxed process.
    303   --symbol-dir=<path>   Specify directory with symbol shared libraries.
    304   --out-dir=<path>      Specify the out directory.
    305   --package-name=<name> Specify package name (alternative to 1st argument).
    306   --program-name=<name> Specify program name (cosmetic only).
    307   --pid=<pid>           Specify application process pid.
    308   --force               Kill any previous debugging session, if any.
    309   --start               Start package's activity on device.
    310   --ui                  Use gdbtui instead of gdb
    311   --activity=<name>     Activity name for --start [$DEFAULT_ACTIVITY].
    312   --annotate=<num>      Enable gdb annotation.
    313   --script=<file>       Specify extra GDB init script.
    314 
    315   --gdbserver=<file>    Specify target gdbserver binary.
    316   --gdb=<file>          Specify host gdb client binary.
    317   --target-arch=<name>  Specify NDK target arch.
    318   --adb=<file>          Specify host ADB binary.
    319 
    320   --su-prefix=<prefix>  Prepend <prefix> to 'adb shell' commands that are
    321                         run by this script. This can be useful to use
    322                         the 'su' program on rooted production devices.
    323                         e.g. --su-prefix="su -c"
    324 
    325   --pull-libs           Force system libraries extraction.
    326   --no-pull-libs        Do not extract any system library.
    327   --libs-dir=<path>     Specify system libraries extraction directory.
    328 
    329   --debug               Use libraries under out/Debug.
    330   --release             Use libraries under out/Release.
    331 
    332 EOF
    333   exit 0
    334 fi
    335 
    336 if [ -z "$PACKAGE_NAME" ]; then
    337   panic "Please specify a package name on the command line. See --help."
    338 fi
    339 
    340 if [ -z "$NDK_DIR" ]; then
    341   ANDROID_NDK_ROOT=$(PYTHONPATH=build/android python -c \
    342 'from pylib.constants import ANDROID_NDK_ROOT; print ANDROID_NDK_ROOT,')
    343 else
    344   if [ ! -d "$NDK_DIR" ]; then
    345     panic "Invalid directory: $NDK_DIR"
    346   fi
    347   if [ ! -f "$NDK_DIR/ndk-build" ]; then
    348     panic "Not a valid NDK directory: $NDK_DIR"
    349   fi
    350   ANDROID_NDK_ROOT=$NDK_DIR
    351 fi
    352 
    353 if [ "$GDBINIT" -a ! -f "$GDBINIT" ]; then
    354   panic "Unknown --script file: $GDBINIT"
    355 fi
    356 
    357 # Find the target architecture from our $GYP_DEFINES
    358 # This returns an NDK-compatible architecture name.
    359 # out: NDK Architecture name, or empty string.
    360 get_gyp_target_arch () {
    361   local ARCH=$(echo $GYP_DEFINES | tr ' ' '\n' | grep '^target_arch=' |\
    362                cut -d= -f2)
    363   case $ARCH in
    364     ia32|i?86|x86) echo "x86";;
    365     mips|arm|arm64|x86_64) echo "$ARCH";;
    366     *) echo "";
    367   esac
    368 }
    369 
    370 if [ -z "$TARGET_ARCH" ]; then
    371   TARGET_ARCH=$(get_gyp_target_arch)
    372   if [ -z "$TARGET_ARCH" ]; then
    373     TARGET_ARCH=arm
    374   fi
    375 else
    376   # Nit: accept Chromium's 'ia32' as a valid target architecture. This
    377   # script prefers the NDK 'x86' name instead because it uses it to find
    378   # NDK-specific files (host gdb) with it.
    379   if [ "$TARGET_ARCH" = "ia32" ]; then
    380     TARGET_ARCH=x86
    381     log "Auto-config: --arch=$TARGET_ARCH  (equivalent to ia32)"
    382   fi
    383 fi
    384 
    385 # Detect the NDK system name, i.e. the name used to identify the host.
    386 # out: NDK system name (e.g. 'linux' or 'darwin')
    387 get_ndk_host_system () {
    388   local HOST_OS
    389   if [ -z "$NDK_HOST_SYSTEM" ]; then
    390     HOST_OS=$(uname -s)
    391     case $HOST_OS in
    392       Linux) NDK_HOST_SYSTEM=linux;;
    393       Darwin) NDK_HOST_SYSTEM=darwin;;
    394       *) panic "You can't run this script on this system: $HOST_OS";;
    395     esac
    396   fi
    397   echo "$NDK_HOST_SYSTEM"
    398 }
    399 
    400 # Detect the NDK host architecture name.
    401 # out: NDK arch name (e.g. 'x86' or 'x86_64')
    402 get_ndk_host_arch () {
    403   local HOST_ARCH HOST_OS
    404   if [ -z "$NDK_HOST_ARCH" ]; then
    405     HOST_OS=$(get_ndk_host_system)
    406     HOST_ARCH=$(uname -p)
    407     case $HOST_ARCH in
    408       i?86) NDK_HOST_ARCH=x86;;
    409       x86_64|amd64) NDK_HOST_ARCH=x86_64;;
    410       *) panic "You can't run this script on this host architecture: $HOST_ARCH";;
    411     esac
    412     # Darwin trick: "uname -p" always returns i386 on 64-bit installations.
    413     if [ "$HOST_OS" = darwin -a "$NDK_HOST_ARCH" = "x86" ]; then
    414       # Use '/usr/bin/file', not just 'file' to avoid buggy MacPorts
    415       # implementations of the tool. See http://b.android.com/53769
    416       HOST_64BITS=$(/usr/bin/file -L "$SHELL" | grep -e "x86[_-]64")
    417       if [ "$HOST_64BITS" ]; then
    418         NDK_HOST_ARCH=x86_64
    419       fi
    420     fi
    421   fi
    422   echo "$NDK_HOST_ARCH"
    423 }
    424 
    425 # Convert an NDK architecture name into a GNU configure triplet.
    426 # $1: NDK architecture name (e.g. 'arm')
    427 # Out: Android GNU configure triplet (e.g. 'arm-linux-androideabi')
    428 get_arch_gnu_config () {
    429   case $1 in
    430     arm)
    431       echo "arm-linux-androideabi"
    432       ;;
    433     arm64)
    434       echo "aarch64-linux-android"
    435       ;;
    436     x86)
    437       echo "i686-linux-android"
    438       ;;
    439     x86_64)
    440       echo "x86_64-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.9/prebuilt/$SUBPATH
    477   if [ ! -f "$FILE" ]; then
    478     FILE=$NDK_DIR/toolchains/$NAME-4.8/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 # Select correct app_process for architecture.
    915 case $TARGET_ARCH in
    916       arm|x86|mips) GDBEXEC=app_process;;
    917       arm64|x86_64) GDBEXEC=app_process64;;
    918       *) fail_panic "Unknown app_process for architecture!";;
    919 esac
    920 
    921 # Detect AddressSanitizer setup on the device. In that case app_process is a
    922 # script, and the real executable is app_process.real.
    923 GDBEXEC_ASAN=app_process.real
    924 adb_shell ls /system/bin/$GDBEXEC_ASAN
    925 if [ $? == 0 ]; then
    926     GDBEXEC=$GDBEXEC_ASAN
    927 fi
    928 
    929 # Pull the app_process binary from the device.
    930 log "Pulling $GDBEXEC from device"
    931 adb pull /system/bin/$GDBEXEC "$TMPDIR"/$GDBEXEC &>/dev/null
    932 fail_panic "Could not retrieve $GDBEXEC from the device!"
    933 
    934 # Setup network redirection
    935 log "Setting network redirection (host:$HOST_PORT -> device:$TARGET_PORT)"
    936 adb forward tcp:$HOST_PORT tcp:$TARGET_PORT
    937 fail_panic "Could not setup network redirection from \
    938 host:localhost:$HOST_PORT to device:localhost:$TARGET_PORT!"
    939 
    940 # Start gdbserver in the background
    941 # Note that using run-as requires the package to be debuggable.
    942 #
    943 # If not, this will fail horribly. The alternative is to run the
    944 # program as root, which requires of course root privileges.
    945 # Maybe we should add a --root option to enable this?
    946 #
    947 log "Starting gdbserver in the background:"
    948 GDBSERVER_LOG=$TMPDIR/gdbserver-$TMP_ID.log
    949 log "adb shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \
    950 --attach $PID"
    951 ("$ADB" shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \
    952  --attach $PID > $GDBSERVER_LOG 2>&1) &
    953 GDBSERVER_PID=$!
    954 echo "$GDBSERVER_PID" > $GDBSERVER_PIDFILE
    955 log "background job pid: $GDBSERVER_PID"
    956 
    957 # Check that it is still running after a few seconds. If not, this means we
    958 # could not properly attach to it
    959 sleep 2
    960 log "Job control: $(jobs -l)"
    961 STATE=$(jobs -l | awk '$2 == "'$GDBSERVER_PID'" { print $3; }')
    962 if [ "$STATE" != "Running" ]; then
    963   echo "ERROR: GDBServer could not attach to PID $PID!"
    964   if [ $(adb_shell su -c getenforce) != "Permissive" ];  then
    965     echo "Device mode is Enforcing. Changing Device mode to Permissive "
    966     $(adb_shell su -c setenforce 0)
    967     if [ $(adb_shell su -c getenforce) != "Permissive" ]; then
    968       echo "ERROR: Failed to Change Device mode to Permissive"
    969       echo "Failure log (use --verbose for more information):"
    970       cat $GDBSERVER_LOG
    971       exit 1
    972     fi
    973   else
    974     echo "Failure log (use --verbose for more information):"
    975     cat $GDBSERVER_LOG
    976     exit 1
    977   fi
    978 fi
    979 
    980 # Generate a file containing useful GDB initialization commands
    981 readonly COMMANDS=$TMPDIR/gdb.init
    982 log "Generating GDB initialization commands file: $COMMANDS"
    983 echo -n "" > $COMMANDS
    984 echo "set print pretty 1" >> $COMMANDS
    985 echo "python" >> $COMMANDS
    986 echo "import sys" >> $COMMANDS
    987 echo "sys.path.insert(0, '$CHROMIUM_SRC/tools/gdb/')" >> $COMMANDS
    988 echo "try:" >> $COMMANDS
    989 echo "  import gdb_chrome" >> $COMMANDS
    990 echo "finally:" >> $COMMANDS
    991 echo "  sys.path.pop(0)" >> $COMMANDS
    992 echo "end" >> $COMMANDS
    993 echo "file $TMPDIR/$GDBEXEC" >> $COMMANDS
    994 echo "directory $CHROMIUM_SRC" >> $COMMANDS
    995 echo "set solib-absolute-prefix $PULL_LIBS_DIR" >> $COMMANDS
    996 echo "set solib-search-path $SOLIB_DIRS:$PULL_LIBS_DIR:$SYMBOL_DIR" \
    997     >> $COMMANDS
    998 echo "echo Attaching and reading symbols, this may take a while.." \
    999     >> $COMMANDS
   1000 echo "target remote :$HOST_PORT" >> $COMMANDS
   1001 
   1002 if [ "$GDBINIT" ]; then
   1003   cat "$GDBINIT" >> $COMMANDS
   1004 fi
   1005 
   1006 if [ "$VERBOSE" -gt 0 ]; then
   1007   echo "### START $COMMANDS"
   1008   cat $COMMANDS
   1009   echo "### END $COMMANDS"
   1010 fi
   1011 
   1012 log "Launching gdb client: $GDB $GDB_ARGS -x $COMMANDS"
   1013 $GDB $GDB_ARGS -x $COMMANDS &&
   1014 rm -f "$GDBSERVER_PIDFILE"
   1015