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