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