123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- #!/bin/sh
- # Copyright 2012 Google LLC
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions are
- # met:
- #
- # * Redistributions of source code must retain the above copyright
- # notice, this list of conditions and the following disclaimer.
- # * Redistributions in binary form must reproduce the above
- # copyright notice, this list of conditions and the following disclaimer
- # in the documentation and/or other materials provided with the
- # distribution.
- # * Neither the name of Google LLC nor the names of its
- # contributors may be used to endorse or promote products derived from
- # this software without specific prior written permission.
- #
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- # Sanitize the environment
- export LANG=C
- export LC_ALL=C
- if [ "$BASH_VERSION" ]; then
- set -o posix
- fi
- PROGDIR=$(dirname "$0")
- PROGDIR=$(cd "$PROGDIR" && pwd)
- PROGNAME=$(basename "$0")
- . $PROGDIR/common-functions.sh
- DEFAULT_ABI="armeabi"
- VALID_ABIS="armeabi armeabi-v7a x86 mips"
- ABI=
- ADB=
- ALL_TESTS=
- ENABLE_M32=
- HELP=
- HELP_ALL=
- NDK_DIR=
- NO_CLEANUP=
- NO_DEVICE=
- NUM_JOBS=$(get_core_count)
- TMPDIR=
- for opt do
- # The following extracts the value if the option is like --name=<value>.
- optarg=$(expr -- $opt : '^--[^=]*=\(.*\)$')
- case $opt in
- --abi=*) ABI=$optarg;;
- --adb=*) ADB=$optarg;;
- --all-tests) ALL_TESTS=true;;
- --enable-m32) ENABLE_M32=true;;
- --help|-h|-?) HELP=TRUE;;
- --help-all) HELP_ALL=true;;
- --jobs=*) NUM_JOBS=$optarg;;
- --ndk-dir=*) NDK_DIR=$optarg;;
- --tmp-dir=*) TMPDIR=$optarg;;
- --no-cleanup) NO_CLEANUP=true;;
- --no-device) NO_DEVICE=true;;
- --quiet) decrease_verbosity;;
- --verbose) increase_verbosity;;
- -*) panic "Invalid option '$opt', see --help for details.";;
- *) panic "This script doesn't take any parameters. See --help for details."
- ;;
- esac
- done
- if [ "$HELP" -o "$HELP_ALL" ]; then
- echo "\
- Usage: $PROGNAME [options]
- This script is used to check that your Google Breakpad source tree can
- be properly built for Android, and that the client library and host tools
- work properly together.
- "
- if [ "$HELP_ALL" ]; then
- echo "\
- In more details, this script will:
- - Rebuild the host version of Google Breakpad in a temporary
- directory (with the Auto-tools based build system).
- - Rebuild the Android client library with the Google Breakpad build
- system (using autotools/configure). This requires that you define
- ANDROID_NDK_ROOT in your environment to point to a valid Android NDK
- installation directory, or use the --ndk-dir=<path> option.
- - Rebuild the Android client library and a test crashing program with the
- Android NDK build system (ndk-build).
- - Require an Android device connected to your machine, and the 'adb'
- tool in your path. They are used to:
- - Install and run a test crashing program.
- - Extract the corresponding minidump from the device.
- - Dump the symbols from the test program on the host with 'dump_syms'
- - Generate a stack trace with 'minidump_stackwalk'
- - Check the stack trace content for valid source file locations.
- You can however skip this requirement and only test the builds by using
- the --no-device flag.
- By default, all generated files will be created in a temporary directory
- that is removed when the script completion. If you want to inspect the
- files, use the --no-cleanup option.
- Finally, use --verbose to increase the verbosity level, this will help
- you see which exact commands are being issues and their result. Use the
- flag twice for even more output. Use --quiet to decrease verbosity
- instead and run the script silently.
- If you have a device connected, the script will probe it to determine
- its primary CPU ABI, and build the test program for it. You can however
- use the --abi=<name> option to override this (this can be useful to check
- the secondary ABI, e.g. using --abi=armeabi to check that such a program
- works correctly on an ARMv7-A device).
- If you don't have a device connected, the test program will be built (but
- not run) with the default '$DEFAULT_ABI' ABI. Again, you can use
- --abi=<name> to override this. Valid ABI names are:
- $VALID_ABIS
- The script will only run the client library unit test on the device
- by default. You can use --all-tests to also build and run the unit
- tests for the Breakpad tools and processor, but be warned that this
- adds several minutes of testing time. --all-tests will also run the
- host unit tests suite.
- "
- fi # HELP_ALL
- echo "\
- Valid options:
- --help|-h|-? Display this message.
- --help-all Display extended help.
- --enable-m32 Build 32-bit version of host tools.
- --abi=<name> Specify target CPU ABI [auto-detected].
- --jobs=<count> Run <count> build tasks in parallel [$NUM_JOBS].
- --ndk-dir=<path> Specify NDK installation directory.
- --tmp-dir=<path> Specify temporary directory (will be wiped-out).
- --adb=<path> Specify adb program path.
- --no-cleanup Don't remove temporary directory after completion.
- --no-device Do not try to detect devices, nor run crash test.
- --all-tests Run all unit tests (i.e. tools and processor ones too).
- --verbose Increase verbosity.
- --quiet Decrease verbosity."
- exit 0
- fi
- TESTAPP_DIR=$PROGDIR/sample_app
- # Select NDK install directory.
- if [ -z "$NDK_DIR" ]; then
- if [ -z "$ANDROID_NDK_ROOT" ]; then
- panic "Please define ANDROID_NDK_ROOT in your environment, or use \
- --ndk-dir=<path>."
- fi
- NDK_DIR="$ANDROID_NDK_ROOT"
- log "Found NDK directory: $NDK_DIR"
- else
- log "Using NDK directory: $NDK_DIR"
- fi
- # Small sanity check.
- NDK_BUILD="$NDK_DIR/ndk-build"
- if [ ! -f "$NDK_BUILD" ]; then
- panic "Your NDK directory is not valid (missing ndk-build): $NDK_DIR"
- fi
- # Ensure the temporary directory is deleted on exit, except if the --no-cleanup
- # option is used.
- clean_tmpdir () {
- if [ "$TMPDIR" ]; then
- if [ -z "$NO_CLEANUP" ]; then
- log "Cleaning up: $TMPDIR"
- rm -rf "$TMPDIR"
- else
- dump "Temporary directory contents preserved: $TMPDIR"
- fi
- fi
- exit "$@"
- }
- atexit clean_tmpdir
- # If --tmp-dir=<path> is not used, create a temporary directory.
- # Otherwise, start by cleaning up the user-provided path.
- if [ -z "$TMPDIR" ]; then
- TMPDIR=$(mktemp -d /tmp/$PROGNAME.XXXXXXXX)
- fail_panic "Can't create temporary directory!"
- log "Using temporary directory: $TMPDIR"
- else
- if [ ! -d "$TMPDIR" ]; then
- mkdir -p "$TMPDIR"
- fail_panic "Can't create temporary directory: $TMPDIR"
- else
- log "Cleaning up temporary directory: $TMPDIR"
- rm -rf "$TMPDIR"/*
- fail_panic "Cannot cleanup temporary directory!"
- fi
- fi
- if [ -z "$NO_DEVICE" ]; then
- if ! adb_check_device $ADB; then
- echo "$(adb_get_error)"
- echo "Use --no-device to build the code without running any tests."
- exit 1
- fi
- fi
- BUILD_LOG="$TMPDIR/build.log"
- RUN_LOG="$TMPDIR/run.log"
- CRASH_LOG="$TMPDIR/crash.log"
- set_run_log "$RUN_LOG"
- TMPHOST="$TMPDIR/host-local"
- cd "$TMPDIR"
- # Build host version of the tools
- dump "Building host binaries."
- CONFIGURE_FLAGS=
- if [ "$ENABLE_M32" ]; then
- CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-m32"
- fi
- (
- run mkdir "$TMPDIR/build-host" &&
- run cd "$TMPDIR/build-host" &&
- run2 "$PROGDIR/../configure" --prefix="$TMPHOST" $CONFIGURE_FLAGS &&
- run2 make -j$NUM_JOBS install
- )
- fail_panic "Can't build host binaries!"
- if [ "$ALL_TESTS" ]; then
- dump "Running host unit tests."
- (
- run cd "$TMPDIR/build-host" &&
- run2 make -j$NUM_JOBS check
- )
- fail_panic "Host unit tests failed!!"
- fi
- TMPBIN=$TMPHOST/bin
- # Generate a stand-alone NDK toolchain
- # Extract CPU ABI and architecture from device, if any.
- if adb_check_device; then
- DEVICE_ABI=$(adb_shell getprop ro.product.cpu.abi)
- DEVICE_ABI2=$(adb_shell getprop ro.product.cpu.abi2)
- if [ -z "$DEVICE_ABI" ]; then
- panic "Can't extract ABI from connected device!"
- fi
- if [ "$DEVICE_ABI2" ]; then
- dump "Found device ABIs: $DEVICE_ABI $DEVICE_ABI2"
- else
- dump "Found device ABI: $DEVICE_ABI"
- DEVICE_ABI2=$DEVICE_ABI
- fi
- # If --abi=<name> is used, check that the device supports it.
- if [ "$ABI" -a "$DEVICE_ABI" != "$ABI" -a "$DEVICE_ABI2" != "$ABI" ]; then
- dump "ERROR: Device ABI(s) do not match --abi command-line value ($ABI)!"
- panic "Please use --no-device to skip device tests."
- fi
- if [ -z "$ABI" ]; then
- ABI=$DEVICE_ABI
- dump "Using CPU ABI: $ABI (device)"
- else
- dump "Using CPU ABI: $ABI (command-line)"
- fi
- else
- if [ -z "$ABI" ]; then
- # No device connected, choose default ABI
- ABI=$DEFAULT_ABI
- dump "Using CPU ABI: $ABI (default)"
- else
- dump "Using CPU ABI: $ABI (command-line)"
- fi
- fi
- # Check the ABI value
- VALID=
- for VALID_ABI in $VALID_ABIS; do
- if [ "$ABI" = "$VALID_ABI" ]; then
- VALID=true
- break
- fi
- done
- if [ -z "$VALID" ]; then
- panic "Unknown CPU ABI '$ABI'. Valid values are: $VALID_ABIS"
- fi
- # Extract architecture name from ABI
- case $ABI in
- armeabi*) ARCH=arm;;
- *) ARCH=$ABI;;
- esac
- # Extract GNU configuration name
- case $ARCH in
- arm)
- GNU_CONFIG=arm-linux-androideabi
- ;;
- x86)
- GNU_CONFIG=i686-linux-android
- ;;
- mips)
- GNU_CONFIG=mipsel-linux-android
- ;;
- *)
- GNU_CONFIG="$ARCH-linux-android"
- ;;
- esac
- # Generate standalone NDK toolchain installation
- NDK_STANDALONE="$TMPDIR/ndk-$ARCH-toolchain"
- echo "Generating NDK standalone toolchain installation"
- mkdir -p "$NDK_STANDALONE"
- # NOTE: The --platform=android-9 is required to provide <regex.h> for GTest.
- run "$NDK_DIR/build/tools/make-standalone-toolchain.sh" \
- --arch="$ARCH" \
- --platform=android-9 \
- --install-dir="$NDK_STANDALONE"
- fail_panic "Can't generate standalone NDK toolchain installation!"
- # Rebuild the client library, processor and tools with the auto-tools based
- # build system. Even though it's not going to be used, this checks that this
- # still works correctly.
- echo "Building full Android binaries with configure/make"
- TMPTARGET="$TMPDIR/target-local"
- (
- PATH="$NDK_STANDALONE/bin:$PATH"
- run mkdir "$TMPTARGET" &&
- run mkdir "$TMPDIR"/build-target &&
- run cd "$TMPDIR"/build-target &&
- run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
- --host="$GNU_CONFIG" &&
- run2 make -j$NUM_JOBS install
- )
- fail_panic "Could not rebuild Android binaries!"
- # Build and/or run unit test suite.
- # If --no-device is used, only rebuild it, otherwise, run in on the
- # connected device.
- if [ "$NO_DEVICE" ]; then
- ACTION="Building"
- # This is a trick to force the Makefile to ignore running the scripts.
- TESTS_ENVIRONMENT="TESTS_ENVIRONMENT=true"
- else
- ACTION="Running"
- TESTS_ENVIRONMENT=
- fi
- (
- PATH="$NDK_STANDALONE/bin:$PATH"
- run cd "$TMPDIR"/build-target &&
- # Reconfigure to only run the client unit test suite.
- # This one should _never_ fail.
- dump "$ACTION Android client library unit tests."
- run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
- --host="$GNU_CONFIG" \
- --disable-tools \
- --disable-processor &&
- run make -j$NUM_JOBS check $TESTS_ENVIRONMENT || exit $?
- if [ "$ALL_TESTS" ]; then
- dump "$ACTION Tools and processor unit tests."
- # Reconfigure to run the processor and tools tests.
- # Most of these fail for now, so do not worry about it.
- run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
- --host="$GNU_CONFIG" &&
- run make -j$NUM_JOBS check $TESTS_ENVIRONMENT
- if [ $? != 0 ]; then
- dump "Tools and processor unit tests failed as expected. \
- Use --verbose for results."
- fi
- fi
- )
- fail_panic "Client library unit test suite failed!"
- # Copy sources to temporary directory
- PROJECT_DIR=$TMPDIR/project
- dump "Copying test program sources to: $PROJECT_DIR"
- run cp -r "$TESTAPP_DIR" "$PROJECT_DIR" &&
- run rm -rf "$PROJECT_DIR/obj" &&
- run rm -rf "$PROJECT_DIR/libs"
- fail_panic "Could not copy test program sources to: $PROJECT_DIR"
- # Build the test program with ndk-build.
- dump "Building test program with ndk-build"
- export NDK_MODULE_PATH="$PROGDIR"
- NDK_BUILD_FLAGS="-j$NUM_JOBS"
- if verbosity_is_higher_than 1; then
- NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS NDK_LOG=1 V=1"
- fi
- run "$NDK_DIR/ndk-build" -C "$PROJECT_DIR" $NDK_BUILD_FLAGS APP_ABI=$ABI
- fail_panic "Can't build test program!"
- # Unless --no-device was used, stop right here if ADB isn't in the path,
- # or there is no connected device.
- if [ "$NO_DEVICE" ]; then
- dump "Done. Please connect a device to run all tests!"
- clean_exit 0
- fi
- # Push the program to the device.
- TESTAPP=test_google_breakpad
- TESTAPP_FILE="$PROJECT_DIR/libs/$ABI/test_google_breakpad"
- if [ ! -f "$TESTAPP_FILE" ]; then
- panic "Device requires '$ABI' binaries. None found!"
- fi
- # Run the program there
- dump "Installing test program on device"
- DEVICE_TMP=/data/local/tmp
- adb_push "$TESTAPP_FILE" "$DEVICE_TMP/"
- fail_panic "Cannot push test program to device!"
- dump "Running test program on device"
- adb_shell cd "$DEVICE_TMP" "&&" ./$TESTAPP > "$CRASH_LOG" 2>/dev/null
- if [ $? = 0 ]; then
- panic "Test program did *not* crash as expected!"
- fi
- if verbosity_is_higher_than 0; then
- echo -n "Crash log: "
- cat "$CRASH_LOG"
- fi
- # Extract minidump from device
- MINIDUMP_NAME=$(awk '$1 == "Dump" && $2 == "path:" { print $3; }' "$CRASH_LOG")
- MINIDUMP_NAME=$(basename "$MINIDUMP_NAME")
- if [ -z "$MINIDUMP_NAME" ]; then
- panic "Test program didn't write minidump properly!"
- fi
- dump "Extracting minidump: $MINIDUMP_NAME"
- adb_pull "$DEVICE_TMP/$MINIDUMP_NAME" .
- fail_panic "Can't extract minidump!"
- dump "Parsing test program symbols"
- if verbosity_is_higher_than 1; then
- log "COMMAND: $TMPBIN/dump_syms \
- $PROJECT_DIR/obj/local/$ABI/$TESTAPP >$TESTAPP.sym"
- fi
- "$TMPBIN/dump_syms" "$PROJECT_DIR/obj/local/$ABI/$TESTAPP" > $TESTAPP.sym
- fail_panic "dump_syms doesn't work!"
- VERSION=$(awk '$1 == "MODULE" { print $4; }' $TESTAPP.sym)
- dump "Found module version: $VERSION"
- if [ -z "$VERSION" ]; then
- echo "ERROR: Can't find proper module version from symbol dump!"
- head -n5 $TESTAPP.sym
- clean_exit 1
- fi
- run mkdir -p "$TMPDIR/symbols/$TESTAPP/$VERSION"
- run mv $TESTAPP.sym "$TMPDIR/symbols/$TESTAPP/$VERSION/"
- dump "Generating stack trace"
- # Don't use 'run' to be able to send stdout and stderr to two different files.
- log "COMMAND: $TMPBIN/minidump_stackwalk $MINIDUMP_NAME symbols"
- "$TMPBIN/minidump_stackwalk" $MINIDUMP_NAME \
- "$TMPDIR/symbols" \
- > "$BUILD_LOG" 2>>"$RUN_LOG"
- fail_panic "minidump_stackwalk doesn't work!"
- dump "Checking stack trace content"
- if verbosity_is_higher_than 1; then
- cat "$BUILD_LOG"
- fi
- # The generated stack trace should look like the following:
- #
- # Thread 0 (crashed)
- # 0 test_google_breakpad!crash [test_breakpad.cpp : 17 + 0x4]
- # r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c
- # r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
- # sp = 0xbea2cb50 lr = 0x00009025 pc = 0x00008f84
- # Found by: given as instruction pointer in context
- # 1 test_google_breakpad!main [test_breakpad.cpp : 25 + 0x3]
- # r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c
- # r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
- # sp = 0xbea2cb50 pc = 0x00009025
- # Found by: call frame info
- # 2 libc.so + 0x164e5
- # r4 = 0x00008f64 r5 = 0xbea2cc34 r6 = 0x00000001 r7 = 0xbea2cc3c
- # r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
- # sp = 0xbea2cc18 pc = 0x400c34e7
- # Found by: call frame info
- # ...
- #
- # The most important part for us is ensuring that the source location could
- # be extracted, so look at the 'test_breakpad.cpp' references here.
- #
- # First, extract all the lines with test_google_breakpad! in them, and
- # dump the corresponding crash location.
- #
- # Note that if the source location can't be extracted, the second field
- # will only be 'test_google_breakpad' without the exclamation mark.
- #
- LOCATIONS=$(awk '$2 ~ "^test_google_breakpad!.*" { print $3; }' "$BUILD_LOG")
- if [ -z "$LOCATIONS" ]; then
- if verbosity_is_lower_than 1; then
- cat "$BUILD_LOG"
- fi
- panic "No source location found in stack trace!"
- fi
- # Now check that they all match "[<source file>"
- BAD_LOCATIONS=
- for LOCATION in $LOCATIONS; do
- case $LOCATION in
- # Escape the opening bracket, or some shells like Dash will not
- # match them properly.
- \[*.cpp|\[*.cc|\[*.h) # These are valid source locations in our executable
- ;;
- *) # Everything else is not!
- BAD_LOCATIONS="$BAD_LOCATIONS $LOCATION"
- ;;
- esac
- done
- if [ "$BAD_LOCATIONS" ]; then
- dump "ERROR: Generated stack trace doesn't contain valid source locations:"
- cat "$BUILD_LOG"
- echo "Bad locations are: $BAD_LOCATIONS"
- exit 1
- fi
- echo "All clear! Congratulations."
|