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