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