1#!/bin/sh 2# Copyright (c) 2012 Google Inc. 3# All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions are 7# met: 8# 9# * Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# * Redistributions in binary form must reproduce the above 12# copyright notice, this list of conditions and the following disclaimer 13# in the documentation and/or other materials provided with the 14# distribution. 15# * Neither the name of Google Inc. nor the names of its 16# contributors may be used to endorse or promote products derived from 17# this software without specific prior written permission. 18# 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31# Sanitize the environment 32export LANG=C 33export LC_ALL=C 34 35if [ "$BASH_VERSION" ]; then 36 set -o posix 37fi 38 39PROGDIR=$(dirname "$0") 40PROGDIR=$(cd "$PROGDIR" && pwd) 41PROGNAME=$(basename "$0") 42 43. $PROGDIR/common-functions.sh 44 45DEFAULT_ABI="armeabi" 46VALID_ABIS="armeabi armeabi-v7a x86 mips" 47 48ABI= 49ADB= 50ALL_TESTS= 51ENABLE_M32= 52HELP= 53HELP_ALL= 54NDK_DIR= 55NO_CLEANUP= 56NO_DEVICE= 57NUM_JOBS=$(get_core_count) 58TMPDIR= 59 60for opt do 61 # The following extracts the value if the option is like --name=<value>. 62 optarg=$(expr -- $opt : '^--[^=]*=\(.*\)$') 63 case $opt in 64 --abi=*) ABI=$optarg;; 65 --adb=*) ADB=$optarg;; 66 --all-tests) ALL_TESTS=true;; 67 --enable-m32) ENABLE_M32=true;; 68 --help|-h|-?) HELP=TRUE;; 69 --help-all) HELP_ALL=true;; 70 --jobs=*) NUM_JOBS=$optarg;; 71 --ndk-dir=*) NDK_DIR=$optarg;; 72 --tmp-dir=*) TMPDIR=$optarg;; 73 --no-cleanup) NO_CLEANUP=true;; 74 --no-device) NO_DEVICE=true;; 75 --quiet) decrease_verbosity;; 76 --verbose) increase_verbosity;; 77 -*) panic "Invalid option '$opt', see --help for details.";; 78 *) panic "This script doesn't take any parameters. See --help for details." 79 ;; 80 esac 81done 82 83if [ "$HELP" -o "$HELP_ALL" ]; then 84 echo "\ 85 Usage: $PROGNAME [options] 86 87 This script is used to check that your Google Breakpad source tree can 88 be properly built for Android, and that the client library and host tools 89 work properly together. 90" 91 if [ "$HELP_ALL" ]; then 92 echo "\ 93 In more details, this script will: 94 95 - Rebuild the host version of Google Breakpad in a temporary 96 directory (with the Auto-tools based build system). 97 98 - Rebuild the Android client library with the Google Breakpad build 99 system (using autotools/configure). This requires that you define 100 ANDROID_NDK_ROOT in your environment to point to a valid Android NDK 101 installation directory, or use the --ndk-dir=<path> option. 102 103 - Rebuild the Android client library and a test crashing program with the 104 Android NDK build system (ndk-build). 105 106 - Require an Android device connected to your machine, and the 'adb' 107 tool in your path. They are used to: 108 109 - Install and run a test crashing program. 110 - Extract the corresponding minidump from the device. 111 - Dump the symbols from the test program on the host with 'dump_syms' 112 - Generate a stack trace with 'minidump_stackwalk' 113 - Check the stack trace content for valid source file locations. 114 115 You can however skip this requirement and only test the builds by using 116 the --no-device flag. 117 118 By default, all generated files will be created in a temporary directory 119 that is removed when the script completion. If you want to inspect the 120 files, use the --no-cleanup option. 121 122 Finally, use --verbose to increase the verbosity level, this will help 123 you see which exact commands are being issues and their result. Use the 124 flag twice for even more output. Use --quiet to decrease verbosity 125 instead and run the script silently. 126 127 If you have a device connected, the script will probe it to determine 128 its primary CPU ABI, and build the test program for it. You can however 129 use the --abi=<name> option to override this (this can be useful to check 130 the secondary ABI, e.g. using --abi=armeabi to check that such a program 131 works correctly on an ARMv7-A device). 132 133 If you don't have a device connected, the test program will be built (but 134 not run) with the default '$DEFAULT_ABI' ABI. Again, you can use 135 --abi=<name> to override this. Valid ABI names are: 136 137 $VALID_ABIS 138 139 The script will only run the client library unit test on the device 140 by default. You can use --all-tests to also build and run the unit 141 tests for the Breakpad tools and processor, but be warned that this 142 adds several minutes of testing time. --all-tests will also run the 143 host unit tests suite. 144" 145 146 fi # HELP_ALL 147 148 echo "\ 149 Valid options: 150 151 --help|-h|-? Display this message. 152 --help-all Display extended help. 153 --enable-m32 Build 32-bit version of host tools. 154 --abi=<name> Specify target CPU ABI [auto-detected]. 155 --jobs=<count> Run <count> build tasks in parallel [$NUM_JOBS]. 156 --ndk-dir=<path> Specify NDK installation directory. 157 --tmp-dir=<path> Specify temporary directory (will be wiped-out). 158 --adb=<path> Specify adb program path. 159 --no-cleanup Don't remove temporary directory after completion. 160 --no-device Do not try to detect devices, nor run crash test. 161 --all-tests Run all unit tests (i.e. tools and processor ones too). 162 --verbose Increase verbosity. 163 --quiet Decrease verbosity." 164 165 exit 0 166fi 167 168TESTAPP_DIR=$PROGDIR/sample_app 169 170# Select NDK install directory. 171if [ -z "$NDK_DIR" ]; then 172 if [ -z "$ANDROID_NDK_ROOT" ]; then 173 panic "Please define ANDROID_NDK_ROOT in your environment, or use \ 174--ndk-dir=<path>." 175 fi 176 NDK_DIR="$ANDROID_NDK_ROOT" 177 log "Found NDK directory: $NDK_DIR" 178else 179 log "Using NDK directory: $NDK_DIR" 180fi 181# Small sanity check. 182NDK_BUILD="$NDK_DIR/ndk-build" 183if [ ! -f "$NDK_BUILD" ]; then 184 panic "Your NDK directory is not valid (missing ndk-build): $NDK_DIR" 185fi 186 187# Ensure the temporary directory is deleted on exit, except if the --no-cleanup 188# option is used. 189 190clean_tmpdir () { 191 if [ "$TMPDIR" ]; then 192 if [ -z "$NO_CLEANUP" ]; then 193 log "Cleaning up: $TMPDIR" 194 rm -rf "$TMPDIR" 195 else 196 dump "Temporary directory contents preserved: $TMPDIR" 197 fi 198 fi 199 exit "$@" 200} 201 202atexit clean_tmpdir 203 204# If --tmp-dir=<path> is not used, create a temporary directory. 205# Otherwise, start by cleaning up the user-provided path. 206if [ -z "$TMPDIR" ]; then 207 TMPDIR=$(mktemp -d /tmp/$PROGNAME.XXXXXXXX) 208 fail_panic "Can't create temporary directory!" 209 log "Using temporary directory: $TMPDIR" 210else 211 if [ ! -d "$TMPDIR" ]; then 212 mkdir -p "$TMPDIR" 213 fail_panic "Can't create temporary directory: $TMPDIR" 214 else 215 log "Cleaning up temporary directory: $TMPDIR" 216 rm -rf "$TMPDIR"/* 217 fail_panic "Cannot cleanup temporary directory!" 218 fi 219fi 220 221if [ -z "$NO_DEVICE" ]; then 222 if ! adb_check_device $ADB; then 223 echo "$(adb_get_error)" 224 echo "Use --no-device to build the code without running any tests." 225 exit 1 226 fi 227fi 228 229BUILD_LOG="$TMPDIR/build.log" 230RUN_LOG="$TMPDIR/run.log" 231CRASH_LOG="$TMPDIR/crash.log" 232 233set_run_log "$RUN_LOG" 234 235TMPHOST="$TMPDIR/host-local" 236 237cd "$TMPDIR" 238 239# Build host version of the tools 240dump "Building host binaries." 241CONFIGURE_FLAGS= 242if [ "$ENABLE_M32" ]; then 243 CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-m32" 244fi 245( 246 run mkdir "$TMPDIR/build-host" && 247 run cd "$TMPDIR/build-host" && 248 run2 "$PROGDIR/../configure" --prefix="$TMPHOST" $CONFIGURE_FLAGS && 249 run2 make -j$NUM_JOBS install 250) 251fail_panic "Can't build host binaries!" 252 253if [ "$ALL_TESTS" ]; then 254 dump "Running host unit tests." 255 ( 256 run cd "$TMPDIR/build-host" && 257 run2 make -j$NUM_JOBS check 258 ) 259 fail_panic "Host unit tests failed!!" 260fi 261 262TMPBIN=$TMPHOST/bin 263 264# Generate a stand-alone NDK toolchain 265 266# Extract CPU ABI and architecture from device, if any. 267if adb_check_device; then 268 DEVICE_ABI=$(adb_shell getprop ro.product.cpu.abi) 269 DEVICE_ABI2=$(adb_shell getprop ro.product.cpu.abi2) 270 if [ -z "$DEVICE_ABI" ]; then 271 panic "Can't extract ABI from connected device!" 272 fi 273 if [ "$DEVICE_ABI2" ]; then 274 dump "Found device ABIs: $DEVICE_ABI $DEVICE_ABI2" 275 else 276 dump "Found device ABI: $DEVICE_ABI" 277 DEVICE_ABI2=$DEVICE_ABI 278 fi 279 280 # If --abi=<name> is used, check that the device supports it. 281 if [ "$ABI" -a "$DEVICE_ABI" != "$ABI" -a "$DEVICE_ABI2" != "$ABI" ]; then 282 dump "ERROR: Device ABI(s) do not match --abi command-line value ($ABI)!" 283 panic "Please use --no-device to skip device tests." 284 fi 285 286 if [ -z "$ABI" ]; then 287 ABI=$DEVICE_ABI 288 dump "Using CPU ABI: $ABI (device)" 289 else 290 dump "Using CPU ABI: $ABI (command-line)" 291 fi 292else 293 if [ -z "$ABI" ]; then 294 # No device connected, choose default ABI 295 ABI=$DEFAULT_ABI 296 dump "Using CPU ABI: $ABI (default)" 297 else 298 dump "Using CPU ABI: $ABI (command-line)" 299 fi 300fi 301 302# Check the ABI value 303VALID= 304for VALID_ABI in $VALID_ABIS; do 305 if [ "$ABI" = "$VALID_ABI" ]; then 306 VALID=true 307 break 308 fi 309done 310 311if [ -z "$VALID" ]; then 312 panic "Unknown CPU ABI '$ABI'. Valid values are: $VALID_ABIS" 313fi 314 315# Extract architecture name from ABI 316case $ABI in 317 armeabi*) ARCH=arm;; 318 *) ARCH=$ABI;; 319esac 320 321# Extract GNU configuration name 322case $ARCH in 323 arm) 324 GNU_CONFIG=arm-linux-androideabi 325 ;; 326 x86) 327 GNU_CONFIG=i686-linux-android 328 ;; 329 mips) 330 GNU_CONFIG=mipsel-linux-android 331 ;; 332 *) 333 GNU_CONFIG="$ARCH-linux-android" 334 ;; 335esac 336 337# Generate standalone NDK toolchain installation 338NDK_STANDALONE="$TMPDIR/ndk-$ARCH-toolchain" 339echo "Generating NDK standalone toolchain installation" 340mkdir -p "$NDK_STANDALONE" 341# NOTE: The --platform=android-9 is required to provide <regex.h> for GTest. 342run "$NDK_DIR/build/tools/make-standalone-toolchain.sh" \ 343 --arch="$ARCH" \ 344 --platform=android-9 \ 345 --install-dir="$NDK_STANDALONE" 346fail_panic "Can't generate standalone NDK toolchain installation!" 347 348# Rebuild the client library, processor and tools with the auto-tools based 349# build system. Even though it's not going to be used, this checks that this 350# still works correctly. 351echo "Building full Android binaries with configure/make" 352TMPTARGET="$TMPDIR/target-local" 353( 354 PATH="$NDK_STANDALONE/bin:$PATH" 355 run mkdir "$TMPTARGET" && 356 run mkdir "$TMPDIR"/build-target && 357 run cd "$TMPDIR"/build-target && 358 run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \ 359 --host="$GNU_CONFIG" && 360 run2 make -j$NUM_JOBS install 361) 362fail_panic "Could not rebuild Android binaries!" 363 364# Build and/or run unit test suite. 365# If --no-device is used, only rebuild it, otherwise, run in on the 366# connected device. 367if [ "$NO_DEVICE" ]; then 368 ACTION="Building" 369 # This is a trick to force the Makefile to ignore running the scripts. 370 TESTS_ENVIRONMENT="TESTS_ENVIRONMENT=true" 371else 372 ACTION="Running" 373 TESTS_ENVIRONMENT= 374fi 375 376( 377 PATH="$NDK_STANDALONE/bin:$PATH" 378 run cd "$TMPDIR"/build-target && 379 # Reconfigure to only run the client unit test suite. 380 # This one should _never_ fail. 381 dump "$ACTION Android client library unit tests." 382 run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \ 383 --host="$GNU_CONFIG" \ 384 --disable-tools \ 385 --disable-processor && 386 run make -j$NUM_JOBS check $TESTS_ENVIRONMENT || exit $? 387 388 if [ "$ALL_TESTS" ]; then 389 dump "$ACTION Tools and processor unit tests." 390 # Reconfigure to run the processor and tools tests. 391 # Most of these fail for now, so do not worry about it. 392 run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \ 393 --host="$GNU_CONFIG" && 394 run make -j$NUM_JOBS check $TESTS_ENVIRONMENT 395 if [ $? != 0 ]; then 396 dump "Tools and processor unit tests failed as expected. \ 397Use --verbose for results." 398 fi 399 fi 400) 401fail_panic "Client library unit test suite failed!" 402 403# Copy sources to temporary directory 404PROJECT_DIR=$TMPDIR/project 405dump "Copying test program sources to: $PROJECT_DIR" 406run cp -r "$TESTAPP_DIR" "$PROJECT_DIR" && 407run rm -rf "$PROJECT_DIR/obj" && 408run rm -rf "$PROJECT_DIR/libs" 409fail_panic "Could not copy test program sources to: $PROJECT_DIR" 410 411# Build the test program with ndk-build. 412dump "Building test program with ndk-build" 413export NDK_MODULE_PATH="$PROGDIR" 414NDK_BUILD_FLAGS="-j$NUM_JOBS" 415if verbosity_is_higher_than 1; then 416 NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS NDK_LOG=1 V=1" 417fi 418run "$NDK_DIR/ndk-build" -C "$PROJECT_DIR" $NDK_BUILD_FLAGS APP_ABI=$ABI 419fail_panic "Can't build test program!" 420 421# Unless --no-device was used, stop right here if ADB isn't in the path, 422# or there is no connected device. 423if [ "$NO_DEVICE" ]; then 424 dump "Done. Please connect a device to run all tests!" 425 clean_exit 0 426fi 427 428# Push the program to the device. 429TESTAPP=test_google_breakpad 430TESTAPP_FILE="$PROJECT_DIR/libs/$ABI/test_google_breakpad" 431if [ ! -f "$TESTAPP_FILE" ]; then 432 panic "Device requires '$ABI' binaries. None found!" 433fi 434 435# Run the program there 436dump "Installing test program on device" 437DEVICE_TMP=/data/local/tmp 438adb_push "$TESTAPP_FILE" "$DEVICE_TMP/" 439fail_panic "Cannot push test program to device!" 440 441dump "Running test program on device" 442adb_shell cd "$DEVICE_TMP" "&&" ./$TESTAPP > "$CRASH_LOG" 2>/dev/null 443if [ $? = 0 ]; then 444 panic "Test program did *not* crash as expected!" 445fi 446if verbosity_is_higher_than 0; then 447 echo -n "Crash log: " 448 cat "$CRASH_LOG" 449fi 450 451# Extract minidump from device 452MINIDUMP_NAME=$(awk '$1 == "Dump" && $2 == "path:" { print $3; }' "$CRASH_LOG") 453MINIDUMP_NAME=$(basename "$MINIDUMP_NAME") 454if [ -z "$MINIDUMP_NAME" ]; then 455 panic "Test program didn't write minidump properly!" 456fi 457 458dump "Extracting minidump: $MINIDUMP_NAME" 459adb_pull "$DEVICE_TMP/$MINIDUMP_NAME" . 460fail_panic "Can't extract minidump!" 461 462dump "Parsing test program symbols" 463if verbosity_is_higher_than 1; then 464 log "COMMAND: $TMPBIN/dump_syms \ 465 $PROJECT_DIR/obj/local/$ABI/$TESTAPP >$TESTAPP.sym" 466fi 467"$TMPBIN/dump_syms" "$PROJECT_DIR/obj/local/$ABI/$TESTAPP" > $TESTAPP.sym 468fail_panic "dump_syms doesn't work!" 469 470VERSION=$(awk '$1 == "MODULE" { print $4; }' $TESTAPP.sym) 471dump "Found module version: $VERSION" 472if [ -z "$VERSION" ]; then 473 echo "ERROR: Can't find proper module version from symbol dump!" 474 head -n5 $TESTAPP.sym 475 clean_exit 1 476fi 477 478run mkdir -p "$TMPDIR/symbols/$TESTAPP/$VERSION" 479run mv $TESTAPP.sym "$TMPDIR/symbols/$TESTAPP/$VERSION/" 480 481dump "Generating stack trace" 482# Don't use 'run' to be able to send stdout and stderr to two different files. 483log "COMMAND: $TMPBIN/minidump_stackwalk $MINIDUMP_NAME symbols" 484"$TMPBIN/minidump_stackwalk" $MINIDUMP_NAME \ 485 "$TMPDIR/symbols" \ 486 > "$BUILD_LOG" 2>>"$RUN_LOG" 487fail_panic "minidump_stackwalk doesn't work!" 488 489dump "Checking stack trace content" 490 491if verbosity_is_higher_than 1; then 492 cat "$BUILD_LOG" 493fi 494 495# The generated stack trace should look like the following: 496# 497# Thread 0 (crashed) 498# 0 test_google_breakpad!crash [test_breakpad.cpp : 17 + 0x4] 499# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c 500# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000 501# sp = 0xbea2cb50 lr = 0x00009025 pc = 0x00008f84 502# Found by: given as instruction pointer in context 503# 1 test_google_breakpad!main [test_breakpad.cpp : 25 + 0x3] 504# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c 505# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000 506# sp = 0xbea2cb50 pc = 0x00009025 507# Found by: call frame info 508# 2 libc.so + 0x164e5 509# r4 = 0x00008f64 r5 = 0xbea2cc34 r6 = 0x00000001 r7 = 0xbea2cc3c 510# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000 511# sp = 0xbea2cc18 pc = 0x400c34e7 512# Found by: call frame info 513# ... 514# 515# The most important part for us is ensuring that the source location could 516# be extracted, so look at the 'test_breakpad.cpp' references here. 517# 518# First, extract all the lines with test_google_breakpad! in them, and 519# dump the corresponding crash location. 520# 521# Note that if the source location can't be extracted, the second field 522# will only be 'test_google_breakpad' without the exclamation mark. 523# 524LOCATIONS=$(awk '$2 ~ "^test_google_breakpad!.*" { print $3; }' "$BUILD_LOG") 525 526if [ -z "$LOCATIONS" ]; then 527 if verbosity_is_lower_than 1; then 528 cat "$BUILD_LOG" 529 fi 530 panic "No source location found in stack trace!" 531fi 532 533# Now check that they all match "[<source file>" 534BAD_LOCATIONS= 535for LOCATION in $LOCATIONS; do 536 case $LOCATION in 537 # Escape the opening bracket, or some shells like Dash will not 538 # match them properly. 539 \[*.cpp|\[*.cc|\[*.h) # These are valid source locations in our executable 540 ;; 541 *) # Everything else is not! 542 BAD_LOCATIONS="$BAD_LOCATIONS $LOCATION" 543 ;; 544 esac 545done 546 547if [ "$BAD_LOCATIONS" ]; then 548 dump "ERROR: Generated stack trace doesn't contain valid source locations:" 549 cat "$BUILD_LOG" 550 echo "Bad locations are: $BAD_LOCATIONS" 551 exit 1 552fi 553 554echo "All clear! Congratulations." 555 556