xref: /aosp_15_r20/external/cronet/build/android/connect_lldb.sh (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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
15PROGNAME=$(basename "$0")
16PROGDIR=$(dirname "$0")
17
18# Force locale to C to allow recognizing output from subprocesses.
19LC_ALL=C
20
21# Location of Chromium-top-level sources.
22CHROMIUM_SRC=$(cd "$PROGDIR"/../.. >/dev/null && pwd 2>/dev/null)
23
24TMPDIR=
25LLDB_SERVER_JOB_PIDFILE=
26LLDB_SERVER_PID=
27TARGET_LLDB_SERVER=
28COMMAND_PREFIX=
29COMMAND_SUFFIX=
30
31clean_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.
56trap "clean_exit 1" INT HUP QUIT TERM
57trap "clean_exit \$?" EXIT
58
59panic () {
60  echo "ERROR: $@" >&2
61  exit 1
62}
63
64fail_panic () {
65  if [ $? != 0 ]; then panic "$@"; fi
66}
67
68log () {
69  if [ "$VERBOSE" -gt 0 ]; then
70    echo "$@"
71  fi
72}
73
74DEFAULT_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
81PROGNAME=${ADB_LLDB_PROGNAME:-$(basename "$0")}
82
83ADB=
84ATTACH_DELAY=1
85HELP=
86LLDB_INIT=
87LLDB_SERVER=
88NDK_DIR=
89NO_PULL_LIBS=
90PACKAGE_NAME=
91PID=
92PORT=
93PROCESS_NAME=
94PROGRAM_NAME="activity"
95PULL_LIBS=
96PULL_LIBS_DIR=
97SU_PREFIX=
98SYMBOL_DIR=
99TARGET_ARCH=
100TOOLCHAIN=
101VERBOSE=0
102
103for 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
139done
140
141if [ "$HELP" ]; then
142  if [ "$ADB_LLDB_PROGNAME" ]; then
143    # Assume wrapper scripts all provide a default package name.
144    cat <<EOF
145Usage: $PROGNAME [options]
146
147Attach lldb to a running Android $PROGRAM_NAME process.
148EOF
149  else
150    # Assume this is a direct call to connect_lldb.sh
151  cat <<EOF
152Usage: $PROGNAME [options] [<package-name>]
153
154Attach lldb to a running Android $PROGRAM_NAME process.
155
156If provided, <package-name> must be the name of the Android application's
157package name to be debugged. You can also use --package-name=<name> to
158specify it.
159EOF
160  fi
161
162  cat <<EOF
163
164This script is used to debug a running $PROGRAM_NAME process.
165
166This script needs several things to work properly. It will try to pick
167them 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
173You can also use --ndk-dir=<path> to specify an alternative NDK installation
174directory.
175
176The script tries to find the most recent version of the debug version of
177shared libraries under one of the following directories:
178
179  \$CHROMIUM_SRC/<out>/lib.unstripped/     (used by GN builds)
180
181Where <out> is determined by CHROMIUM_OUTPUT_DIR, or --output-directory.
182
183You can set the path manually via --symbol-dir.
184
185The script tries to extract the target architecture from your target device,
186but if this fails, will default to 'arm'. Use --target-arch=<name> to force
187its value.
188
189Otherwise, the script will complain, but you can use the --lldb-server,
190--lldb and --symbol-lib options to specify everything manually.
191
192An alternative to --lldb=<file> is to use --toolchain=<path> to specify
193the path to the host target-specific cross-toolchain.
194
195You will also need the 'adb' tool in your path. Otherwise, use the --adb
196option. The script will complain if there is more than one device connected
197and a device is not specified with either --device or ANDROID_SERIAL).
198
199The first time you use it on a device, the script will pull many system
200libraries required by the process into a temporary directory. This
201is done to strongly improve the debugging experience, like allowing
202readable thread stacks and more. The libraries are copied to the following
203directory by default:
204
205  $DEFAULT_PULL_LIBS_DIR/
206
207But you can use the --pull-libs-dir=<path> option to specify an
208alternative. The script can detect when you change the connected device,
209and will re-pull the libraries only in this case. You can however force it
210with the --pull-libs option.
211
212Any local .lldb-init script will be ignored, but it is possible to pass a
213lldb command script with the --source=<file> option. Note that its commands
214will be passed to lldb after the remote connection and library symbol
215loading have completed.
216
217Valid 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
249EOF
250  exit 0
251fi
252
253if [ -z "$PACKAGE_NAME" ]; then
254  panic "Please specify a package name on the command line. See --help."
255fi
256
257if [[ -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
267fi
268
269if 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."
272fi
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
278detect_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
295if [ -z "$SYMBOL_DIR" ]; then
296  detect_symbol_dir
297elif [[ -z "$(ls "$SYMBOL_DIR"/lib*.so 2>/dev/null)" ]]; then
298  panic "Could not find any symbols under $SYMBOL_DIR"
299fi
300
301if [ -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,)')
304else
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
312fi
313
314if [ "$LLDB_INIT" -a ! -f "$LLDB_INIT" ]; then
315  panic "Unknown --source file: $LLDB_INIT"
316fi
317
318# Checks that ADB is in our path
319if [ -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"
326fi
327
328# Checks that it works minimally
329ADB_VERSION=$($ADB version 2>/dev/null)
330echo "$ADB_VERSION" | fgrep -q -e "Android Debug Bridge"
331if [ $? != 0 ]; then
332  panic "Your 'adb' tool seems invalid, use --adb=<file> to specify a \
333different one: $ADB"
334fi
335
336# If there are more than one device connected, and ANDROID_SERIAL is not
337# defined, prints an error message.
338NUM_DEVICES_PLUS2=$($ADB devices 2>/dev/null | wc -l)
339if [ "$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
343fi
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().
353adb_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.
385get_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
400if [ -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"
406else
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
414fi
415
416# Translates GN target architecure to NDK subdirectory name.
417# $1: GN target architecture.
418# Out: NDK subdirectory name.
419get_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')
429get_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')
444get_ndk_host_arch () {
445  echo "x86_64"
446}
447
448# $1: NDK install path.
449get_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.
458get_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
477if [ -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"
484fi
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.
489if [ -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 \
493valid one!"
494  fi
495  log "Auto-config: --lldb-server=$LLDB_SERVER"
496fi
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.
501TMP_ID=$$
502
503# Temporary directory, will get cleaned up on exit.
504TMPDIR=/tmp/$USER-adb-lldb-tmp-$TMP_ID
505mkdir -p "$TMPDIR" && rm -rf "$TMPDIR"/*
506
507LLDB_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
512get_file_timestamp () {
513  stat -c %Y "$1" 2>/dev/null
514}
515
516# Allow several concurrent debugging sessions
517APP_DATA_DIR=$(adb_shell run-as $PACKAGE_NAME /system/bin/sh -c pwd)
518if [ $? != 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)
522fi
523log "App data dir: $APP_DATA_DIR"
524TARGET_LLDB_SERVER="$APP_DATA_DIR/lldb-server-adb-lldb-$TMP_ID"
525TMP_TARGET_LLDB_SERVER=/data/local/tmp/lldb-server-adb-lldb-$TMP_ID
526
527# Select correct app_process for architecture.
528case $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!";;
532esac
533
534# Default to app_process if bit-width specific process isn't found.
535adb_shell ls /system/bin/$LLDBEXEC > /dev/null
536if [ $? != 0 ]; then
537    LLDBEXEC=app_process
538fi
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.
542LLDBEXEC_ASAN=app_process.real
543adb_shell ls /system/bin/$LLDBEXEC_ASAN > /dev/null
544if [ $? == 0 ]; then
545    LLDBEXEC=$LLDBEXEC_ASAN
546fi
547
548ORG_PULL_LIBS_DIR=$PULL_LIBS_DIR
549if [[ -n "$ANDROID_SERIAL" ]]; then
550  DEFAULT_PULL_LIBS_DIR="$DEFAULT_PULL_LIBS_DIR/$ANDROID_SERIAL-$SUFFIX_64_BIT"
551fi
552PULL_LIBS_DIR=${PULL_LIBS_DIR:-$DEFAULT_PULL_LIBS_DIR}
553
554HOST_FINGERPRINT=
555DEVICE_FINGERPRINT=$(adb_shell getprop ro.build.fingerprint)
556[[ "$DEVICE_FINGERPRINT" ]] || panic "Failed to get the device fingerprint"
557log "Device build fingerprint: $DEVICE_FINGERPRINT"
558
559if [ ! -f "$PULL_LIBS_DIR/build.fingerprint" ]; then
560  log "Auto-config: --pull-libs  (no cached libraries)"
561  PULL_LIBS=true
562else
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
572fi
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).
576if [ -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"
588fi
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#
594if [ "$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="\""
606else
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
617fi
618log "Command prefix: '$COMMAND_PREFIX'"
619log "Command suffix: '$COMMAND_SUFFIX'"
620
621mkdir -p "$PULL_LIBS_DIR"
622fail_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
627if [ "$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"
651fi
652
653# Pull the app_process binary from the device.
654log "Pulling $LLDBEXEC from device"
655"$ADB" pull /system/bin/$LLDBEXEC "$TMPDIR"/$LLDBEXEC &>/dev/null
656fail_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.
660SOLIB_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.
666adb_shell $COMMAND_PREFIX chmod a+x $APP_DATA_DIR $COMMAND_SUFFIX
667
668# Push lldb-server to the device
669log "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
673fail_panic "Could not copy lldb-server to the device!"
674
675if [ -z "$PORT" ]; then
676  # Random port to allow multiple concurrent sessions.
677  PORT=$(( $RANDOM % 1000 + 5039 ))
678fi
679HOST_PORT=$PORT
680TARGET_DOMAIN_SOCKET=$APP_DATA_DIR/lldb-socket-$HOST_PORT
681
682# Setup network redirection
683log "Setting network redirection (host:$HOST_PORT -> device:$TARGET_DOMAIN_SOCKET)"
684"$ADB" forward tcp:$HOST_PORT localfilesystem:$TARGET_DOMAIN_SOCKET
685fail_panic "Could not setup network redirection from \
686host: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
695for 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
732done
733
734# Generate a file containing useful LLDB initialization commands
735readonly COMMANDS=$TMPDIR/lldb.init
736log "Generating LLDB initialization commands file: $COMMANDS"
737cat > "$COMMANDS" <<EOF
738settings append target.exec-search-paths $SYMBOL_DIR $SOLIB_DIRS $PULL_LIBS_DIR
739settings set target.source-map ../.. $CHROMIUM_SRC
740target create '$TMPDIR/$LLDBEXEC'
741target modules search-paths add / $TMPDIR/$LLDBEXEC/
742script print("Connecting to :$HOST_PORT... (symbol load can take a while)")
743gdb-remote $HOST_PORT
744EOF
745
746if [ "$LLDB_INIT" ]; then
747  cat "$LLDB_INIT" >> "$COMMANDS"
748fi
749
750if [ "$VERBOSE" -gt 0 ]; then
751  echo "### START $COMMANDS"
752  cat "$COMMANDS"
753  echo "### END $COMMANDS"
754fi
755
756log "Launching lldb client: $LLDB $LLDB_ARGS --source $COMMANDS"
757echo "Server log: $LLDB_SERVER_LOG"
758$LLDB $LLDB_ARGS --source "$COMMANDS"
759