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