Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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.
31 # Sanitize the environment
32 export LANG=C
33 export LC_ALL=C
35 if [ "$BASH_VERSION" ]; then
36 set -o posix
37 fi
39 PROGDIR=$(dirname "$0")
40 PROGDIR=$(cd "$PROGDIR" && pwd)
41 PROGNAME=$(basename "$0")
43 . $PROGDIR/common-functions.sh
45 DEFAULT_ABI="armeabi"
46 VALID_ABIS="armeabi armeabi-v7a x86 mips"
48 ABI=
49 ADB=
50 ALL_TESTS=
51 ENABLE_M32=
52 HELP=
53 HELP_ALL=
54 NDK_DIR=
55 NO_CLEANUP=
56 NO_DEVICE=
57 NUM_JOBS=$(get_core_count)
58 TMPDIR=
60 for 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
81 done
83 if [ "$HELP" -o "$HELP_ALL" ]; then
84 echo "\
85 Usage: $PROGNAME [options]
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:
95 - Rebuild the host version of Google Breakpad in a temporary
96 directory (with the Auto-tools based build system).
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.
103 - Rebuild the Android client library and a test crashing program with the
104 Android NDK build system (ndk-build).
106 - Require an Android device connected to your machine, and the 'adb'
107 tool in your path. They are used to:
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.
115 You can however skip this requirement and only test the builds by using
116 the --no-device flag.
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.
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.
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).
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:
137 $VALID_ABIS
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 "
146 fi # HELP_ALL
148 echo "\
149 Valid options:
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."
165 exit 0
166 fi
168 TESTAPP_DIR=$PROGDIR/sample_app
170 # Select NDK install directory.
171 if [ -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"
178 else
179 log "Using NDK directory: $NDK_DIR"
180 fi
181 # Small sanity check.
182 NDK_BUILD="$NDK_DIR/ndk-build"
183 if [ ! -f "$NDK_BUILD" ]; then
184 panic "Your NDK directory is not valid (missing ndk-build): $NDK_DIR"
185 fi
187 # Ensure the temporary directory is deleted on exit, except if the --no-cleanup
188 # option is used.
190 clean_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 }
202 atexit clean_tmpdir
204 # If --tmp-dir=<path> is not used, create a temporary directory.
205 # Otherwise, start by cleaning up the user-provided path.
206 if [ -z "$TMPDIR" ]; then
207 TMPDIR=$(mktemp -d /tmp/$PROGNAME.XXXXXXXX)
208 fail_panic "Can't create temporary directory!"
209 log "Using temporary directory: $TMPDIR"
210 else
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
219 fi
221 if [ -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
227 fi
229 BUILD_LOG="$TMPDIR/build.log"
230 RUN_LOG="$TMPDIR/run.log"
231 CRASH_LOG="$TMPDIR/crash.log"
233 set_run_log "$RUN_LOG"
235 TMPHOST="$TMPDIR/host-local"
237 cd "$TMPDIR"
239 # Build host version of the tools
240 dump "Building host binaries."
241 CONFIGURE_FLAGS=
242 if [ "$ENABLE_M32" ]; then
243 CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-m32"
244 fi
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 )
251 fail_panic "Can't build host binaries!"
253 if [ "$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!!"
260 fi
262 TMPBIN=$TMPHOST/bin
264 # Generate a stand-alone NDK toolchain
266 # Extract CPU ABI and architecture from device, if any.
267 if 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
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
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
292 else
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
300 fi
302 # Check the ABI value
303 VALID=
304 for VALID_ABI in $VALID_ABIS; do
305 if [ "$ABI" = "$VALID_ABI" ]; then
306 VALID=true
307 break
308 fi
309 done
311 if [ -z "$VALID" ]; then
312 panic "Unknown CPU ABI '$ABI'. Valid values are: $VALID_ABIS"
313 fi
315 # Extract architecture name from ABI
316 case $ABI in
317 armeabi*) ARCH=arm;;
318 *) ARCH=$ABI;;
319 esac
321 # Extract GNU configuration name
322 case $ARCH in
323 arm)
324 GNU_CONFIG=arm-linux-androideabi
325 ;;
326 x86)
327 GNU_CONFIG=i686-linux-android
328 ;;
329 *)
330 GNU_CONFIG="$ARCH-linux-android"
331 ;;
332 esac
334 # Generate standalone NDK toolchain installation
335 NDK_STANDALONE="$TMPDIR/ndk-$ARCH-toolchain"
336 echo "Generating NDK standalone toolchain installation"
337 mkdir -p "$NDK_STANDALONE"
338 # NOTE: The --platform=android-9 is required to provide <regex.h> for GTest.
339 run "$NDK_DIR/build/tools/make-standalone-toolchain.sh" \
340 --arch="$ARCH" \
341 --platform=android-9 \
342 --install-dir="$NDK_STANDALONE"
343 fail_panic "Can't generate standalone NDK toolchain installation!"
345 # Rebuild the client library, processor and tools with the auto-tools based
346 # build system. Even though it's not going to be used, this checks that this
347 # still works correctly.
348 echo "Building full Android binaries with configure/make"
349 TMPTARGET="$TMPDIR/target-local"
350 (
351 PATH="$NDK_STANDALONE/bin:$PATH"
352 run mkdir "$TMPTARGET" &&
353 run mkdir "$TMPDIR"/build-target &&
354 run cd "$TMPDIR"/build-target &&
355 run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
356 --host="$GNU_CONFIG" &&
357 run2 make -j$NUM_JOBS install
358 )
359 fail_panic "Could not rebuild Android binaries!"
361 # Build and/or run unit test suite.
362 # If --no-device is used, only rebuild it, otherwise, run in on the
363 # connected device.
364 if [ "$NO_DEVICE" ]; then
365 ACTION="Building"
366 # This is a trick to force the Makefile to ignore running the scripts.
367 TESTS_ENVIRONMENT="TESTS_ENVIRONMENT=true"
368 else
369 ACTION="Running"
370 TESTS_ENVIRONMENT=
371 fi
373 (
374 PATH="$NDK_STANDALONE/bin:$PATH"
375 run cd "$TMPDIR"/build-target &&
376 # Reconfigure to only run the client unit test suite.
377 # This one should _never_ fail.
378 dump "$ACTION Android client library unit tests."
379 run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
380 --host="$GNU_CONFIG" \
381 --disable-tools \
382 --disable-processor &&
383 run make -j$NUM_JOBS check $TESTS_ENVIRONMENT || exit $?
385 if [ "$ALL_TESTS" ]; then
386 dump "$ACTION Tools and processor unit tests."
387 # Reconfigure to run the processor and tools tests.
388 # Most of these fail for now, so do not worry about it.
389 run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
390 --host="$GNU_CONFIG" &&
391 run make -j$NUM_JOBS check $TESTS_ENVIRONMENT
392 if [ $? != 0 ]; then
393 dump "Tools and processor unit tests failed as expected. \
394 Use --verbose for results."
395 fi
396 fi
397 )
398 fail_panic "Client library unit test suite failed!"
400 # Copy sources to temporary directory
401 PROJECT_DIR=$TMPDIR/project
402 dump "Copying test program sources to: $PROJECT_DIR"
403 run cp -r "$TESTAPP_DIR" "$PROJECT_DIR" &&
404 run rm -rf "$PROJECT_DIR/obj" &&
405 run rm -rf "$PROJECT_DIR/libs"
406 fail_panic "Could not copy test program sources to: $PROJECT_DIR"
408 # Build the test program with ndk-build.
409 dump "Building test program with ndk-build"
410 export NDK_MODULE_PATH="$PROGDIR"
411 NDK_BUILD_FLAGS="-j$NUM_JOBS"
412 if verbosity_is_higher_than 1; then
413 NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS NDK_LOG=1 V=1"
414 fi
415 run "$NDK_DIR/ndk-build" -C "$PROJECT_DIR" $NDK_BUILD_FLAGS APP_ABI=$ABI
416 fail_panic "Can't build test program!"
418 # Unless --no-device was used, stop right here if ADB isn't in the path,
419 # or there is no connected device.
420 if [ "$NO_DEVICE" ]; then
421 dump "Done. Please connect a device to run all tests!"
422 clean_exit 0
423 fi
425 # Push the program to the device.
426 TESTAPP=test_google_breakpad
427 TESTAPP_FILE="$PROJECT_DIR/libs/$ABI/test_google_breakpad"
428 if [ ! -f "$TESTAPP_FILE" ]; then
429 panic "Device requires '$ABI' binaries. None found!"
430 fi
432 # Run the program there
433 dump "Installing test program on device"
434 DEVICE_TMP=/data/local/tmp
435 adb_push "$TESTAPP_FILE" "$DEVICE_TMP/"
436 fail_panic "Cannot push test program to device!"
438 dump "Running test program on device"
439 adb_shell cd "$DEVICE_TMP" "&&" ./$TESTAPP > "$CRASH_LOG" 2>/dev/null
440 if [ $? = 0 ]; then
441 panic "Test program did *not* crash as expected!"
442 fi
443 if verbosity_is_higher_than 0; then
444 echo -n "Crash log: "
445 cat "$CRASH_LOG"
446 fi
448 # Extract minidump from device
449 MINIDUMP_NAME=$(awk '$1 == "Dump" && $2 == "path:" { print $3; }' "$CRASH_LOG")
450 MINIDUMP_NAME=$(basename "$MINIDUMP_NAME")
451 if [ -z "$MINIDUMP_NAME" ]; then
452 panic "Test program didn't write minidump properly!"
453 fi
455 dump "Extracting minidump: $MINIDUMP_NAME"
456 adb_pull "$DEVICE_TMP/$MINIDUMP_NAME" .
457 fail_panic "Can't extract minidump!"
459 dump "Parsing test program symbols"
460 if verbosity_is_higher_than 1; then
461 log "COMMAND: $TMPBIN/dump_syms \
462 $PROJECT_DIR/obj/local/$ABI/$TESTAPP >$TESTAPP.sym"
463 fi
464 "$TMPBIN/dump_syms" "$PROJECT_DIR/obj/local/$ABI/$TESTAPP" > $TESTAPP.sym
465 fail_panic "dump_syms doesn't work!"
467 VERSION=$(awk '$1 == "MODULE" { print $4; }' $TESTAPP.sym)
468 dump "Found module version: $VERSION"
469 if [ -z "$VERSION" ]; then
470 echo "ERROR: Can't find proper module version from symbol dump!"
471 head -n5 $TESTAPP.sym
472 clean_exit 1
473 fi
475 run mkdir -p "$TMPDIR/symbols/$TESTAPP/$VERSION"
476 run mv $TESTAPP.sym "$TMPDIR/symbols/$TESTAPP/$VERSION/"
478 dump "Generating stack trace"
479 # Don't use 'run' to be able to send stdout and stderr to two different files.
480 log "COMMAND: $TMPBIN/minidump_stackwalk $MINIDUMP_NAME symbols"
481 "$TMPBIN/minidump_stackwalk" $MINIDUMP_NAME \
482 "$TMPDIR/symbols" \
483 > "$BUILD_LOG" 2>>"$RUN_LOG"
484 fail_panic "minidump_stackwalk doesn't work!"
486 dump "Checking stack trace content"
488 if verbosity_is_higher_than 1; then
489 cat "$BUILD_LOG"
490 fi
492 # The generated stack trace should look like the following:
493 #
494 # Thread 0 (crashed)
495 # 0 test_google_breakpad!crash [test_breakpad.cpp : 17 + 0x4]
496 # r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c
497 # r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
498 # sp = 0xbea2cb50 lr = 0x00009025 pc = 0x00008f84
499 # Found by: given as instruction pointer in context
500 # 1 test_google_breakpad!main [test_breakpad.cpp : 25 + 0x3]
501 # r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c
502 # r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
503 # sp = 0xbea2cb50 pc = 0x00009025
504 # Found by: call frame info
505 # 2 libc.so + 0x164e5
506 # r4 = 0x00008f64 r5 = 0xbea2cc34 r6 = 0x00000001 r7 = 0xbea2cc3c
507 # r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
508 # sp = 0xbea2cc18 pc = 0x400c34e7
509 # Found by: call frame info
510 # ...
511 #
512 # The most important part for us is ensuring that the source location could
513 # be extracted, so look at the 'test_breakpad.cpp' references here.
514 #
515 # First, extract all the lines with test_google_breakpad! in them, and
516 # dump the corresponding crash location.
517 #
518 # Note that if the source location can't be extracted, the second field
519 # will only be 'test_google_breakpad' without the exclamation mark.
520 #
521 LOCATIONS=$(awk '$2 ~ "^test_google_breakpad!.*" { print $3; }' "$BUILD_LOG")
523 if [ -z "$LOCATIONS" ]; then
524 if verbosity_is_lower_than 1; then
525 cat "$BUILD_LOG"
526 fi
527 panic "No source location found in stack trace!"
528 fi
530 # Now check that they all match "[<source file>"
531 BAD_LOCATIONS=
532 for LOCATION in $LOCATIONS; do
533 case $LOCATION in
534 # Escape the opening bracket, or some shells like Dash will not
535 # match them properly.
536 \[*.cpp|\[*.cc|\[*.h) # These are valid source locations in our executable
537 ;;
538 *) # Everything else is not!
539 BAD_LOCATIONS="$BAD_LOCATIONS $LOCATION"
540 ;;
541 esac
542 done
544 if [ "$BAD_LOCATIONS" ]; then
545 dump "ERROR: Generated stack trace doesn't contain valid source locations:"
546 cat "$BUILD_LOG"
547 echo "Bad locations are: $BAD_LOCATIONS"
548 exit 1
549 fi
551 echo "All clear! Congratulations."