Home | History | Annotate | Download | only in ndk
      1 #!/bin/sh
      2 #
      3 # Copyright (C) 2010 The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #      http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 #
     17 
     18 # This wrapper script is used to launch a native debugging session
     19 # on a given NDK application. The application must be debuggable, i.e.
     20 # its android:debuggable attribute must be set to 'true' in the
     21 # <application> element of its manifest.
     22 #
     23 # See docs/NDK-GDB.TXT for usage description. Essentially, you just
     24 # need to launch ndk-gdb from your application project directory
     25 # after doing ndk-build && ant install && <start-application-on-device>
     26 #
     27 . `dirname $0`/build/core/ndk-common.sh
     28 
     29 force_32bit_binaries
     30 
     31 find_program ADB_CMD adb
     32 ADB_FLAGS=
     33 
     34 AWK_CMD=awk
     35 
     36 DEBUG_PORT=5039
     37 
     38 # Delay in seconds between launching the activity and attaching gdbserver on it.
     39 # This is needed because there is no way to know when the activity has really
     40 # started, and sometimes this takes a few seconds.
     41 DELAY=2
     42 
     43 PARAMETERS=
     44 OPTION_HELP=no
     45 OPTION_PROJECT=
     46 OPTION_FORCE=no
     47 OPTION_ADB=
     48 OPTION_EXEC=
     49 OPTION_START=no
     50 OPTION_LAUNCH=
     51 OPTION_LAUNCH_LIST=no
     52 OPTION_DELAY=
     53 
     54 check_parameter ()
     55 {
     56     if [ -z "$2" ]; then
     57         echo "ERROR: Missing parameter after option '$1'"
     58         exit 1
     59     fi
     60 }
     61 
     62 check_adb_flags ()
     63 {
     64     if [ -n "$ADB_FLAGS" ] ; then
     65         echo "ERROR: Only one of -e, -d or -s <serial> can be used at the same time!"
     66         exit 1
     67     fi
     68 }
     69 
     70 get_build_var ()
     71 {
     72     if [ -z "$GNUMAKE" ] ; then
     73         GNUMAKE=make
     74     fi
     75     $GNUMAKE --no-print-dir -f $ANDROID_NDK_ROOT/build/core/build-local.mk -C $PROJECT DUMP_$1
     76 }
     77 
     78 get_build_var_for_abi ()
     79 {
     80     if [ -z "$GNUMAKE" ] ; then
     81         GNUMAKE=make
     82     fi
     83     $GNUMAKE --no-print-dir -f $ANDROID_NDK_ROOT/build/core/build-local.mk -C $PROJECT DUMP_$1 APP_ABI=$2
     84 }
     85 
     86 # Used to run an awk script on the manifest
     87 run_awk_manifest_script ()
     88 {
     89     $AWK_CMD -f $AWK_SCRIPTS/$1 $PROJECT/$MANIFEST
     90 }
     91 
     92 if [ "$HOST_OS" = "cygwin" ] ; then
     93 # Return native path representation from cygwin one
     94 # $1: a cygwin-compatible path (e.g. /cygdrive/c/some/thing)
     95 # Return: path in host windows representation, e.g. C:/some/thing
     96 #
     97 # We use mixed mode (i.e. / as the directory separator) because
     98 # all the tools we use recognize it properly, and it avoids lots
     99 # of escaping nonsense associated with "\"
    100 #
    101 native_path ()
    102 {
    103     cygpath -m $1
    104 }
    105 else # HOST_OS != windows
    106 native_path ()
    107 {
    108     echo "$1"
    109 }
    110 fi # HOST_OS != windows
    111 
    112 VERBOSE=no
    113 while [ -n "$1" ]; do
    114     opt="$1"
    115     optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
    116     case "$opt" in
    117         --help|-h|-\?)
    118             OPTION_HELP=yes
    119             ;;
    120         --verbose)
    121             VERBOSE=yes
    122             ;;
    123         -s)
    124             check_parameter $1 $2
    125             check_adb_flags
    126             ADB_FLAGS=" -s $2"
    127             shift
    128             ;;
    129         -s*)
    130             check_adb_flags
    131             optarg=`expr -- "$opt" : '-s\(.*\)'`
    132             ADB_FLAGS=" -s $optarg"
    133             ;;
    134         -p)
    135             check_parameter $1 $2
    136             OPTION_PROJECT="$2"
    137             shift
    138             ;;
    139         -p*)
    140             optarg=`expr -- "$opt" : '-p\(.*\)'`
    141             OPTION_PROJECT="$optarg"
    142             ;;
    143         --exec=*)
    144             OPTION_EXEC="$optarg"
    145             ;;
    146         -x)
    147             check_parameter $1 $2
    148             OPTION_EXEC="$2"
    149             shift
    150             ;;
    151         -x*)
    152             optarg=`expr -- "$opt" : '-x\(.*\)'`
    153             OPTION_EXEC="$optarg"
    154             ;;
    155         -e)
    156             check_adb_flags
    157             ADB_FLAGS=" -e"
    158             ;;
    159         -d)
    160             check_adb_flags
    161             ADB_FLAGS=" -d"
    162             ;;
    163         --adb=*) # specify ADB command
    164             OPTION_ADB="$optarg"
    165             ;;
    166         --awk=*)
    167             AWK_CMD="$optarg"
    168             ;;
    169         --project=*)
    170             OPTION_PROJECT="$optarg"
    171             ;;
    172         --port=*)
    173             DEBUG_PORT="$optarg"
    174             ;;
    175         --force)
    176             OPTION_FORCE="yes"
    177             ;;
    178         --launch-list)
    179             OPTION_LAUNCH_LIST="yes"
    180             ;;
    181         --launch=*)
    182             OPTION_LAUNCH="$optarg"
    183             ;;
    184         --start)
    185             OPTION_START=yes
    186             ;;
    187         --delay=*)
    188             OPTION_DELAY="$optarg"
    189             ;;
    190         -*) # unknown options
    191             echo "ERROR: Unknown option '$opt', use --help for list of valid ones."
    192             exit 1
    193         ;;
    194         *)  # Simply record parameter
    195             if [ -z "$PARAMETERS" ] ; then
    196                 PARAMETERS="$opt"
    197             else
    198                 PARAMETERS="$PARAMETERS $opt"
    199             fi
    200             ;;
    201     esac
    202     shift
    203 done
    204 
    205 if [ "$OPTION_HELP" = "yes" ] ; then
    206     echo "Usage: $PROGNAME [options]"
    207     echo ""
    208     echo "Setup a gdb debugging session for your Android NDK application."
    209     echo "Read $$NDK/docs/NDK-GDB.TXT for complete usage instructions."
    210     echo ""
    211     echo "Valid options:"
    212     echo ""
    213     echo "    --help|-h|-?      Print this help"
    214     echo "    --verbose         Enable verbose mode"
    215     echo "    --force           Kill existing debug session if it exists"
    216     echo "    --start           Launch application instead of attaching to existing one"
    217     echo "    --launch=<name>   Same as --start, but specify activity name (see below)"
    218     echo "    --launch-list     List all launchable activity names from manifest"
    219     echo "    --delay=<secs>    Delay in seconds between activity start and gdbserver attach."
    220     echo "    --project=<path>  Specify application project path"
    221     echo "    -p <path>         Same as --project=<path>"
    222     echo "    --port=<port>     Use tcp:localhost:<port> to communicate with gdbserver [$DEBUG_PORT]"
    223     echo "    --exec=<file>     Execute gdb initialization commands in <file> after connection"
    224     echo "    -x <file>         Same as --exec=<file>"
    225     echo "    --adb=<file>      Use specific adb command [$ADB_CMD]"
    226     echo "    --awk=<file>      Use specific awk command [$AWK_CMD]"
    227     echo "    -e                Connect to single emulator instance"
    228     echo "    -d                Connect to single target device"
    229     echo "    -s <serial>       Connect to specific emulator or device"
    230     echo ""
    231     exit 0
    232 fi
    233 
    234 log "Android NDK installation path: $ANDROID_NDK_ROOT"
    235 
    236 if [ -n "$OPTION_EXEC" ] ; then
    237     if [ ! -f "$OPTION_EXEC" ]; then
    238         echo "ERROR: Invalid initialization file: $OPTION_EXEC"
    239         exit 1
    240     fi
    241 fi
    242 
    243 if [ -n "$OPTION_DELAY" ] ; then
    244     DELAY="$OPTION_DELAY"
    245 fi
    246 
    247 # Check ADB tool version
    248 if [ -n "$OPTION_ADB" ] ; then
    249     ADB_CMD="$OPTION_ADB"
    250     log "Using specific adb command: $ADB_CMD"
    251 else
    252     if [ -z "$ADB_CMD" ] ; then
    253         echo "ERROR: The 'adb' tool is not in your path."
    254         echo "       You can change your PATH variable, or use"
    255         echo "       --adb=<executable> to point to a valid one."
    256         exit 1
    257     fi
    258     log "Using default adb command: $ADB_CMD"
    259 fi
    260 
    261 ADB_VERSION=`$ADB_CMD version`
    262 if [ $? != 0 ] ; then
    263     echo "ERROR: Could not run ADB with: $ADB_CMD"
    264     exit 1
    265 fi
    266 log "ADB version found: $ADB_VERSION"
    267 
    268 ADB_CMD="${ADB_CMD}${ADB_FLAGS}"
    269 log "Using final ADB command: '$ADB_CMD'"
    270 
    271 adb_shell ()
    272 {
    273     # Run an adb shell command and return its output.
    274     #
    275     # We need to filter weird control characters like \r that are
    276     # included in the output.
    277     #
    278     $ADB_CMD shell $@ | sed -e 's![[:cntrl:]]!!g'
    279 }
    280 
    281 # Check the awk tool
    282 AWK_SCRIPTS=$ANDROID_NDK_ROOT/build/awk
    283 AWK_TEST=`$AWK_CMD -f $AWK_SCRIPTS/check-awk.awk`
    284 if [ $? != 0 ] ; then
    285     echo "ERROR: Could not run '$AWK_CMD' command. Do you have it installed properly?"
    286     exit 1
    287 fi
    288 if [ "$AWK_TEST" != "Pass" ] ; then
    289     echo "ERROR: Your version of 'awk' is obsolete. Please use --awk=<file> to point to Nawk or Gawk!"
    290     exit 1
    291 fi
    292 
    293 # Name of the manifest file
    294 MANIFEST=AndroidManifest.xml
    295 
    296 # Find the root of the application project.
    297 if [ -n "$OPTION_PROJECT" ] ; then
    298     PROJECT=$OPTION_PROJECT
    299     log "Using specified project path: $PROJECT"
    300     if [ ! -d "$PROJECT" ] ; then
    301         echo "ERROR: Your --project option does not point to a directory!"
    302         exit 1
    303     fi
    304     if [ ! -f "$PROJECT/$MANIFEST" ] ; then
    305         echo "ERROR: Your --project does not point to an Android project path!"
    306         echo "       It is missing a $MANIFEST file."
    307         exit 1
    308     fi
    309 else
    310     # Assume we are in the project directory
    311     if [ -f "$MANIFEST" ] ; then
    312         PROJECT=.
    313     else
    314         PROJECT=
    315         CURDIR=`pwd`
    316         while [ "$CURDIR" != "/" ] ; do
    317             if [ -f "$CURDIR/$MANIFEST" ] ; then
    318                 PROJECT="$CURDIR"
    319                 break
    320             fi
    321             CURDIR=`dirname $CURDIR`
    322         done
    323         if [ -z "$PROJECT" ] ; then
    324             echo "ERROR: Launch this script from an application project directory, or use --project=<path>."
    325             exit 1
    326         fi
    327     fi
    328     log "Using auto-detected project path: $PROJECT"
    329 fi
    330 
    331 # Extract the package name from the manifest
    332 PACKAGE_NAME=`run_awk_manifest_script extract-package-name.awk`
    333 log "Found package name: $PACKAGE_NAME"
    334 if [ $? != 0 -o "$PACKAGE_NAME" = "<none>" ] ; then
    335     echo "ERROR: Could not extract package name from $PROJECT/$MANIFEST."
    336     echo "       Please check that the file is well-formed!"
    337     exit 1
    338 fi
    339 
    340 # If --launch-list is used, list all launchable activities, and be done with it
    341 if [ "$OPTION_LAUNCH_LIST" = "yes" ] ; then
    342     log "Extracting list of launchable activities from manifest:"
    343     run_awk_manifest_script extract-launchable.awk
    344     exit 0
    345 fi
    346 
    347 APP_ABIS=`get_build_var APP_ABI`
    348 log "ABIs targetted by application: $APP_ABIS"
    349 
    350 # Check the ADB command, and that we can connect to the device/emulator
    351 ADB_TEST=`$ADB_CMD shell ls`
    352 if [ $? != 0 ] ; then
    353     echo "ERROR: Could not connect to device or emulator!"
    354     echo "       Please check that an emulator is running or a device is connected"
    355     echo "       through USB to this machine. You can use -e, -d and -s <serial>"
    356     echo "       in case of multiple ones."
    357     exit 1
    358 fi
    359 
    360 # Check that the device is running Froyo (API Level 8) or higher
    361 #
    362 API_LEVEL=`adb_shell getprop ro.build.version.sdk`
    363 if [ $? != 0 -o -z "$API_LEVEL" ] ; then
    364     echo "ERROR: Could not find target device's supported API level!"
    365     echo "ndk-gdb will only work if your device is running Android 2.2 or higher."
    366     exit 1
    367 fi
    368 log "Device API Level: $API_LEVEL"
    369 if [ "$API_LEVEL" -lt "8" ] ; then
    370     echo "ERROR: ndk-gdb requires a target device running Android 2.2 (API level 8) or higher."
    371     echo "The target device is running API level $API_LEVEL!"
    372     exit 1
    373 fi
    374 
    375 # Get the target device's supported ABI(s)
    376 # And check that they are supported by the application
    377 #
    378 COMPAT_ABI=none
    379 CPU_ABI=`adb_shell getprop ro.product.cpu.abi`
    380 for ABI in $APP_ABIS; do
    381     if [ "$ABI" = "$CPU_ABI" ] ; then
    382         COMPAT_ABI=$CPU_ABI
    383         break
    384     fi
    385 done
    386 
    387 CPU_ABI2=`adb_shell getprop ro.product.cpu.abi2`
    388 if [ -z "$CPU_ABI2" ] ; then
    389     log "Device CPU ABI: $CPU_ABI"
    390 else
    391     log "Device CPU ABIs: $CPU_ABI $CPU_ABI2"
    392     if [ "$COMPAT_ABI" = "none" ] ; then
    393         for ABI in $APP_ABIS; do
    394             if [ "$ABI" = "$CPU_ABI2" ] ; then
    395                 COMPAT_ABI=$CPU_ABI2
    396                 break
    397             fi
    398         done
    399     fi
    400 fi
    401 if [ "$COMPAT_ABI" = none ] ; then
    402     echo "ERROR: The device does not support the application's targetted CPU ABIs!"
    403     if [ "$CPU_ABI2" = "$CPU_ABI" ] ; then
    404         CPU_ABI2=
    405     fi
    406     echo "       Device supports:  $CPU_ABI $CPU_ABI2"
    407     echo "       Package supports: $APP_ABIS"
    408     exit 1
    409 fi
    410 log "Compatible device ABI: $COMPAT_ABI"
    411 
    412 # Check that the application is debuggable, or nothing will work
    413 DEBUGGABLE=`run_awk_manifest_script extract-debuggable.awk`
    414 log "Found debuggable flag: $DEBUGGABLE"
    415 if [ $? != 0 -o "$DEBUGGABLE" != "true" ] ; then
    416     # If gdbserver exists, then we built with 'ndk-build NDK_DEBUG=1' and it's
    417     # ok to not have android:debuggable set to true in the original manifest.
    418     # However, if this is not the case, then complain!!
    419     if [ -f $PROJECT/libs/$COMPAT_ABI/gdbserver ] ; then
    420         log "Found gdbserver under libs/$COMPAT_ABI, assuming app was built with NDK_DEBUG=1"
    421     else
    422         echo "ERROR: Package $PACKAGE_NAME is not debuggable ! You can fix that in two ways:"
    423         echo ""
    424         echo "  - Rebuilt with the NDK_DEBUG=1 option when calling 'ndk-build'."
    425         echo ""
    426         echo "  - Modify your manifest to set android:debuggable attribute to \"true\","
    427         echo "    then rebuild normally."
    428         echo ""
    429         echo "After one of these, re-install to the device!"
    430         exit 1
    431     fi
    432 else
    433     # DEBUGGABLE is true in the manifest. Let's check that the user didn't change the
    434     # debuggable flag in the manifest without calling ndk-build afterwards.
    435     if [ ! -f $PROJECT/libs/$COMPAT_ABI/gdbserver ] ; then
    436         echo "ERROR: Could not find gdbserver binary under $PROJECT/libs/$COMPAT_ABI"
    437         echo "       This usually means you modified your AndroidManifest.xml to set"
    438         echo "       the android:debuggable flag to 'true' but did not rebuild the"
    439         echo "       native binaries. Please call 'ndk-build' to do so,"
    440         echo "       *then* re-install to the device!"
    441         exit 1
    442     fi
    443 fi
    444 
    445 # Let's check that 'gdbserver' is properly installed on the device too. If this
    446 # is not the case, the user didn't install the proper package after rebuilding.
    447 #
    448 DEVICE_GDBSERVER=`adb_shell ls /data/data/$PACKAGE_NAME/lib/gdbserver`
    449 log "Found device gdbserver: $DEVICE_GDBSERVER"
    450 if pattern_match "No such file or directory" "$DEVICE_GDBSERVER" ] ; then
    451     echo "ERROR: Non-debuggable application installed on the target device."
    452     echo "       Please re-install the debuggable version!"
    453     exit 1
    454 fi
    455 
    456 # Get information from the build system
    457 GDBSETUP_INIT=`get_build_var_for_abi NDK_APP_GDBSETUP $COMPAT_ABI`
    458 log "Using gdb setup init: $GDBSETUP_INIT"
    459 
    460 TOOLCHAIN_PREFIX=`get_build_var_for_abi TOOLCHAIN_PREFIX $COMPAT_ABI`
    461 log "Using toolchain prefix: $TOOLCHAIN_PREFIX"
    462 
    463 APP_OUT=`get_build_var_for_abi TARGET_OUT $COMPAT_ABI`
    464 log "Using app out directory: $APP_OUT"
    465 
    466 # Find the <dataDir> of the package on the device
    467 DATA_DIR=`adb_shell run-as $PACKAGE_NAME /system/bin/sh -c pwd`
    468 log "Found data directory: '$DATA_DIR'"
    469 if [ $? != 0 -o -z "$DATA_DIR" ] ; then
    470     echo "ERROR: Could not extract package's data directory. Are you sure that"
    471     echo "       your installed application is debuggable?"
    472     exit 1
    473 fi
    474 
    475 # Launch the activity if needed
    476 if [ "$OPTION_START" = "yes" ] ; then
    477     # If --launch is used, ignore --start, otherwise extract the first
    478     # launchable activity name from the manifest and use it as if --launch=<name>
    479     # was used instead.
    480     #
    481     if [ -z "$OPTION_LAUNCH" ] ; then
    482         OPTION_LAUNCH=`run_awk_manifest_script extract-launchable.awk | sed 2q`
    483         if [ $? != 0 ] ; then
    484             echo "ERROR: Could not extract name of launchable activity from manifest!"
    485             echo "       Try to use --launch=<name> directly instead as a work-around."
    486             exit 1
    487         fi
    488         log "Found first launchable activity: $OPTION_LAUNCH"
    489         if [ -z "$OPTION_LAUNCH" ] ; then
    490             echo "ERROR: It seems that your Application does not have any launchable activity!"
    491             echo "       Please fix your manifest file and rebuild/re-install your application."
    492             exit 1
    493         fi
    494     fi
    495 fi
    496 
    497 if [ -n "$OPTION_LAUNCH" ] ; then
    498     log "Launching activity: $PACKAGE_NAME/$OPTION_LAUNCH"
    499     run $ADB_CMD shell am start -n $PACKAGE_NAME/$OPTION_LAUNCH
    500     if [ $? != 0 ] ; then
    501         echo "ERROR: Could not launch specified activity: $OPTION_LAUNCH"
    502         echo "       Use --launch-list to dump a list of valid values."
    503         exit 1
    504     fi
    505     # Sleep a bit, it sometimes take one second to start properly
    506     # Note that we use the 'sleep' command on the device here.
    507     run $ADB_CMD shell sleep $DELAY
    508 fi
    509 
    510 # Find the PID of the application being run
    511 PID=`$ADB_CMD shell ps | $AWK_CMD -f $AWK_SCRIPTS/extract-pid.awk -v PACKAGE=$PACKAGE_NAME`
    512 log "Found running PID: $PID"
    513 if [ $? != 0 -o "$PID" = "0" ] ; then
    514     echo "ERROR: Could not extract PID of application on device/emulator."
    515     if [ -n "$OPTION_LAUNCH" ] ; then
    516         echo "       Weird, this probably means one of these:"
    517         echo ""
    518         echo "         - The installed package does not match your current manifest."
    519         echo "         - The application process was terminated."
    520         echo ""
    521         echo "       Try using the --verbose option and look at its output for details."
    522     else
    523         echo "       Are you sure the application is already started?"
    524         echo "       Consider using --start or --launch=<name> if not."
    525     fi
    526     exit 1
    527 fi
    528 
    529 # Check that there is no other instance of gdbserver running
    530 GDBSERVER_PS=`$ADB_CMD shell ps | grep lib/gdbserver`
    531 if [ -n "$GDBSERVER_PS" ] ; then
    532     if [ "$OPTION_FORCE" = "no" ] ; then
    533         echo "ERROR: Another debug session running, Use --force to kill it."
    534         exit 1
    535     fi
    536     log "Killing existing debugging session"
    537     GDBSERVER_PID=`echo $GDBSERVER_PS | $AWK_CMD -f $AWK_SCRIPTS/extract-pid.awk -v PACKAGE=lib/gdbserver`
    538     if [ $GDBSERVER_PID != 0 ] ; then
    539         run $ADB_CMD shell kill -9 $GDBSERVER_PID
    540     fi
    541 fi
    542 
    543 # Launch gdbserver now
    544 DEBUG_SOCKET=debug-socket
    545 run $ADB_CMD shell run-as $PACKAGE_NAME lib/gdbserver +$DEBUG_SOCKET --attach $PID &
    546 if [ $? != 0 ] ; then
    547     echo "ERROR: Could not launch gdbserver on the device?"
    548     exit 1
    549 fi
    550 log "Launched gdbserver succesfully."
    551 
    552 # Setup network redirection
    553 log "Setup network redirection"
    554 run $ADB_CMD forward tcp:$DEBUG_PORT localfilesystem:$DATA_DIR/$DEBUG_SOCKET
    555 if [ $? != 0 ] ; then
    556     echo "ERROR: Could not setup network redirection to gdbserver?"
    557     echo "       Maybe using --port=<port> to use a different TCP port might help?"
    558     exit 1
    559 fi
    560 
    561 # Get the app_server binary from the device
    562 APP_PROCESS=$APP_OUT/app_process
    563 run $ADB_CMD pull /system/bin/app_process `native_path $APP_PROCESS`
    564 log "Pulled app_process from device/emulator."
    565 
    566 run $ADB_CMD pull /system/lib/libc.so `native_path $APP_OUT/libc.so`
    567 log "Pulled libc.so from device/emulator."
    568 
    569 # Now launch the appropriate gdb client with the right init commands
    570 #
    571 GDBCLIENT=${TOOLCHAIN_PREFIX}gdb
    572 GDBSETUP=$APP_OUT/gdb.setup
    573 cp -f $GDBSETUP_INIT $GDBSETUP
    574 #uncomment the following to debug the remote connection only
    575 #echo "set debug remote 1" >> $GDBSETUP
    576 echo "file `native_path $APP_PROCESS`" >> $GDBSETUP
    577 echo "target remote :$DEBUG_PORT" >> $GDBSETUP
    578 if [ -n "$OPTION_EXEC" ] ; then
    579     cat $OPTION_EXEC >> $GDBSETUP
    580 fi
    581 $GDBCLIENT -x `native_path $GDBSETUP`
    582