tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

adb_gdb (29016B)


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