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