diff --git a/README.ANDROID b/README.ANDROID new file mode 100644 index 00000000..816f57a9 --- /dev/null +++ b/README.ANDROID @@ -0,0 +1,134 @@ +Google Breakpad for Android +=========================== + +This document explains how to use the Google Breakpad client library +on Android, and later generate valid stack traces from the minidumps +it generates. + +Note that this release only supports ARM-based Android systems. +We're working on adding support for x86 and MIPS, but that might +require an udpated NDK release. + + +I. Building the client library: +=============================== + +The Android client is built as a static library that you can +link into your own Android native code. There are two ways to +build it: + +I.1. Building with ndk-build: +----------------------------- + +If you're using the ndk-build build system, you can follow +these simple steps: + + 1/ Include android/google_breakpad/Android.mk from your own + project's Android.mk + + This can be done either directly, or using ndk-build's + import-module feature. + + 2/ Link the library to one of your modules by using: + + LOCAL_STATIC_LIBRARIES += breakpad_client + +NOTE: The client library requires a C++ STL implementation, + which you can select with APP_STL in your Application.mk + + It has been tested succesfully with both STLport and GNU libstdc++ + + +II.1. Building with a standalone Android toolchain: +--------------------------------------------------- + +All you need to do is configure your build with the right 'host' +value, and disable the processor and tools, as in: + + $GOOGLE_BREAKPAD_PATH/configure --host=arm-linux-androideabi \ + --disable-processor \ + --disable-tools + make -j4 + +The library will be under src/client/linux/libbreakpad_client.a + + +II. Using the client library in Android: +======================================== + +The usage instructions are very similar to the Linux ones that are +found at http://code.google.com/p/google-breakpad/wiki/LinuxStarterGuide + +1/ You need to include "client/linux/handler/exception_handler.h" from a C++ + source file. + +2/ If you're not using ndk-build, you also need to: + + - add $GOOGLE_BREAKPAD_PATH to your compiler include path + - add -llog to your linker flags + + Note that ndk-build does that for your automatically. + +3/ Keep in mind that there is no /tmp directory on Android. + + If you use the library from a regular Android applications, specify a + path under your app-specific storage directory. An alternative is to + store them on the SDCard, but this requires a specific permission. + +For a concrete example, see the sample test application under +android/sample_app. See its README for more information. + + +III. Getting a stack trace on the host: +======================================= + +This process is similar to other platforms, but here's a quick example: + +1/ Retrieve the minidumps on your development machine. + +2/ Dump the symbols for your native libraries with the 'dump_syms' tool. + This first requires building the host version of Google Breakpad, then + calling: + + dump_syms $PROJECT_PATH/obj/local/$ABI/libfoo.so > libfoo.so.sym + +3/ Create the symbol directory hierarchy. + + The first line of the generated libfoo.so.sym will have a "MODULE" + entry that carries a hexadecimal version number, e.g.: + + MODULE Linux arm D51B4A5504974FA6ECC1869CAEE3603B0 test_google_breakpad + + Note: The second field could be either 'Linux' or 'Android'. + + Extract the version number, and a 'symbol' directory, for example: + + $PROJECT_PATH/symbols/libfoo.so/$VERSION/ + + Copy/Move your libfoo.sym file there. + +4/ Invoke minidump_stackwalk to create the stack trace: + + minidump_stackwalk $MINIDUMP_FILE $PROJECT_PATH/symbols + +Note that various helper scripts can be found on the web to automate these +steps. + +IV. Verifying the Android build library: +======================================== + +If you modify Google Breakpad and want to check that it still works correctly +on Android, please run the android/run-test-program.sh script which will do all +necessary verifications for you. This includes: + + - Rebuilding the full host package + - Rebuilding the client library with configure/make + - Rebuilding the client library with ndk-build + - Building, installing and running a test crasher program on a device + - Extracting the corresponding minidump, dumping the test program symbols + and generating a stack trace. + - Checking the stack trace for valid source locations. + +For more details, please run: + + android/run-test-program.sh --help-all diff --git a/android/google_breakpad/Android.mk b/android/google_breakpad/Android.mk new file mode 100644 index 00000000..a77e3087 --- /dev/null +++ b/android/google_breakpad/Android.mk @@ -0,0 +1,96 @@ +# Copyright (c) 2012, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# ndk-build module definition for the Google Breakpad client library +# +# To use this file, do the following: +# +# 1/ Include this file from your own Android.mk, either directly +# or with through the NDK's import-module function. +# +# 2/ Use the client static library in your project with: +# +# LOCAL_STATIC_LIBRARIES += breakpad_client +# +# 3/ In your source code, include "src/client/linux/exception_handler.h" +# and use the Linux instructions to use it. +# +# This module works with either the STLport or GNU libstdc++, but you need +# to select one in your Application.mk +# + +# Sanity check. We can only build for ARM for now. +ifneq (,$(filter-out armeabi armeabi-v7a,$(TARGET_ARCH_ABI))) +$(error Sorry, Google Breakpad only works on Android ARM for now!) +endif + +# The top Google Breakpad directory. +# We assume this Android.mk to be under 'android/google_breakpad' + +LOCAL_PATH := $(call my-dir)/../.. + +# Defube the client library module, as a simple static library that +# exports the right include path / linker flags to its users. + +include $(CLEAR_VARS) + +LOCAL_MODULE := breakpad_client + +LOCAL_CPP_EXTENSION := .cc + +# Breakpad uses inline ARM assembly that requires the library +# to be built in ARM mode. Otherwise, the build will fail with +# cryptic assembler messages like: +# Compile++ thumb : google_breakpad_client <= crash_generation_client.cc +# /tmp/cc8aMSoD.s: Assembler messages: +# /tmp/cc8aMSoD.s:132: Error: invalid immediate: 288 is out of range +# /tmp/cc8aMSoD.s:244: Error: invalid immediate: 296 is out of range +LOCAL_ARM_MODE := arm + +# List of client source files, directly taken from Makefile.am +LOCAL_SRC_FILES := \ + src/client/linux/crash_generation/crash_generation_client.cc \ + src/client/linux/handler/exception_handler.cc \ + src/client/linux/log/log.cc \ + src/client/linux/minidump_writer/linux_dumper.cc \ + src/client/linux/minidump_writer/linux_ptrace_dumper.cc \ + src/client/linux/minidump_writer/minidump_writer.cc \ + src/client/minidump_file_writer.cc src/common/convert_UTF.c \ + src/common/md5.cc src/common/string_conversion.cc \ + src/common/linux/file_id.cc src/common/linux/guid_creator.cc \ + src/common/linux/memory_mapped_file.cc \ + src/common/linux/safe_readlink.cc + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/src +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) +LOCAL_EXPORT_LDLIBS := -llog + +include $(BUILD_STATIC_LIBRARY) + +# Done. \ No newline at end of file diff --git a/android/run-checks.sh b/android/run-checks.sh new file mode 100755 index 00000000..86ac16c9 --- /dev/null +++ b/android/run-checks.sh @@ -0,0 +1,594 @@ +#!/bin/sh +# Copyright (c) 2012 Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Sanitize the environment +export LANG=C +export LC_ALL=C + +if [ "$BASH_VERSION" ]; then + set -o posix +fi + +PROGDIR=$(dirname "$0") +PROGDIR=$(cd "$PROGDIR" && pwd) +PROGNAME=$(basename "$0") + +# Utility functions + +TMPDIR= + +# Used to exit the program after removing the temporary directory. +clean_exit () { + if [ "$TMPDIR" ]; then + if [ -z "$NO_CLEANUP" ]; then + log "Cleaning up: $TMPDIR" + rm -rf "$TMPDIR" + else + dump "Temporary directory contents preserved: $TMPDIR" + fi + fi + exit "$@" +} + +# Dump a panic message then exit. +panic () { + echo "ERROR: $@" + clean_exit 1; +} + +# If the previous command failed, dump a panic message then exit. +fail_panic () { + if [ $? != 0 ]; then + panic "$@" + fi; +} + +# Extract number of cores to speed up the builds +get_core_count () { + case $(uname -s) in + Linux) + grep -c -e '^processor' /proc/cpuinfo + ;; + Darwin) + sysctl -n hw.ncpu + ;; + CYGWIN*|*_NT-*) + echo $NUMBER_OF_PROCESSORS + ;; + *) + echo 1 + ;; + esac +} + +ADB= +ENABLE_M32= +HELP= +HELP_ALL= +NDK_DIR= +NO_CLEANUP= +NO_DEVICE= +NUM_JOBS=$(get_core_count) +TMPDIR= +VERBOSE=0 + +for opt do + # The following extracts the value if the option is like --name=. + optarg=$(expr -- $opt : '^--[^=]*=\(.*\)$') + case $opt in + --adb=*) ADB=$optarg;; + --enable-m32) ENABLE_M32=true;; + --help|-h|-?) HELP=TRUE;; + --help-all) HELP_ALL=true;; + --jobs=*) NUM_JOBS=$optarg;; + --ndk-dir=*) NDK_DIR=$optarg;; + --tmp-dir=*) TMPDIR=$optarg;; + --no-cleanup) NO_CLEANUP=true;; + --no-device) NO_DEVICE=true;; + --quiet) VERBOSE=$(( $VERBOSE - 1 ));; + --verbose) VERBOSE=$(( $VERBOSE + 1 ));; + -*) panic "Invalid option '$opt', see --help for details.";; + *) panic "This script doesn't take any parameters. See --help for details." + ;; + esac +done + +if [ "$HELP" -o "$HELP_ALL" ]; then + echo "\ + Usage: $PROGNAME [options] + + This script is used to check that your Google Breakpad source tree can + be properly built for Android, and that the client library and host tools + work properly together. +" + if [ "$HELP_ALL" ]; then + echo "\ + In more details, this script will: + + - Rebuild the host version of Google Breakpad in a temporary + directory (with the Auto-tools based build system). + + - Rebuild the Android client library with the Google Breakpad build + system (using autotools/configure). This requires that you define + ANDROID_NDK_ROOT in your environment to point to a valid Android NDK + installation directory, or use the --ndk-dir= option. + + - Rebuild the Android client library and a test crashing program with the + Android NDK build system (ndk-build). + + - Require an Android device connected to your machine, and the 'adb' + tool in your path. They are used to: + + - Install and run a test crashing program. + - Extract the corresponding minidump from the device. + - Dump the symbols from the test program on the host with 'dump_syms' + - Generate a stack trace with 'minidump_stackwalk' + - Check the stack trace content for valid source file locations. + + You can however skip this requirement and only test the builds by using + the --no-device flag. + + By default, all generated files will be created in a temporary directory + that is removed when the script completion. If you want to inspect the + files, use the --no-cleanup option. + + Finally, use --verbose to increase the verbosity level, this will help + you see which exact commands are being issues and their result. Use the + flag twice for even more output. Use --quiet to decrease verbosity + instead and run the script silently. +" + + fi # HELP_ALL + + echo "\ + Valid options: + + --help|-h|-? Display this message. + --help-all Display extended help. + --enable-m32 Build 32-bit version of host tools. + --jobs= Run build tasks in parallel [$NUM_JOBS]. + --ndk-dir= Specify NDK installation directory. + --tmp-dir= Specify temporary directory (will be wiped-out). + --adb= Specify adb program path. + --no-cleanup Don't remove temporary directory after completion. + --no-device Do not try to detect devices, nor run crash test. + --verbose Increase verbosity. + --quiet Decrease verbosity." + + clean_exit 0 +fi + +# Dump message to stdout, unless verbosity is < 0, i.e. --quiet was called +dump () { + if [ "$VERBOSE" -ge 0 ]; then + echo "$@" + fi +} + +# If --verbose was used, dump a message to stdout. +log () { + if [ "$VERBOSE" -ge 1 ]; then + echo "$@" + fi +} + +# Run a command. Output depends on $VERBOSE: +# $VERBOSE <= 0: Run command, store output into the run log +# $VERBOSE >= 1: Dump command, run it, output goest to stdout +# Note: Ideally, the command's output would go to the run log for $VERBOSE >= 1 +# but the 'tee' tool doesn't preserve the status code of its input pipe +# in case of error. +run () { + local LOGILE + if [ "$RUN_LOG" ]; then + LOGFILE=$RUN_LOG + else + LOGFILE=/dev/null + fi + + if [ "$VERBOSE" -ge 1 ]; then + echo "COMMAND: $@" + "$@" + else + "$@" >>$LOGFILE 2>&1 + fi +} + +# Same as run(), but only dump command output for $VERBOSE >= 2 +run2 () { + local LOGILE + if [ "$RUN_LOG" ]; then + LOGFILE=$RUN_LOG + else + LOGFILE=/dev/null + fi + + if [ "$VERBOSE" -ge 1 ]; then + echo "COMMAND: $@" + fi + if [ "$VERBOSE" -ge 2 ]; then + "$@" + else + "$@" >>$LOGFILE 2>&1 + fi +} + +TESTAPP_DIR=$PROGDIR/sample_app + +# Select NDK install directory. +if [ -z "$NDK_DIR" ]; then + if [ -z "$ANDROID_NDK_ROOT" ]; then + panic "Please define ANDROID_NDK_ROOT in your environment, or use \ +--ndk-dir=." + fi + NDK_DIR="$ANDROID_NDK_ROOT" + log "Found NDK directory: $NDK_DIR" +else + log "Using NDK directory: $NDK_DIR" +fi +# Small sanity check. +NDK_BUILD="$NDK_DIR/ndk-build" +if [ ! -f "$NDK_BUILD" ]; then + panic "Your NDK directory is not valid (missing ndk-build): $NDK_DIR" +fi + +# If --tmp-dir= is not used, create a temporary directory. +# Otherwise, start by cleaning up the user-provided path. +if [ -z "$TMPDIR" ]; then + TMPDIR=$(mktemp -d /tmp/$PROGNAME.XXXXXXXX) + fail_panic "Can't create temporary directory!" + log "Using temporary directory: $TMPDIR" +else + if [ ! -d "$TMPDIR" ]; then + mkdir -p "$TMPDIR" + fail_panic "Can't create temporary directory: $TMPDIR" + else + log "Cleaning up temporary directory: $TMPDIR" + rm -rf "$TMPDIR"/* + fail_panic "Cannot cleanup temporary directory!" + fi +fi + +# Ensure a clean exit when the script is: +# - Interrupted by Ctrl-C (INT) +# - Interrupted by log out (HUP) +# - Being asked to quit nicely (TERM) +# - Being asked to quit and dump core (QUIT) +trap "clean_exit 1" INT HUP TERM QUIT + +# The 'adb shell' command is pretty hopeless, try to make sense of it by: +# 1/ Removing trailing \r from line endings. +# 2/ Ensuring the function returns the command's status code. +# +adb_shell () { + local RET ADB_LOG + ADB_LOG=$(mktemp "$TMPDIR/adb-XXXXXXXX") + "$ADB" shell "$@" ";" echo \$? > "$ADB_LOG" 2>&1 + sed -i -e 's![[:cntrl:]]!!g' "$ADB_LOG" # Remove \r. + RET=$(sed -e '$!d' "$ADB_LOG") # Last line contains status code. + sed -e '$d' "$ADB_LOG" # Print everything except last line. + rm -f "$ADB_LOG" + return $RET +} + +check_for_adb () { + local ADB_VERSION ADB_DEVICES NUM_DEVICES FINGERPRINT + + # Auto-detect ADB in current path when needed. + if [ -z "$ADB" ]; then + ADB=$(which adb 2>/dev/null) + if [ -z "$ADB" ]; then + panic "The 'adb' tool is not in your path! Use either --no-device to\ + only check the builds, or --adb= to specify the tool path." + fi + log "Found ADB path: $ADB" + else + log "Using ADB path: $ADB" + fi + + # Check that it works. + ADB_VERSION=$("$ADB" version 2>/dev/null) + case $ADB_VERSION in + "Android Debug Bridge "*) # Pass. + log "Found ADB version: $ADB_VERSION" + ;; + *) # Fail. + panic "Your ADB binary does not seem to work: $ADB" + ;; + esac + + # Count the number of connected devices. + ADB_DEVICES=$("$ADB" devices 2>/dev/null | awk '$2 == "device" { print $1; }') + if [ "$ADB_DEVICES" ]; then + NUM_DEVICES=$(echo "$ADB_DEVICES" | wc -l) + else + NUM_DEVICES=0 + fi + case $NUM_DEVICES in + 0) + panic "No Android device connected! Connect one, or use --no-device \ +to only check the builds." + ;; + 1) + export ANDROID_SERIAL=$ADB_DEVICES + ;; + *) + if [ "$ANDROID_SERIAL" ]; then + ADB_DEVICES=$ANDROID_SERIAL + NUM_DEVICES=1 + else + dump "ERROR: More than one Android device connected. Please define \ +ANDROID_SERIAL" + dump " in your environment, or use --no-device to only check \ +the builds." + clean_exit 1 + fi + ;; + esac + + FINGERPRINT=$(adb_shell getprop ro.build.fingerprint) + dump "Using device: $ANDROID_SERIAL ($FINGERPRINT)" +} + +if [ -z "$NO_DEVICE" ]; then + check_for_adb +fi + +BUILD_LOG="$TMPDIR/build.log" +RUN_LOG="$TMPDIR/run.log" +CRASH_LOG="$TMPDIR/crash.log" + +TMPHOST="$TMPDIR/host-local" + +cd "$TMPDIR" + +# Build host version of the tools +dump "Building host tools." +CONFIGURE_FLAGS= +if [ "$ENABLE_M32" ]; then + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-m32" +fi +( + run mkdir "$TMPDIR/build-host" && + run cd "$TMPDIR/build-host" && + run2 "$PROGDIR/../configure" --prefix="$TMPHOST" $CONFIGURE_FLAGS && + run2 make -j$NUM_JOBS install +) +fail_panic "Can't build host-tools!" + +TMPBIN=$TMPHOST/bin + +# Generate a stand-alone NDK toolchain + +# Extract ABI and architecture from device, if any. +if [ "$ADB" ]; then + ABI=$(adb_shell getprop ro.product.cpu.abi) + if [ -z "$ABI" ]; then + panic "Can't extract ABI from connected device!" + fi + dump "Found device ABI: $ABI" +else + # No device connected, choose default ABI + ABI=armeabi + dump "Default ABI: $ABI" +fi + +# Compute architecture name +case $ABI in + arm*) ARCH=arm;; + *) ARCH="$ABI";; +esac + +# Extract GNU configuration name +case $ARCH in + arm) GNU_CONFIG=arm-linux-androideabi;; + *) GNU_CONFIG="$ARCH-linux-android";; +esac + +# Generate standalone NDK toolchain installation +NDK_STANDALONE="$TMPDIR/ndk-$ARCH-toolchain" +echo "Generating NDK standalone toolchain installation" +mkdir -p "$NDK_STANDALONE" +run "$NDK_DIR/build/tools/make-standalone-toolchain.sh" \ + --arch="$ARCH" \ + --install-dir="$NDK_STANDALONE" +fail_panic "Can't generate standalone NDK toolchain installation!" + +# Rebuild the client library with the auto-tools base build system. +# Even though it's not going to be used, this checks that this still +# works correctly. +echo "Building client Android library with configure/make" +TMPTARGET="$TMPDIR/target-local" +( + PATH="$NDK_STANDALONE/bin:$PATH" + run mkdir "$TMPTARGET" && + run mkdir "$TMPDIR"/build-target && + run cd "$TMPDIR"/build-target && + run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \ + --host="$GNU_CONFIG" \ + --disable-tools \ + --disable-processor && + run2 make -j$NUM_JOBS install +) +fail_panic "Could not rebuild Android client library!" + +# Copy sources to temporary directory +PROJECT_DIR=$TMPDIR/project +dump "Copying test program sources to: $PROJECT_DIR" +run cp -r "$TESTAPP_DIR" "$PROJECT_DIR" && +run rm -rf "$PROJECT_DIR/obj" && +run rm -rf "$PROJECT_DIR/libs" +fail_panic "Could not copy test program sources to: $PROJECT_DIR" + +# Build the test program with ndk-build. +dump "Building test program with ndk-build" +export NDK_MODULE_PATH="$PROGDIR" +NDK_BUILD_FLAGS="-j$NUM_JOBS" +if [ "$VERBOSE" -ge 2 ]; then + NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS NDK_LOG=1 V=1" +fi +run "$NDK_DIR/ndk-build" -C "$PROJECT_DIR" $NDK_BUILD_FLAGS +fail_panic "Can't build test program!" + +# Unless --no-device was used, stop right here if ADB isn't in the path, +# or there is no connected device. +if [ "$NO_DEVICE" ]; then + dump "Done. Please connect a device to run all tests!" + clean_exit 0 +fi + +# Push the program to the device. +TESTAPP=test_google_breakpad +TESTAPP_FILE="$PROJECT_DIR/libs/$ABI/test_google_breakpad" +if [ ! -f "$TESTAPP_FILE" ]; then + panic "Device requires '$ABI' binaries. None found!" +fi + +# Run the program there +dump "Installing test program on device" +DEVICE_TMP=/data/local/tmp +run "$ADB" push "$TESTAPP_FILE" "$DEVICE_TMP/" +fail_panic "Cannot push test program to device!" + +dump "Running test program on device" +adb_shell cd "$DEVICE_TMP" "&&" ./$TESTAPP > "$CRASH_LOG" 2>/dev/null +if [ $? = 0 ]; then + panic "Test program did *not* crash as expected!" +fi +if [ "$VERBOSE" -ge 1 ]; then + echo -n "Crash log: " + cat "$CRASH_LOG" +fi + +# Extract minidump from device +MINIDUMP_NAME=$(awk '$1 == "Dump" && $2 == "path:" { print $3; }' "$CRASH_LOG") +MINIDUMP_NAME=$(basename "$MINIDUMP_NAME") +if [ -z "$MINIDUMP_NAME" ]; then + panic "Test program didn't write minidump properly!" +fi + +dump "Extracting minidump: $MINIDUMP_NAME" +run "$ADB" pull "$DEVICE_TMP/$MINIDUMP_NAME" . +fail_panic "Can't extract minidump!" + +dump "Parsing test program symbols" +if [ "$VERBOSE" -ge 1 ]; then + log "COMMAND: $TMPBIN/dump_syms \ + $PROJECT_DIR/obj/local/$ABI/$TESTAPP >$TESTAPP.sym" +fi +"$TMPBIN/dump_syms" "$PROJECT_DIR/obj/local/$ABI/$TESTAPP" > $TESTAPP.sym +fail_panic "dump_syms doesn't work!" + +VERSION=$(awk '$1 == "MODULE" { print $4; }' $TESTAPP.sym) +dump "Found module version: $VERSION" +if [ -z "$VERSION" ]; then + echo "ERROR: Can't find proper module version from symbol dump!" + head -n5 $TESTAPP.sym + clean_exit 1 +fi + +run mkdir -p "$TMPDIR/symbols/$TESTAPP/$VERSION" +run mv $TESTAPP.sym "$TMPDIR/symbols/$TESTAPP/$VERSION/" + +dump "Generating stack trace" +# Don't use 'run' to be able to send stdout and stderr to two different files. +log "COMMAND: $TMPBIN/minidump_stackwalk $MINIDUMP_NAME symbols" +"$TMPBIN/minidump_stackwalk" $MINIDUMP_NAME \ + "$TMPDIR/symbols" \ + > "$BUILD_LOG" 2>>"$RUN_LOG" +fail_panic "minidump_stackwalk doesn't work!" + +dump "Checking stack trace content" + +if [ "$VERBOSE" -ge 1 ]; then + cat "$BUILD_LOG" +fi + +# The generated stack trace should look like the following: +# +# Thread 0 (crashed) +# 0 test_google_breakpad!crash [test_breakpad.cpp : 17 + 0x4] +# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c +# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000 +# sp = 0xbea2cb50 lr = 0x00009025 pc = 0x00008f84 +# Found by: given as instruction pointer in context +# 1 test_google_breakpad!main [test_breakpad.cpp : 25 + 0x3] +# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c +# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000 +# sp = 0xbea2cb50 pc = 0x00009025 +# Found by: call frame info +# 2 libc.so + 0x164e5 +# r4 = 0x00008f64 r5 = 0xbea2cc34 r6 = 0x00000001 r7 = 0xbea2cc3c +# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000 +# sp = 0xbea2cc18 pc = 0x400c34e7 +# Found by: call frame info +# ... +# +# The most important part for us is ensuring that the source location could +# be extracted, so look at the 'test_breakpad.cpp' references here. +# +# First, extract all the lines with test_google_breakpad! in them, and +# dump the corresponding crash location. +# +# Note that if the source location can't be extracted, the second field +# will only be 'test_google_breakpad' without the exclamation mark. +# +LOCATIONS=$(awk '$2 ~ "^test_google_breakpad!.*" { print $3; }' "$BUILD_LOG") + +if [ -z "$LOCATIONS" ]; then + if [ "$VERBOSE" -lt 1 ]; then + cat "$BUILD_LOG" + fi + panic "No source location found in stack trace!" +fi + +# Now check that they all match "[" +BAD_LOCATIONS= +for LOCATION in $LOCATIONS; do + case $LOCATION in + # Escape the opening bracket, or some shells like Dash will not + # match them properly. + \[*.cpp|\[*.cc|\[*.h) # These are valid source locations in our executable + ;; + *) # Everything else is not! + BAD_LOCATIONS="$BAD_LOCATIONS $LOCATION" + ;; + esac +done + +if [ "$BAD_LOCATIONS" ]; then + dump "ERROR: Generated stack trace doesn't contain valid source locations:" + cat "$BUILD_LOG" + echo "Bad locations are: $BAD_LOCATIONS" + clean_exit 1 +fi + +echo "All clear! Congratulations." +clean_exit 0 diff --git a/android/sample_app/README b/android/sample_app/README new file mode 100644 index 00000000..aa19dbb4 --- /dev/null +++ b/android/sample_app/README @@ -0,0 +1,32 @@ +This is a sample Android executable that can be used to test the +Google Breakpad client library on Android. + +Its purpose is simply to crash and generate a minidump under /data/local/tmp. + +Build instructions: + + cd android/sample_app + $NDK/ndk-build + + Where $NDK points to a valid Android NDK installation. + +Usage instructions: + + After buildind the test program, send it to a device, then run it as + the shell UID: + + adb push libs/armeabi/test_google_breakpad /data/local/tmp + adb shell /data/local/tmp/test_google_breakpad + + This will simply crash after dumping the name of the generated minidump + file. + + See jni/test_breakpad.cpp for details. + + Use 'armeabi-v7a' instead of 'armeabi' above to test the ARMv7-A version + of the binary. + +Note: + If you plan to use the library in a regular Android application, store + the minidump files either to your app-specific directory, or to the SDCard + (the latter requiring a specific permission). diff --git a/android/sample_app/jni/Android.mk b/android/sample_app/jni/Android.mk new file mode 100644 index 00000000..61487b52 --- /dev/null +++ b/android/sample_app/jni/Android.mk @@ -0,0 +1,44 @@ +# Copyright (c) 2012, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := test_google_breakpad +LOCAL_SRC_FILES := test_breakpad.cpp +LOCAL_STATIC_LIBRARIES += breakpad_client +include $(BUILD_EXECUTABLE) + +# If NDK_MODULE_PATH is defined, import the module, otherwise do a direct +# includes. This allows us to build in all scenarios easily. +ifneq ($(NDK_MODULE_PATH),) + $(call import-module,google_breakpad) +else + include $(LOCAL_PATH)/../../google_breakpad/Android.mk +endif diff --git a/android/sample_app/jni/Application.mk b/android/sample_app/jni/Application.mk new file mode 100644 index 00000000..50dcd0d5 --- /dev/null +++ b/android/sample_app/jni/Application.mk @@ -0,0 +1,31 @@ +# Copyright (c) 2012, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +APP_STL := stlport_static +APP_ABI := armeabi armeabi-v7a diff --git a/android/sample_app/jni/test_breakpad.cpp b/android/sample_app/jni/test_breakpad.cpp new file mode 100644 index 00000000..6c18edfb --- /dev/null +++ b/android/sample_app/jni/test_breakpad.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "client/linux/handler/exception_handler.h" + +namespace { + +bool DumpCallback(const char* dump_path, + const char* minidump_id, + void* context, + bool succeeded) { + printf("Dump path: %s/%s.dmp\n", dump_path, minidump_id); + return succeeded; +} + +void Crash() { + volatile int* a = reinterpret_cast(NULL); + *a = 1; +} + +} // namespace + +int main(int argc, char* argv[]) { + google_breakpad::ExceptionHandler eh(".", NULL, DumpCallback, NULL, true); + Crash(); + return 0; +}