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