xref: /aosp_15_r20/external/google-breakpad/android/run-checks.sh (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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