Integrate Breakpad crash dumper and tools into Android build

- Includes Linux x86_64 binaries dump_syms and minidump_stackwalk
    - Includes adb_sanitize.c tool (gcc -std=gnu11 -o adb_sanitize adb_sanitize.c) for easily pulling local minidumps
This commit is contained in:
Aaron Culliney 2015-09-19 11:47:36 -07:00
parent a2d62a77b6
commit b8921b8441
15 changed files with 187 additions and 14 deletions

View File

@ -1,2 +1,3 @@
APP_ABI := armeabi armeabi-v7a ## TODO : x86
APP_PLATFORM := android-23
APP_STL := gnustl_static

View File

@ -7,13 +7,25 @@ PACKAGE_NAME := "apple2ix"
COMMON_SOURCES_MK := $(LOCAL_PATH)/sources.mk
include $(COMMON_SOURCES_MK)
# -----------------------------------------------------------------------------
# Breakpad crash reporter ...
LOCAL_STATIC_LIBRARIES := breakpad_client
# hmmm, Breakpad's README.ANDROID seems to suggest you shouldn't need to do this kludgery ...
BREAKPAD_SRC_PATH := $(APPLE2_SRC_PATH)/../externals/breakpad/src
BREAKPAD_CFLAGS := -I$(BREAKPAD_SRC_PATH) -I$(APPLE2_SRC_PATH)/common/android/include
# -----------------------------------------------------------------------------
# Android build config
LOCAL_CPP_EXTENSION := .C
LOCAL_CPPFLAGS := -std=gnu++11
LOCAL_MODULE := libapple2ix
LOCAL_SRC_FILES :=
LOCAL_SRC_FILES := $(APPLE2_SRC_PATH)/breakpad.C
#LOCAL_ARM_MODE := arm
LOCAL_CFLAGS := $(APPLE2_BASE_CFLAGS)
LOCAL_CFLAGS := $(APPLE2_BASE_CFLAGS) $(BREAKPAD_CFLAGS)
LOCAL_LDLIBS := $(APPLE2_BASE_LDLIBS)
# Add assembly files first ... mostly for the benefit of the ARM assembler ...
@ -31,4 +43,5 @@ include $(BUILD_SHARED_LIBRARY)
# --OR-- Build an executable so native can drive this show
#include $(BUILD_EXECUTABLE)
$(call import-module, breakpad/android/google_breakpad)
$(call import-module, android/cpufeatures)

1
Android/jni/breakpad Symbolic link
View File

@ -0,0 +1 @@
../../externals/breakpad

1
Android/jni/breakpad.mk Symbolic link
View File

@ -0,0 +1 @@
breakpad/android/google_breakpad/Android.mk

View File

@ -106,9 +106,9 @@ fi
# build native sources
if test "x$do_release" = "x1" ; then
ndk-build V=1 # NDK_TOOLCHAIN_VERSION=clang
ndk-build V=1 NDK_MODULE_PATH=. # NDK_TOOLCHAIN_VERSION=clang
else
ndk-build V=1 NDK_DEBUG=1 # NDK_TOOLCHAIN_VERSION=clang
ndk-build V=1 NDK_MODULE_PATH=. NDK_DEBUG=1 # NDK_TOOLCHAIN_VERSION=clang
fi
ret=$?
if test "x$ret" != "x0" ; then

View File

