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