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 15PROGNAME=$(basename "$0") 16PROGDIR=$(dirname "$0") 17 18# Location of Chromium-top-level sources. 19CHROMIUM_SRC=$(cd "$PROGDIR"/../.. >/dev/null && pwd 2>/dev/null) 20 21# Location of Chromium out/ directory. 22if [ -z "$CHROMIUM_OUT_DIR" ]; then 23 CHROMIUM_OUT_DIR=out 24fi 25 26TMPDIR= 27GDBSERVER_PIDFILE= 28TARGET_GDBSERVER= 29 30clean_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. 49trap "clean_exit 1" INT HUP QUIT TERM 50trap "clean_exit \$?" EXIT 51 52panic () { 53 echo "ERROR: $@" >&2 54 exit 1 55} 56 57fail_panic () { 58 if [ $? != 0 ]; then panic "$@"; fi 59} 60 61log () { 62 if [ "$VERBOSE" -gt 0 ]; then 63 echo "$@" 64 fi 65} 66 67DEFAULT_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# 80DEFAULT_ACTIVITY=${ADB_GDB_ACTIVITY:-".Main"} 81 82# Allow wrapper scripts to set the program name through ADB_GDB_PROGNAME 83PROGNAME=${ADB_GDB_PROGNAME:-$(basename "$0")} 84 85ACTIVITY=$DEFAULT_ACTIVITY 86ADB= 87ANNOTATE= 88# Note: Ignore BUILDTYPE variable, because the Ninja build doesn't use it. 89BUILDTYPE= 90FORCE= 91GDBEXEPOSTFIX=gdb 92GDBINIT= 93GDBSERVER= 94HELP= 95NDK_DIR= 96NO_PULL_LIBS= 97PACKAGE_NAME= 98PID= 99PROGRAM_NAME="activity" 100PULL_LIBS= 101PULL_LIBS_DIR= 102SANDBOXED= 103SANDBOXED_INDEX= 104START= 105SU_PREFIX= 106SYMBOL_DIR= 107TARGET_ARCH= 108TOOLCHAIN= 109VERBOSE=0 110 111for 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 207done 208 209print_help_options () { 210 cat <<EOF 211EOF 212} 213 214if [ "$HELP" ]; then 215 if [ "$ADB_GDB_PROGNAME" ]; then 216 # Assume wrapper scripts all provide a default package name. 217 cat <<EOF 218Usage: $PROGNAME [options] 219 220Attach gdb to a running Android $PROGRAM_NAME process. 221EOF 222 else 223 # Assume this is a direct call to adb_gdb 224 cat <<EOF 225Usage: $PROGNAME [options] [<package-name>] 226 227Attach gdb to a running Android $PROGRAM_NAME process. 228 229If provided, <package-name> must be the name of the Android application's 230package name to be debugged. You can also use --package-name=<name> to 231specify it. 232EOF 233 fi 234 235 cat <<EOF 236 237This script is used to debug a running $PROGRAM_NAME process. 238This can be a regular Android application process, or a sandboxed 239service, if you use the --sandboxed or --sandboxed=<num> option. 240 241This script needs several things to work properly. It will try to pick 242them 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 248You can also use --ndk-dir=<path> to specify an alternative NDK installation 249directory. 250 251The script tries to find the most recent version of the debug version of 252shared 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 259Where <out> is 'out' by default, unless the --out=<name> option is used or 260the CHROMIUM_OUT_DIR environment variable is defined. 261 262You can restrict this search by using --release or --debug to specify the 263build type, or simply use --symbol-dir=<path> to specify the file manually. 264 265The script tries to extract the target architecture from your GYP_DEFINES, 266but if this fails, will default to 'arm'. Use --target-arch=<name> to force 267its value. 268 269Otherwise, the script will complain, but you can use the --gdbserver, 270--gdb and --symbol-lib options to specify everything manually. 271 272An alternative to --gdb=<file> is to use --toollchain=<path> to specify 273the path to the host target-specific cross-toolchain. 274 275You will also need the 'adb' tool in your path. Otherwise, use the --adb 276option. The script will complain if there is more than one device connected 277and ANDROID_SERIAL is not defined. 278 279The first time you use it on a device, the script will pull many system 280libraries required by the process into a temporary directory. This 281is done to strongly improve the debugging experience, like allowing 282readable thread stacks and more. The libraries are copied to the following 283directory by default: 284 285 $DEFAULT_PULL_LIBS_DIR/ 286 287But you can use the --pull-libs-dir=<path> option to specify an 288alternative. The script can detect when you change the connected device, 289and will re-pull the libraries only in this case. You can however force it 290with the --pull-libs option. 291 292Any local .gdbinit script will be ignored, but it is possible to pass a 293gdb command script with the --script=<file> option. Note that its commands 294will be passed to gdb after the remote connection and library symbol 295loading have completed. 296 297Valid 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 332EOF 333 exit 0 334fi 335 336if [ -z "$PACKAGE_NAME" ]; then 337 panic "Please specify a package name on the command line. See --help." 338fi 339 340if [ -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,') 343else 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 351fi 352 353if [ "$GDBINIT" -a ! -f "$GDBINIT" ]; then 354 panic "Unknown --script file: $GDBINIT" 355fi 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. 360get_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 370if [ -z "$TARGET_ARCH" ]; then 371 TARGET_ARCH=$(get_gyp_target_arch) 372 if [ -z "$TARGET_ARCH" ]; then 373 TARGET_ARCH=arm 374 fi 375else 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 383fi 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') 387get_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') 402get_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') 428get_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') 454get_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. 470get_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-") 491get_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. \ 517Please verify your NDK installation!" 518 fi 519 echo "${GCC%%gcc}" 520} 521 522# $1: NDK install path 523# $2: target architecture. 524get_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# 541if [ -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" 546else 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 554fi 555 556# Cosmetic: Remove trailing directory separator. 557ANDROID_TOOLCHAIN=${ANDROID_TOOLCHAIN%/} 558 559# Find host GDB client binary 560if [ -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" 567fi 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# 573if [ -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 \ 577valid one!" 578 fi 579 log "Auto-config: --gdbserver=$GDBSERVER" 580fi 581 582 583 584# Check that ADB is in our path 585if [ -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" 592fi 593 594# Check that it works minimally 595ADB_VERSION=$($ADB version 2>/dev/null) 596echo "$ADB_VERSION" | fgrep -q -e "Android Debug Bridge" 597if [ $? != 0 ]; then 598 panic "Your 'adb' tool seems invalid, use --adb=<file> to specify a \ 599different one: $ADB" 600fi 601 602# If there are more than one device connected, and ANDROID_SERIAL is not 603# defined, print an error message. 604NUM_DEVICES_PLUS2=$($ADB devices 2>/dev/null | wc -l) 605if [ "$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 609fi 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. 614TMP_ID=$$ 615 616# Temporary directory, will get cleaned up on exit. 617TMPDIR=/tmp/$USER-adb-gdb-tmp-$TMP_ID 618mkdir -p "$TMPDIR" && rm -rf "$TMPDIR"/* 619 620GDBSERVER_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 629adb_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. 662if [ "$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 668fi 669 670if [ "$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 \ 675package is installed?" 676fi 677 678# Return the timestamp of a given time, as number of seconds since epoch. 679# $1: file path 680# Out: file timestamp 681get_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# 692detect_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 734if [ -z "$SYMBOL_DIR" ]; then 735 detect_symbol_dir "$BUILDTYPE" 736fi 737 738# Allow several concurrent debugging sessions 739TARGET_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 743get_build_fingerprint_from () { 744 cat "$1" | grep -e '^ro.build.fingerprint=' | cut -d= -f2 745} 746 747 748ORG_PULL_LIBS_DIR=$PULL_LIBS_DIR 749PULL_LIBS_DIR=${PULL_LIBS_DIR:-$DEFAULT_PULL_LIBS_DIR} 750 751HOST_FINGERPRINT= 752DEVICE_FINGERPRINT=$(adb_shell getprop ro.build.fingerprint) 753log "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. 758if [ -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 \ 770fingerprints match." 771 NO_PULL_LIBS=true 772 fi 773 fi 774fi 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# 779if [ -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 794fi 795 796# Extract the system libraries from the device if necessary. 797if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then 798 echo "Extracting system libraries into: $PULL_LIBS_DIR" 799fi 800 801mkdir -p "$PULL_LIBS_DIR" 802fail_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. 806GDB_ARGS= 807if [ "$ANNOTATE" ]; then 808 GDB_ARGS=$GDB_ARGS" --annotate=$ANNOTATE" 809fi 810 811# Get the PID from the first argument or else find the PID of the 812# browser process. 813if [ -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 \ 831running? Try using --start." 832 fi 833 fi 834 log "Found process PID: $PID" 835elif [ "$SANDBOXED" ]; then 836 echo "WARNING: --sandboxed option ignored due to use of --pid." 837fi 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# 843if [ "$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" 854else 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 863fi 864log "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 869if [ "$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 !?" 893fi 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. 897SOLIB_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 906log "Pushing gdbserver $GDBSERVER to $TARGET_GDBSERVER" 907adb push $GDBSERVER $TARGET_GDBSERVER &>/dev/null 908fail_panic "Could not copy gdbserver to the device!" 909 910PORT=5039 911HOST_PORT=$PORT 912TARGET_PORT=$PORT 913 914# Select correct app_process for architecture. 915case $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!";; 919esac 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. 923GDBEXEC_ASAN=app_process.real 924adb_shell ls /system/bin/$GDBEXEC_ASAN 925if [ $? == 0 ]; then 926 GDBEXEC=$GDBEXEC_ASAN 927fi 928 929# Pull the app_process binary from the device. 930log "Pulling $GDBEXEC from device" 931adb pull /system/bin/$GDBEXEC "$TMPDIR"/$GDBEXEC &>/dev/null 932fail_panic "Could not retrieve $GDBEXEC from the device!" 933 934# Setup network redirection 935log "Setting network redirection (host:$HOST_PORT -> device:$TARGET_PORT)" 936adb forward tcp:$HOST_PORT tcp:$TARGET_PORT 937fail_panic "Could not setup network redirection from \ 938host: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# 947log "Starting gdbserver in the background:" 948GDBSERVER_LOG=$TMPDIR/gdbserver-$TMP_ID.log 949log "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) & 953GDBSERVER_PID=$! 954echo "$GDBSERVER_PID" > $GDBSERVER_PIDFILE 955log "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 959sleep 2 960log "Job control: $(jobs -l)" 961STATE=$(jobs -l | awk '$2 == "'$GDBSERVER_PID'" { print $3; }') 962if [ "$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 978fi 979 980# Generate a file containing useful GDB initialization commands 981readonly COMMANDS=$TMPDIR/gdb.init 982log "Generating GDB initialization commands file: $COMMANDS" 983echo -n "" > $COMMANDS 984echo "set print pretty 1" >> $COMMANDS 985echo "python" >> $COMMANDS 986echo "import sys" >> $COMMANDS 987echo "sys.path.insert(0, '$CHROMIUM_SRC/tools/gdb/')" >> $COMMANDS 988echo "try:" >> $COMMANDS 989echo " import gdb_chrome" >> $COMMANDS 990echo "finally:" >> $COMMANDS 991echo " sys.path.pop(0)" >> $COMMANDS 992echo "end" >> $COMMANDS 993echo "file $TMPDIR/$GDBEXEC" >> $COMMANDS 994echo "directory $CHROMIUM_SRC" >> $COMMANDS 995echo "set solib-absolute-prefix $PULL_LIBS_DIR" >> $COMMANDS 996echo "set solib-search-path $SOLIB_DIRS:$PULL_LIBS_DIR:$SYMBOL_DIR" \ 997 >> $COMMANDS 998echo "echo Attaching and reading symbols, this may take a while.." \ 999 >> $COMMANDS 1000echo "target remote :$HOST_PORT" >> $COMMANDS 1001 1002if [ "$GDBINIT" ]; then 1003 cat "$GDBINIT" >> $COMMANDS 1004fi 1005 1006if [ "$VERBOSE" -gt 0 ]; then 1007 echo "### START $COMMANDS" 1008 cat $COMMANDS 1009 echo "### END $COMMANDS" 1010fi 1011 1012log "Launching gdb client: $GDB $GDB_ARGS -x $COMMANDS" 1013$GDB $GDB_ARGS -x $COMMANDS && 1014rm -f "$GDBSERVER_PIDFILE" 1015