@ -94,6 +94,14 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobje
return;
}
data_dir = strdup(dataDir);
if (initializeCrashHandler) {
initializeCrashHandler(data_dir);
}
(*env)->ReleaseStringUTFChars(env, j_dataDir, dataDir);
LOG("data_dir : %s", data_dir);
AndroidCpuFamily family = android_getCpuFamily();
uint64_t features = android_getCpuFeatures();
if (family == ANDROID_CPU_FAMILY_X86) {
@ -123,10 +131,6 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobje
}
}
data_dir = strdup(dataDir);
(*env)->ReleaseStringUTFChars(env, j_dataDir, dataDir);
LOG("data_dir : %s", data_dir);
android_deviceSampleRateHz = (unsigned long)sampleRate;
android_monoBufferSubmitSizeSamples = (unsigned long)monoBufferSize;
android_stereoBufferSubmitSizeSamples = (unsigned long)stereoBufferSize;
@ -142,7 +146,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobje
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsChanged(JNIEnv *env, jobject obj, jint width, jint height) {
LOG("%s", "");
LOG("...");
video_backend->reshape(width, height);
}
@ -158,7 +162,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnResume(JNIEnv *env, jobje
if (!cpu_isPaused()) {
return;
}
LOG("%s", "");
LOG("...");
if (!isSystemResume) {
#if TESTING
// test driver thread is managing CPU
@ -175,7 +179,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnPause(JNIEnv *env, jobjec
if (cpu_isPaused()) {
return;
}
LOG("%s", "");
LOG("...");
#if TESTING
// test driver thread is managing CPU
@ -210,7 +214,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeRender(JNIEnv *env, jobject
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeReboot(JNIEnv *env, jobject obj) {
LOG("%s", "");
LOG("...");
cpu65_reboot();
}
@ -220,7 +224,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnQuit(JNIEnv *env, jobject
#else
shuttingDown = true;
LOG("%s", "");
LOG("...");
c_eject_6(0);
c_eject_6(1);
@ -327,7 +331,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeChooseDisk(JNIEnv *env, job
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEjectDisk(JNIEnv *env, jobject obj, jboolean driveA) {
LOG("%s", "");
LOG("...");
c_eject_6(!driveA);
}

View File

View File

View File

View File

@ -0,0 +1,107 @@
//
// Sigh ... adb shell changes LF to CRLF in binary streams ...
//
// http://stackoverflow.com/questions/13578416/read-binary-stdout-data-from-adb-shell
//
// Problem:
// ( adb shell run-as com.example.someapp dd if=/data/data/com.example.someapp/some_binfile.bin 2\>/dev/null ) > local_binfile.bin
//
// * Without piping through adb_sanitize you would get a corrupted binary
//
// Fix:
// ( adb shell run-as com.example.someapp dd if=/data/data/com.example.someapp/some_binfile.bin 2\>/dev/null ) | adb_sanitize > local_binfile.bin
//
// Addenda:
// * The only other way to pull stuff from /data/data/... appears to be to use the heavyweight 'adb backup', yuck
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/errno.h>
// cribbed from AOSP and modified with usleep() and to also ignore EAGAIN
#undef TEMP_FAILURE_RETRY
#define TEMP_FAILURE_RETRY(exp) ({ \
typeof (exp) _rc; \
do { \
_rc = (exp); \
if (_rc == -1 && (errno == EINTR || errno == EAGAIN) ) { \
usleep(10); \
} else { \
break; \
} \
} while (1); \
_rc; })
static int _convert_crlf_to_lf(void) {
const char CR = 0x0d;
const char LF = 0x0a;
ssize_t inlen = 0;
char inbuf[BUFSIZ];
char outbuf[BUFSIZ];
bool sawCR = false;
char *errRd = NULL;
char *errWrt = NULL;
while ( (inlen = TEMP_FAILURE_RETRY(read(STDIN_FILENO, inbuf, BUFSIZ))) ) {
if (inlen == -1) {
errRd = "error reading from stdin";
break;
}
// convert CRLF -> LF
ssize_t outlen=0;
for (ssize_t i=0; i<inlen; i++) {
char c = inbuf[i];
if (sawCR && (c != LF)) {
outbuf[outlen++] = CR;
}
sawCR = false;
if (c == CR) {
sawCR = true;
} else {
outbuf[outlen++] = c;
}
}
if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, outbuf, outlen)) == -1) {
errWrt = "error writing to stdout";
break;
}
}
if (sawCR) {
if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, &CR, 1)) == -1) {
errWrt = "err writing to stdout";
}
}
if (errRd) {
fprintf(stderr, "%s : %s", errRd, strerror(errno));
return 1;
}
if (errWrt) {
fprintf(stderr, "%s : %s", errWrt, strerror(errno));
return 2;
}
TEMP_FAILURE_RETRY(fsync(STDOUT_FILENO));
return 0;
}
int main(int argc, char **argv, char **envp) {
#if UNIT_TEST
#error TODO FIXME ... should test edge cases CRLF split between buffered reads, and file/stream terminating 0D ([...0D][0A...] , [...0D])
_run_unit_tests();
#else
return _convert_crlf_to_lf();
#endif
}

BIN
externals/bin/dump_syms vendored Executable file

Binary file not shown.

BIN
externals/bin/minidump_stackwalk vendored Executable file

Binary file not shown.

42
src/breakpad.C Normal file
View File

@ -0,0 +1,42 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
// Breakpad crash handling
#if !defined(__cplusplus)
#error Breakpad requires C++
#endif
#include "common.h"
#include "client/linux/handler/exception_handler.h"
static google_breakpad::ExceptionHandler *eh = nullptr;
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) {
// WARNING : should only do minimal work from within a crashing context ...
//LOG("Dump path: %s\n", descriptor.path());
return succeeded;
}
extern "C" {
static void initializeBreakpadHandler(const char *dumpDir) {
LOG("Initializing Breakpad with dump dir : %s", dumpDir);
google_breakpad::MinidumpDescriptor descriptor(dumpDir);
eh = new google_breakpad::ExceptionHandler(descriptor, NULL, dumpCallback, NULL, true, -1);
}
__attribute__((constructor(CTOR_PRIORITY_EARLY)))
static void _breakpad_registration(void) {
LOG("Registering Breakpad as handler");
initializeCrashHandler = &initializeBreakpadHandler;
}
}

View File

@ -23,6 +23,7 @@ color_mode_t color_mode = COLOR;
const char *data_dir = NULL;
char **argv = NULL;
int argc = 0;
void (*initializeCrashHandler)(const char *dumpDir) = NULL;
__attribute__((constructor(CTOR_PRIORITY_FIRST)))
static void _init_common() {

View File

@ -30,4 +30,7 @@ void emulator_start(void);
// shutdown emulator in preparation for app exit
void emulator_shutdown(void);
// crash handler initialization (if available)
extern void (*initializeCrashHandler)(const char *dumpDir);
#endif