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