connect_lldb.sh (25542B)
1 #!/bin/bash 2 # 3 # Copyright 2023 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 debug it. 9 # Most users should not use this directly, but one of the wrapper scripts like 10 # connect_lldb.sh_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 LLDB_SERVER_JOB_PIDFILE= 26 LLDB_SERVER_PID= 27 TARGET_LLDB_SERVER= 28 COMMAND_PREFIX= 29 COMMAND_SUFFIX= 30 31 clean_exit () { 32 if [ "$TMPDIR" ]; then 33 LLDB_SERVER_JOB_PID=$(cat $LLDB_SERVER_JOB_PIDFILE 2>/dev/null) 34 if [ "$LLDB_SERVER_PID" ]; then 35 log "Killing lldb-server process on-device: $LLDB_SERVER_PID" 36 adb_shell kill $LLDB_SERVER_PID 37 fi 38 if [ "$LLDB_SERVER_JOB_PID" ]; then 39 log "Killing background lldb-server process: $LLDB_SERVER_JOB_PID" 40 kill -9 $LLDB_SERVER_JOB_PID >/dev/null 2>&1 41 rm -f "$LLDB_SERVER_JOB_PIDFILE" 42 fi 43 if [ "$TARGET_LLDB_SERVER" ]; then 44 log "Removing target lldb-server binary: $TARGET_LLDB_SERVER." 45 "$ADB" shell "$COMMAND_PREFIX" rm "$TARGET_LLDB_SERVER" \ 46 "$TARGET_DOMAIN_SOCKET" "$COMMAND_SUFFIX" >/dev/null 2>&1 47 fi 48 log "Cleaning up: $TMPDIR" 49 rm -rf "$TMPDIR" 50 fi 51 trap "" EXIT 52 exit $1 53 } 54 55 # Ensure clean exit on Ctrl-C or normal exit. 56 trap "clean_exit 1" INT HUP QUIT TERM 57 trap "clean_exit \$?" EXIT 58 59 panic () { 60 echo "ERROR: $@" >&2 61 exit 1 62 } 63 64 fail_panic () { 65 if [ $? != 0 ]; then panic "$@"; fi 66 } 67 68 log () { 69 if [ "$VERBOSE" -gt 0 ]; then 70 echo "$@" 71 fi 72 } 73 74 DEFAULT_PULL_LIBS_DIR="/tmp/adb-lldb-support-$USER" 75 76 # NOTE: Allow wrapper scripts to set various default through ADB_LLDB_XXX 77 # environment variables. This is only for cosmetic reasons, i.e. to 78 # display proper default in the --help output. 79 80 # Allow wrapper scripts to set the program name through ADB_LLDB_PROGNAME 81 PROGNAME=${ADB_LLDB_PROGNAME:-$(basename "$0")} 82 83 ADB= 84 ATTACH_DELAY=1 85 HELP= 86 LLDB_INIT= 87 LLDB_SERVER= 88 NDK_DIR= 89 NO_PULL_LIBS= 90 PACKAGE_NAME= 91 PID= 92 PORT= 93 PROCESS_NAME= 94 PROGRAM_NAME="activity" 95 PULL_LIBS= 96 PULL_LIBS_DIR= 97 SU_PREFIX= 98 SYMBOL_DIR= 99 TARGET_ARCH= 100 TOOLCHAIN= 101 VERBOSE=0 102 103 for opt; do 104 optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') 105 case $opt in 106 --adb=*) ADB=$optarg ;; 107 --attach-delay=*) ATTACH_DELAY=$optarg ;; 108 --device=*) export ANDROID_SERIAL=$optarg ;; 109 --help|-h|-?) HELP=true ;; 110 --lldb=*) LLDB=$optarg ;; 111 --lldb-server=*) LLDB_SERVER=$optarg ;; 112 --ndk-dir=*) NDK_DIR=$optarg ;; 113 --no-pull-libs) NO_PULL_LIBS=true ;; 114 --output-directory=*) CHROMIUM_OUTPUT_DIR=$optarg ;; 115 --package-name=*) PACKAGE_NAME=$optarg ;; 116 --pid=*) PID=$optarg ;; 117 --port=*) PORT=$optarg ;; 118 --process-name=*) PROCESS_NAME=$optarg ;; 119 --program-name=*) PROGRAM_NAME=$optarg ;; 120 --pull-libs) PULL_LIBS=true ;; 121 --pull-libs-dir=*) PULL_LIBS_DIR=$optarg ;; 122 --source=*) LLDB_INIT=$optarg ;; 123 --su-prefix=*) SU_PREFIX=$optarg ;; 124 --symbol-dir=*) SYMBOL_DIR=$optarg ;; 125 --target-arch=*) TARGET_ARCH=$optarg ;; 126 --toolchain=*) TOOLCHAIN=$optarg ;; 127 --verbose) VERBOSE=$(( $VERBOSE + 1 )) ;; 128 -*) 129 panic "Unknown option $opt, see --help." >&2 130 ;; 131 *) 132 if [ "$PACKAGE_NAME" ]; then 133 panic "You can only provide a single package name as argument!\ 134 See --help." 135 fi 136 PACKAGE_NAME=$opt 137 ;; 138 esac 139 done 140 141 if [ "$HELP" ]; then 142 if [ "$ADB_LLDB_PROGNAME" ]; then 143 # Assume wrapper scripts all provide a default package name. 144 cat <<EOF 145 Usage: $PROGNAME [options] 146 147 Attach lldb to a running Android $PROGRAM_NAME process. 148 EOF 149 else 150 # Assume this is a direct call to connect_lldb.sh 151 cat <<EOF 152 Usage: $PROGNAME [options] [<package-name>] 153 154 Attach lldb to a running Android $PROGRAM_NAME process. 155 156 If provided, <package-name> must be the name of the Android application's 157 package name to be debugged. You can also use --package-name=<name> to 158 specify it. 159 EOF 160 fi 161 162 cat <<EOF 163 164 This script is used to debug a running $PROGRAM_NAME process. 165 166 This script needs several things to work properly. It will try to pick 167 them up automatically for you though: 168 169 - target lldb-server binary 170 - host lldb client, possibly a wrapper (e.g. lldb.sh) 171 - directory with symbolic version of $PROGRAM_NAME's shared libraries. 172 173 You can also use --ndk-dir=<path> to specify an alternative NDK installation 174 directory. 175 176 The script tries to find the most recent version of the debug version of 177 shared libraries under one of the following directories: 178 179 \$CHROMIUM_SRC/<out>/lib.unstripped/ (used by GN builds) 180 181 Where <out> is determined by CHROMIUM_OUTPUT_DIR, or --output-directory. 182 183 You can set the path manually via --symbol-dir. 184 185 The script tries to extract the target architecture from your target device, 186 but if this fails, will default to 'arm'. Use --target-arch=<name> to force 187 its value. 188 189 Otherwise, the script will complain, but you can use the --lldb-server, 190 --lldb and --symbol-lib options to specify everything manually. 191 192 An alternative to --lldb=<file> is to use --toolchain=<path> to specify 193 the path to the host target-specific cross-toolchain. 194 195 You will also need the 'adb' tool in your path. Otherwise, use the --adb 196 option. The script will complain if there is more than one device connected 197 and a device is not specified with either --device or ANDROID_SERIAL). 198 199 The first time you use it on a device, the script will pull many system 200 libraries required by the process into a temporary directory. This 201 is done to strongly improve the debugging experience, like allowing 202 readable thread stacks and more. The libraries are copied to the following 203 directory by default: 204 205 $DEFAULT_PULL_LIBS_DIR/ 206 207 But you can use the --pull-libs-dir=<path> option to specify an 208 alternative. The script can detect when you change the connected device, 209 and will re-pull the libraries only in this case. You can however force it 210 with the --pull-libs option. 211 212 Any local .lldb-init script will be ignored, but it is possible to pass a 213 lldb command script with the --source=<file> option. Note that its commands 214 will be passed to lldb after the remote connection and library symbol 215 loading have completed. 216 217 Valid options: 218 --help|-h|-? Print this message. 219 --verbose Increase verbosity. 220 221 --symbol-dir=<path> Specify directory with symbol shared libraries. 222 --output-directory=<path> Specify the output directory (e.g. "out/Debug"). 223 --package-name=<name> Specify package name (alternative to 1st argument). 224 --program-name=<name> Specify program name (cosmetic only). 225 --process-name=<name> Specify process name to attach to (uses package-name 226 if not passsed). 227 --pid=<pid> Specify application process pid. 228 --attach-delay=<num> Seconds to wait for lldb-server to attach to the 229 remote process before starting lldb. Default 1. 230 <num> may be a float if your sleep(1) supports it. 231 --source=<file> Specify extra LLDB init script. 232 233 --lldb-server=<file> Specify target lldb-server binary. 234 --lldb=<file> Specify host lldb client binary. 235 --target-arch=<name> Specify NDK target arch. 236 --adb=<file> Specify host ADB binary. 237 --device=<file> ADB device serial to use (-s flag). 238 --port=<port> Specify the tcp port to use. 239 240 --su-prefix=<prefix> Prepend <prefix> to 'adb shell' commands that are 241 run by this script. This can be useful to use 242 the 'su' program on rooted production devices. 243 e.g. --su-prefix="su -c" 244 245 --pull-libs Force system libraries extraction. 246 --no-pull-libs Do not extract any system library. 247 --libs-dir=<path> Specify system libraries extraction directory. 248 249 EOF 250 exit 0 251 fi 252 253 if [ -z "$PACKAGE_NAME" ]; then 254 panic "Please specify a package name on the command line. See --help." 255 fi 256 257 if [[ -z "$SYMBOL_DIR" && -z "$CHROMIUM_OUTPUT_DIR" ]]; then 258 if [[ -e "build.ninja" ]]; then 259 CHROMIUM_OUTPUT_DIR=$PWD 260 else 261 panic "Please specify an output directory by using one of: 262 --output-directory=out/Debug 263 CHROMIUM_OUTPUT_DIR=out/Debug 264 Setting working directory to an output directory. 265 See --help." 266 fi 267 fi 268 269 if ls *.so >/dev/null 2>&1; then 270 panic ".so files found in your working directory. These will conflict with" \ 271 "library lookup logic. Change your working directory and try again." 272 fi 273 274 # Detects the build type and symbol directory. This is done by finding 275 # the most recent sub-directory containing debug shared libraries under 276 # $CHROMIUM_OUTPUT_DIR. 277 # Out: nothing, but this sets SYMBOL_DIR 278 detect_symbol_dir () { 279 # GN places unstripped libraries under out/lib.unstripped 280 local PARENT_DIR="$CHROMIUM_OUTPUT_DIR" 281 if [[ ! -e "$PARENT_DIR" ]]; then 282 PARENT_DIR="$CHROMIUM_SRC/$PARENT_DIR" 283 fi 284 SYMBOL_DIR="$PARENT_DIR/lib.unstripped" 285 if [[ -z "$(ls "$SYMBOL_DIR"/lib*.so 2>/dev/null)" ]]; then 286 SYMBOL_DIR="$PARENT_DIR/lib" 287 if [[ -z "$(ls "$SYMBOL_DIR"/lib*.so 2>/dev/null)" ]]; then 288 panic "Could not find any symbols under \ 289 $PARENT_DIR/lib{.unstripped}. Please build the program first!" 290 fi 291 fi 292 log "Auto-config: --symbol-dir=$SYMBOL_DIR" 293 } 294 295 if [ -z "$SYMBOL_DIR" ]; then 296 detect_symbol_dir 297 elif [[ -z "$(ls "$SYMBOL_DIR"/lib*.so 2>/dev/null)" ]]; then 298 panic "Could not find any symbols under $SYMBOL_DIR" 299 fi 300 301 if [ -z "$NDK_DIR" ]; then 302 ANDROID_NDK_ROOT=$(PYTHONPATH=$CHROMIUM_SRC/build/android python3 -c \ 303 'from pylib.constants import ANDROID_NDK_ROOT; print(ANDROID_NDK_ROOT,)') 304 else 305 if [ ! -d "$NDK_DIR" ]; then 306 panic "Invalid directory: $NDK_DIR" 307 fi 308 if [ ! -d "$NDK_DIR/toolchains" ]; then 309 panic "Not a valid NDK directory: $NDK_DIR" 310 fi 311 ANDROID_NDK_ROOT=$NDK_DIR 312 fi 313 314 if [ "$LLDB_INIT" -a ! -f "$LLDB_INIT" ]; then 315 panic "Unknown --source file: $LLDB_INIT" 316 fi 317 318 # Checks that ADB is in our path 319 if [ -z "$ADB" ]; then 320 ADB=$(which adb 2>/dev/null) 321 if [ -z "$ADB" ]; then 322 panic "Can't find 'adb' tool in your path. Install it or use \ 323 --adb=<file>" 324 fi 325 log "Auto-config: --adb=$ADB" 326 fi 327 328 # Checks that it works minimally 329 ADB_VERSION=$($ADB version 2>/dev/null) 330 echo "$ADB_VERSION" | fgrep -q -e "Android Debug Bridge" 331 if [ $? != 0 ]; then 332 panic "Your 'adb' tool seems invalid, use --adb=<file> to specify a \ 333 different one: $ADB" 334 fi 335 336 # If there are more than one device connected, and ANDROID_SERIAL is not 337 # defined, prints an error message. 338 NUM_DEVICES_PLUS2=$($ADB devices 2>/dev/null | wc -l) 339 if [ "$NUM_DEVICES_PLUS2" -gt 3 -a -z "$ANDROID_SERIAL" ]; then 340 echo "ERROR: There is more than one Android device connected to ADB." 341 echo "Please define ANDROID_SERIAL to specify which one to use." 342 exit 1 343 fi 344 345 # Runs a command through adb shell, strip the extra \r from the output 346 # and return the correct status code to detect failures. This assumes 347 # that the adb shell command prints a final \n to stdout. 348 # $1+: command to run 349 # Out: command's stdout 350 # Return: command's status 351 # Note: the command's stderr is lost 352 # Info: In Python would be done via DeviceUtils.RunShellCommand(). 353 adb_shell () { 354 local TMPOUT="$(mktemp)" 355 local LASTLINE RET 356 local ADB=${ADB:-adb} 357 358 # The weird sed rule is to strip the final \r on each output line 359 # Since 'adb shell' never returns the command's proper exit/status code, 360 # we force it to print it as '%%<status>' in the temporary output file, 361 # which we will later strip from it. 362 $ADB shell $@ ";" echo "%%\$?" 2>/dev/null | \ 363 sed -e 's![[:cntrl:]]!!g' > $TMPOUT 364 # Get last line in log, which contains the exit code from the command 365 LASTLINE=$(sed -e '$!d' $TMPOUT) 366 # Extract the status code from the end of the line, which must 367 # be '%%<code>'. 368 RET=$(echo "$LASTLINE" | \ 369 awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,RSTART+2); } }') 370 # Remove the status code from the last line. Note that this may result 371 # in an empty line. 372 LASTLINE=$(echo "$LASTLINE" | \ 373 awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,1,RSTART-1); } }') 374 # The output itself: all lines except the status code. 375 sed -e '$d' $TMPOUT && printf "%s" "$LASTLINE" 376 # Remove temp file. 377 rm -f $TMPOUT 378 # Exit with the appropriate status. 379 return $RET 380 } 381 382 # Finds the target architecture from a local shared library. 383 # This returns an NDK-compatible architecture name. 384 # Out: NDK Architecture name, or empty string. 385 get_gn_target_arch () { 386 # ls prints a broken pipe error when there are a lot of libs. 387 local RANDOM_LIB=$(ls "$SYMBOL_DIR"/lib*.so 2>/dev/null| head -n1) 388 local SO_DESC=$(file $RANDOM_LIB) 389 case $SO_DESC in 390 *32-bit*ARM,*) echo "arm";; 391 *64-bit*ARM,*) echo "arm64";; 392 *64-bit*aarch64,*) echo "arm64";; 393 *32-bit*Intel,*) echo "x86";; 394 *x86-64,*) echo "x86_64";; 395 *32-bit*MIPS,*) echo "mips";; 396 *) echo ""; 397 esac 398 } 399 400 if [ -z "$TARGET_ARCH" ]; then 401 TARGET_ARCH=$(get_gn_target_arch) 402 if [ -z "$TARGET_ARCH" ]; then 403 TARGET_ARCH=arm 404 fi 405 log "Auto-config: --arch=$TARGET_ARCH" 406 else 407 # Nit: accept Chromium's 'ia32' as a valid target architecture. This 408 # script prefers the NDK 'x86' name instead because it uses it to find 409 # NDK-specific files (host lldb) with it. 410 if [ "$TARGET_ARCH" = "ia32" ]; then 411 TARGET_ARCH=x86 412 log "Auto-config: --arch=$TARGET_ARCH (equivalent to ia32)" 413 fi 414 fi 415 416 # Translates GN target architecure to NDK subdirectory name. 417 # $1: GN target architecture. 418 # Out: NDK subdirectory name. 419 get_ndk_arch_dir () { 420 case "$1" in 421 arm64) echo "aarch64";; 422 x86) echo "i386";; 423 *) echo "$1"; 424 esac 425 } 426 427 # Detects the NDK system name, i.e. the name used to identify the host. 428 # out: NDK system name (e.g. 'linux' or 'darwin') 429 get_ndk_host_system () { 430 local HOST_OS 431 if [ -z "$NDK_HOST_SYSTEM" ]; then 432 HOST_OS=$(uname -s) 433 case $HOST_OS in 434 Linux) NDK_HOST_SYSTEM=linux;; 435 Darwin) NDK_HOST_SYSTEM=darwin;; 436 *) panic "You can't run this script on this system: $HOST_OS";; 437 esac 438 fi 439 echo "$NDK_HOST_SYSTEM" 440 } 441 442 # Detects the NDK host architecture name. 443 # out: NDK arch name (e.g. 'x86_64') 444 get_ndk_host_arch () { 445 echo "x86_64" 446 } 447 448 # $1: NDK install path. 449 get_ndk_host_lldb_client() { 450 local NDK_DIR="$1" 451 local HOST_OS=$(get_ndk_host_system) 452 local HOST_ARCH=$(get_ndk_host_arch) 453 echo "$NDK_DIR/toolchains/llvm/prebuilt/$HOST_OS-$HOST_ARCH/bin/lldb.sh" 454 } 455 456 # $1: NDK install path. 457 # $2: target architecture. 458 get_ndk_lldb_server () { 459 local NDK_DIR="$1" 460 local ARCH=$2 461 local HOST_OS=$(get_ndk_host_system) 462 local HOST_ARCH=$(get_ndk_host_arch) 463 local NDK_ARCH_DIR=$(get_ndk_arch_dir "$ARCH") 464 local i 465 # For lldb-server is under lib64/ for r25, and lib/ for r26+. 466 for i in "lib64" "lib"; do 467 local RET=$(realpath -m $NDK_DIR/toolchains/llvm/prebuilt/$HOST_OS-$HOST_ARCH/$i/clang/*/lib/linux/$NDK_ARCH_DIR/lldb-server) 468 if [ -e "$RET" ]; then 469 echo $RET 470 return 0 471 fi 472 done 473 return 1 474 } 475 476 # Find host LLDB client binary 477 if [ -z "$LLDB" ]; then 478 LLDB=$(get_ndk_host_lldb_client "$ANDROID_NDK_ROOT") 479 if [ -z "$LLDB" ]; then 480 panic "Can't find Android lldb client in your path, check your \ 481 --toolchain or --lldb path." 482 fi 483 log "Host lldb client: $LLDB" 484 fi 485 486 # Find lldb-server binary, we will later push it to /data/local/tmp 487 # This ensures that both lldb-server and $LLDB talk the same binary protocol, 488 # otherwise weird problems will appear. 489 if [ -z "$LLDB_SERVER" ]; then 490 LLDB_SERVER=$(get_ndk_lldb_server "$ANDROID_NDK_ROOT" "$TARGET_ARCH") 491 if [ -z "$LLDB_SERVER" ]; then 492 panic "Can't find NDK lldb-server binary. use --lldb-server to specify \ 493 valid one!" 494 fi 495 log "Auto-config: --lldb-server=$LLDB_SERVER" 496 fi 497 498 # A unique ID for this script's session. This needs to be the same in all 499 # sub-shell commands we're going to launch, so take the PID of the launcher 500 # process. 501 TMP_ID=$$ 502 503 # Temporary directory, will get cleaned up on exit. 504 TMPDIR=/tmp/$USER-adb-lldb-tmp-$TMP_ID 505 mkdir -p "$TMPDIR" && rm -rf "$TMPDIR"/* 506 507 LLDB_SERVER_JOB_PIDFILE="$TMPDIR"/lldb-server-$TMP_ID.pid 508 509 # Returns the timestamp of a given file, as number of seconds since epoch. 510 # $1: file path 511 # Out: file timestamp 512 get_file_timestamp () { 513 stat -c %Y "$1" 2>/dev/null 514 } 515 516 # Allow several concurrent debugging sessions 517 APP_DATA_DIR=$(adb_shell run-as $PACKAGE_NAME /system/bin/sh -c pwd) 518 if [ $? != 0 ]; then 519 echo "Failed to run-as $PACKAGE_NAME, is the app debuggable?" 520 APP_DATA_DIR=$(adb_shell dumpsys package $PACKAGE_NAME | \ 521 sed -ne 's/^ \+dataDir=//p' | head -n1) 522 fi 523 log "App data dir: $APP_DATA_DIR" 524 TARGET_LLDB_SERVER="$APP_DATA_DIR/lldb-server-adb-lldb-$TMP_ID" 525 TMP_TARGET_LLDB_SERVER=/data/local/tmp/lldb-server-adb-lldb-$TMP_ID 526 527 # Select correct app_process for architecture. 528 case $TARGET_ARCH in 529 arm|x86|mips) LLDBEXEC=app_process32;; 530 arm64|x86_64) LLDBEXEC=app_process64; SUFFIX_64_BIT=64;; 531 *) panic "Unknown app_process for architecture!";; 532 esac 533 534 # Default to app_process if bit-width specific process isn't found. 535 adb_shell ls /system/bin/$LLDBEXEC > /dev/null 536 if [ $? != 0 ]; then 537 LLDBEXEC=app_process 538 fi 539 540 # Detect AddressSanitizer setup on the device. In that case app_process is a 541 # script, and the real executable is app_process.real. 542 LLDBEXEC_ASAN=app_process.real 543 adb_shell ls /system/bin/$LLDBEXEC_ASAN > /dev/null 544 if [ $? == 0 ]; then 545 LLDBEXEC=$LLDBEXEC_ASAN 546 fi 547 548 ORG_PULL_LIBS_DIR=$PULL_LIBS_DIR 549 if [[ -n "$ANDROID_SERIAL" ]]; then 550 DEFAULT_PULL_LIBS_DIR="$DEFAULT_PULL_LIBS_DIR/$ANDROID_SERIAL-$SUFFIX_64_BIT" 551 fi 552 PULL_LIBS_DIR=${PULL_LIBS_DIR:-$DEFAULT_PULL_LIBS_DIR} 553 554 HOST_FINGERPRINT= 555 DEVICE_FINGERPRINT=$(adb_shell getprop ro.build.fingerprint) 556 [[ "$DEVICE_FINGERPRINT" ]] || panic "Failed to get the device fingerprint" 557 log "Device build fingerprint: $DEVICE_FINGERPRINT" 558 559 if [ ! -f "$PULL_LIBS_DIR/build.fingerprint" ]; then 560 log "Auto-config: --pull-libs (no cached libraries)" 561 PULL_LIBS=true 562 else 563 HOST_FINGERPRINT=$(< "$PULL_LIBS_DIR/build.fingerprint") 564 log "Host build fingerprint: $HOST_FINGERPRINT" 565 if [ "$HOST_FINGERPRINT" == "$DEVICE_FINGERPRINT" ]; then 566 log "Auto-config: --no-pull-libs (fingerprint match)" 567 NO_PULL_LIBS=true 568 else 569 log "Auto-config: --pull-libs (fingerprint mismatch)" 570 PULL_LIBS=true 571 fi 572 fi 573 574 # Get the PID from the first argument or else find the PID of the 575 # browser process (or the process named by $PROCESS_NAME). 576 if [ -z "$PID" ]; then 577 if [ -z "$PROCESS_NAME" ]; then 578 PROCESS_NAME=$PACKAGE_NAME 579 fi 580 if [ -z "$PID" ]; then 581 PID=$(adb_shell ps | \ 582 awk '$9 == "'$PROCESS_NAME'" { print $2; }' | head -1) 583 fi 584 if [ -z "$PID" ]; then 585 panic "Can't find application process PID." 586 fi 587 log "Found process PID: $PID" 588 fi 589 590 # Determine if 'adb shell' runs as root or not. 591 # If so, we can launch lldb-server directly, otherwise, we have to 592 # use run-as $PACKAGE_NAME ..., which requires the package to be debuggable. 593 # 594 if [ "$SU_PREFIX" ]; then 595 # Need to check that this works properly. 596 SU_PREFIX_TEST_LOG=$TMPDIR/su-prefix.log 597 adb_shell $SU_PREFIX \"echo "foo"\" > $SU_PREFIX_TEST_LOG 2>&1 598 if [ $? != 0 -o "$(cat $SU_PREFIX_TEST_LOG)" != "foo" ]; then 599 echo "ERROR: Cannot use '$SU_PREFIX' as a valid su prefix:" 600 echo "$ adb shell $SU_PREFIX \"echo foo\"" 601 cat $SU_PREFIX_TEST_LOG 602 exit 1 603 fi 604 COMMAND_PREFIX="$SU_PREFIX \"" 605 COMMAND_SUFFIX="\"" 606 else 607 SHELL_UID=$("$ADB" shell cat /proc/self/status | \ 608 awk '$1 == "Uid:" { print $2; }') 609 log "Shell UID: $SHELL_UID" 610 if [ "$SHELL_UID" != 0 -o -n "$NO_ROOT" ]; then 611 COMMAND_PREFIX="run-as $PACKAGE_NAME" 612 COMMAND_SUFFIX= 613 else 614 COMMAND_PREFIX= 615 COMMAND_SUFFIX= 616 fi 617 fi 618 log "Command prefix: '$COMMAND_PREFIX'" 619 log "Command suffix: '$COMMAND_SUFFIX'" 620 621 mkdir -p "$PULL_LIBS_DIR" 622 fail_panic "Can't create --libs-dir directory: $PULL_LIBS_DIR" 623 624 # Pull device's system libraries that are mapped by our process. 625 # Pulling all system libraries is too long, so determine which ones 626 # we need by looking at /proc/$PID/maps instead 627 if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then 628 echo "Extracting system libraries into: $PULL_LIBS_DIR" 629 MAPPINGS=$(adb_shell $COMMAND_PREFIX cat /proc/$PID/maps $COMMAND_SUFFIX) 630 if [ $? != 0 ]; then 631 echo "ERROR: Could not list process's memory mappings." 632 if [ "$SU_PREFIX" ]; then 633 panic "Are you sure your --su-prefix is correct?" 634 else 635 panic "Use --su-prefix if the application is not debuggable." 636 fi 637 fi 638 # Remove the fingerprint file in case pulling one of the libs fails. 639 rm -f "$PULL_LIBS_DIR/build.fingerprint" 640 SYSTEM_LIBS=$(echo "$MAPPINGS" | \ 641 awk '$6 ~ /\/(system|apex|vendor)\/.*\.so$/ { print $6; }' | sort -u) 642 for SYSLIB in /system/bin/linker$SUFFIX_64_BIT $SYSTEM_LIBS; do 643 echo "Pulling from device: $SYSLIB" 644 DST_FILE=$PULL_LIBS_DIR$SYSLIB 645 DST_DIR=$(dirname "$DST_FILE") 646 mkdir -p "$DST_DIR" && "$ADB" pull $SYSLIB "$DST_FILE" 2>/dev/null 647 fail_panic "Could not pull $SYSLIB from device !?" 648 done 649 echo "Writing the device fingerprint" 650 echo "$DEVICE_FINGERPRINT" > "$PULL_LIBS_DIR/build.fingerprint" 651 fi 652 653 # Pull the app_process binary from the device. 654 log "Pulling $LLDBEXEC from device" 655 "$ADB" pull /system/bin/$LLDBEXEC "$TMPDIR"/$LLDBEXEC &>/dev/null 656 fail_panic "Could not retrieve $LLDBEXEC from the device!" 657 658 # Find all the sub-directories of $PULL_LIBS_DIR, up to depth 4 659 # so we can add them to target.exec-search-paths later. 660 SOLIB_DIRS=$(find $PULL_LIBS_DIR -mindepth 1 -maxdepth 4 -type d | \ 661 grep -v "^$" | tr '\n' ' ') 662 663 # Applications with minSdkVersion >= 24 will have their data directories 664 # created with rwx------ permissions, preventing adbd from forwarding to 665 # the lldb-server socket. 666 adb_shell $COMMAND_PREFIX chmod a+x $APP_DATA_DIR $COMMAND_SUFFIX 667 668 # Push lldb-server to the device 669 log "Pushing lldb-server $LLDB_SERVER to $TARGET_LLDB_SERVER" 670 "$ADB" push $LLDB_SERVER $TMP_TARGET_LLDB_SERVER >/dev/null && \ 671 adb_shell $COMMAND_PREFIX cp $TMP_TARGET_LLDB_SERVER $TARGET_LLDB_SERVER $COMMAND_SUFFIX && \ 672 adb_shell rm $TMP_TARGET_LLDB_SERVER 673 fail_panic "Could not copy lldb-server to the device!" 674 675 if [ -z "$PORT" ]; then 676 # Random port to allow multiple concurrent sessions. 677 PORT=$(( $RANDOM % 1000 + 5039 )) 678 fi 679 HOST_PORT=$PORT 680 TARGET_DOMAIN_SOCKET=$APP_DATA_DIR/lldb-socket-$HOST_PORT 681 682 # Setup network redirection 683 log "Setting network redirection (host:$HOST_PORT -> device:$TARGET_DOMAIN_SOCKET)" 684 "$ADB" forward tcp:$HOST_PORT localfilesystem:$TARGET_DOMAIN_SOCKET 685 fail_panic "Could not setup network redirection from \ 686 host:localhost:$HOST_PORT to device:$TARGET_DOMAIN_SOCKET" 687 688 # Start lldb-server in the background 689 # Note that using run-as requires the package to be debuggable. 690 # 691 # If not, this will fail horribly. The alternative is to run the 692 # program as root, which requires of course root privileges. 693 # Maybe we should add a --root option to enable this? 694 695 for i in 1 2; do 696 log "Starting lldb-server in the background:" 697 LLDB_SERVER_LOG=$TMPDIR/lldb-server-$TMP_ID.log 698 log "adb shell $COMMAND_PREFIX $TARGET_LLDB_SERVER g \ 699 $TARGET_DOMAIN_SOCKET \ 700 --attach $PID $COMMAND_SUFFIX" 701 "$ADB" shell $COMMAND_PREFIX $TARGET_LLDB_SERVER g \ 702 $TARGET_DOMAIN_SOCKET \ 703 --attach $PID $COMMAND_SUFFIX > $LLDB_SERVER_LOG 2>&1 & 704 LLDB_SERVER_JOB_PID=$! 705 LLDB_SERVER_PID=$(adb_shell $COMMAND_PREFIX pidof $(basename $TARGET_LLDB_SERVER)) 706 echo "$LLDB_SERVER_JOB_PID" > $LLDB_SERVER_JOB_PIDFILE 707 log "background job pid: $LLDB_SERVER_JOB_PID" 708 709 # Sleep to allow lldb-server to attach to the remote process and be 710 # ready to connect to. 711 log "Sleeping ${ATTACH_DELAY}s to ensure lldb-server is alive" 712 sleep "$ATTACH_DELAY" 713 log "Job control: $(jobs -l)" 714 STATE=$(jobs -l | awk '$2 == "'$LLDB_SERVER_JOB_PID'" { print $3; }') 715 if [ "$STATE" != "Running" ]; then 716 pid_msg=$(grep "is already traced by process" $LLDB_SERVER_LOG 2>/dev/null) 717 if [[ -n "$pid_msg" ]]; then 718 old_pid=${pid_msg##* } 719 old_pid=${old_pid//[$'\r\n']} # Trim trailing \r. 720 echo "Killing previous lldb-server process (pid=$old_pid)" 721 adb_shell $COMMAND_PREFIX kill -9 $old_pid $COMMAND_SUFFIX 722 continue 723 fi 724 echo "ERROR: lldb-server either failed to run or attach to PID $PID!" 725 echo "Here is the output from lldb-server (also try --verbose for more):" 726 echo "===== lldb-server.log start =====" 727 cat $LLDB_SERVER_LOG 728 echo ="===== lldb-server.log end ======" 729 exit 1 730 fi 731 break 732 done 733 734 # Generate a file containing useful LLDB initialization commands 735 readonly COMMANDS=$TMPDIR/lldb.init 736 log "Generating LLDB initialization commands file: $COMMANDS" 737 cat > "$COMMANDS" <<EOF 738 settings append target.exec-search-paths $SYMBOL_DIR $SOLIB_DIRS $PULL_LIBS_DIR 739 settings set target.source-map ../.. $CHROMIUM_SRC 740 target create '$TMPDIR/$LLDBEXEC' 741 target modules search-paths add / $TMPDIR/$LLDBEXEC/ 742 script print("Connecting to :$HOST_PORT... (symbol load can take a while)") 743 gdb-remote $HOST_PORT 744 EOF 745 746 if [ "$LLDB_INIT" ]; then 747 cat "$LLDB_INIT" >> "$COMMANDS" 748 fi 749 750 if [ "$VERBOSE" -gt 0 ]; then 751 echo "### START $COMMANDS" 752 cat "$COMMANDS" 753 echo "### END $COMMANDS" 754 fi 755 756 log "Launching lldb client: $LLDB $LLDB_ARGS --source $COMMANDS" 757 echo "Server log: $LLDB_SERVER_LOG" 758 $LLDB $LLDB_ARGS --source "$COMMANDS"