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 31export LANG=C 32export LC_ALL=C 33 34if [ "$BASH_VERSION" ]; then 35 set -o posix 36fi 37 38PROGDIR=$(dirname "$0") 39PROGDIR=$(cd "$PROGDIR" && pwd) 40PROGNAME=$(basename "$0") 41 42. $PROGDIR/common-functions.sh 43 44DEFAULT_ABI="armeabi" 45VALID_ABIS="armeabi armeabi-v7a x86 mips" 46 47ABI= 48ADB= 49ALL_TESTS= 50ENABLE_M32= 51HELP= 52HELP_ALL= 53NDK_DIR= 54NO_CLEANUP= 55NO_DEVICE= 56NUM_JOBS=$(get_core_count) 57TMPDIR= 58 59for 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 80done 81 82if [ "$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 165fi 166 167TESTAPP_DIR=$PROGDIR/sample_app 168 169# Select NDK install directory. 170if [ -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" 177else 178 log "Using NDK directory: $NDK_DIR" 179fi 180# Small sanity check. 181NDK_BUILD="$NDK_DIR/ndk-build" 182if [ ! -f "$NDK_BUILD" ]; then 183 panic "Your NDK directory is not valid (missing ndk-build): $NDK_DIR" 184fi 185 186# Ensure the temporary directory is deleted on exit, except if the --no-cleanup 187# option is used. 188 189clean_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 201atexit 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. 205if [ -z "$TMPDIR" ]; then 206 TMPDIR=$(mktemp -d /tmp/$PROGNAME.XXXXXXXX) 207 fail_panic "Can't create temporary directory!" 208 log "Using temporary directory: $TMPDIR" 209else 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 218fi 219 220if [ -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 226fi 227 228BUILD_LOG="$TMPDIR/build.log" 229RUN_LOG="$TMPDIR/run.log" 230CRASH_LOG="$TMPDIR/crash.log" 231 232set_run_log "$RUN_LOG" 233 234TMPHOST="$TMPDIR/host-local" 235 236cd "$TMPDIR" 237 238# Build host version of the tools 239dump "Building host binaries." 240CONFIGURE_FLAGS= 241if [ "$ENABLE_M32" ]; then 242 CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-m32" 243fi 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) 250fail_panic "Can't build host binaries!" 251 252if [ "$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!!" 259fi 260 261TMPBIN=$TMPHOST/bin 262 263# Generate a stand-alone NDK toolchain 264 265# Extract CPU ABI and architecture from device, if any. 266if 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 291else 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 299fi 300 301# Check the ABI value 302VALID= 303for VALID_ABI in $VALID_ABIS; do 304 if [ "$ABI" = "$VALID_ABI" ]; then 305 VALID=true 306 break 307 fi 308done 309 310if [ -z "$VALID" ]; then 311 panic "Unknown CPU ABI '$ABI'. Valid values are: $VALID_ABIS" 312fi 313 314# Extract architecture name from ABI 315case $ABI in 316 armeabi*) ARCH=arm;; 317 *) ARCH=$ABI;; 318esac 319 320# Extract GNU configuration name 321case $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 ;; 334esac 335 336# Generate standalone NDK toolchain installation 337NDK_STANDALONE="$TMPDIR/ndk-$ARCH-toolchain" 338echo "Generating NDK standalone toolchain installation" 339mkdir -p "$NDK_STANDALONE" 340# NOTE: The --platform=android-9 is required to provide <regex.h> for GTest. 341run "$NDK_DIR/build/tools/make-standalone-toolchain.sh" \ 342 --arch="$ARCH" \ 343 --platform=android-9 \ 344 --install-dir="$NDK_STANDALONE" 345fail_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. 350echo "Building full Android binaries with configure/make" 351TMPTARGET="$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) 361fail_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. 366if [ "$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" 370else 371 ACTION="Running" 372 TESTS_ENVIRONMENT= 373fi 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. \ 396Use --verbose for results." 397 fi 398 fi 399) 400fail_panic "Client library unit test suite failed!" 401 402# Copy sources to temporary directory 403PROJECT_DIR=$TMPDIR/project 404dump "Copying test program sources to: $PROJECT_DIR" 405run cp -r "$TESTAPP_DIR" "$PROJECT_DIR" && 406run rm -rf "$PROJECT_DIR/obj" && 407run rm -rf "$PROJECT_DIR/libs" 408fail_panic "Could not copy test program sources to: $PROJECT_DIR" 409 410# Build the test program with ndk-build. 411dump "Building test program with ndk-build" 412export NDK_MODULE_PATH="$PROGDIR" 413NDK_BUILD_FLAGS="-j$NUM_JOBS" 414if verbosity_is_higher_than 1; then 415 NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS NDK_LOG=1 V=1" 416fi 417run "$NDK_DIR/ndk-build" -C "$PROJECT_DIR" $NDK_BUILD_FLAGS APP_ABI=$ABI 418fail_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. 422if [ "$NO_DEVICE" ]; then 423 dump "Done. Please connect a device to run all tests!" 424 clean_exit 0 425fi 426 427# Push the program to the device. 428TESTAPP=test_google_breakpad 429TESTAPP_FILE="$PROJECT_DIR/libs/$ABI/test_google_breakpad" 430if [ ! -f "$TESTAPP_FILE" ]; then 431 panic "Device requires '$ABI' binaries. None found!" 432fi 433 434# Run the program there 435dump "Installing test program on device" 436DEVICE_TMP=/data/local/tmp 437adb_push "$TESTAPP_FILE" "$DEVICE_TMP/" 438fail_panic "Cannot push test program to device!" 439 440dump "Running test program on device" 441adb_shell cd "$DEVICE_TMP" "&&" ./$TESTAPP > "$CRASH_LOG" 2>/dev/null 442if [ $? = 0 ]; then 443 panic "Test program did *not* crash as expected!" 444fi 445if verbosity_is_higher_than 0; then 446 echo -n "Crash log: " 447 cat "$CRASH_LOG" 448fi 449 450# Extract minidump from device 451MINIDUMP_NAME=$(awk '$1 == "Dump" && $2 == "path:" { print $3; }' "$CRASH_LOG") 452MINIDUMP_NAME=$(basename "$MINIDUMP_NAME") 453if [ -z "$MINIDUMP_NAME" ]; then 454 panic "Test program didn't write minidump properly!" 455fi 456 457dump "Extracting minidump: $MINIDUMP_NAME" 458adb_pull "$DEVICE_TMP/$MINIDUMP_NAME" . 459fail_panic "Can't extract minidump!" 460 461dump "Parsing test program symbols" 462if verbosity_is_higher_than 1; then 463 log "COMMAND: $TMPBIN/dump_syms \ 464 $PROJECT_DIR/obj/local/$ABI/$TESTAPP >$TESTAPP.sym" 465fi 466"$TMPBIN/dump_syms" "$PROJECT_DIR/obj/local/$ABI/$TESTAPP" > $TESTAPP.sym 467fail_panic "dump_syms doesn't work!" 468 469VERSION=$(awk '$1 == "MODULE" { print $4; }' $TESTAPP.sym) 470dump "Found module version: $VERSION" 471if [ -z "$VERSION" ]; then 472 echo "ERROR: Can't find proper module version from symbol dump!" 473 head -n5 $TESTAPP.sym 474 clean_exit 1 475fi 476 477run mkdir -p "$TMPDIR/symbols/$TESTAPP/$VERSION" 478run mv $TESTAPP.sym "$TMPDIR/symbols/$TESTAPP/$VERSION/" 479 480dump "Generating stack trace" 481# Don't use 'run' to be able to send stdout and stderr to two different files. 482log "COMMAND: $TMPBIN/minidump_stackwalk $MINIDUMP_NAME symbols" 483"$TMPBIN/minidump_stackwalk" $MINIDUMP_NAME \ 484 "$TMPDIR/symbols" \ 485 > "$BUILD_LOG" 2>>"$RUN_LOG" 486fail_panic "minidump_stackwalk doesn't work!" 487 488dump "Checking stack trace content" 489 490if verbosity_is_higher_than 1; then 491 cat "$BUILD_LOG" 492fi 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# 523LOCATIONS=$(awk '$2 ~ "^test_google_breakpad!.*" { print $3; }' "$BUILD_LOG") 524 525if [ -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!" 530fi 531 532# Now check that they all match "[<source file>" 533BAD_LOCATIONS= 534for 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 544done 545 546if [ "$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 551fi 552 553echo "All clear! Congratulations." 554 555