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