mirror of
https://github.com/mauiaaron/apple2.git
synced 2024-06-26 00:29:27 +00:00
process crashes in-situ on Android device
This commit is contained in:
parent
88be6086a2
commit
d90b523440
|
@ -101,8 +101,6 @@ public class Apple2Activity extends Activity {
|
|||
|
||||
public native void nativeEjectDisk(boolean driveA);
|
||||
|
||||
public native void nativePerformCrash(int crashType);
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -405,6 +403,7 @@ public class Apple2Activity extends Activity {
|
|||
synchronized (Apple2Activity.this) {
|
||||
if (mSplashScreen != null) {
|
||||
mSplashScreen.show();
|
||||
Apple2CrashHandler.getInstance().checkForCrashes(Apple2Activity.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,6 +135,129 @@ public class Apple2CrashHandler {
|
|||
return areJavaCrashesPresent(activity) || areNativeCrashesPresent(activity);
|
||||
}
|
||||
|
||||
public void checkForCrashes(final Apple2Activity activity) {
|
||||
if (!areCrashesPresent(activity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean previouslyRanCrashCheck = mAlreadyRanCrashCheck.getAndSet(true);
|
||||
|
||||
boolean previouslySentReport = mAlreadySentReport.get();
|
||||
if (previouslySentReport) {
|
||||
|
||||
// here we assume that the crash data was previously sent via email ... if not then we lost it =P
|
||||
|
||||
Log.d(TAG, "Cleaning up crash data ...");
|
||||
int idx = 0;
|
||||
File[] nativeCrashes = _nativeCrashFiles(activity);
|
||||
for (File crash : nativeCrashes) {
|
||||
|
||||
if (!crash.delete()) {
|
||||
Log.d(TAG, "Could not unlink crash : " + crash);
|
||||
}
|
||||
|
||||
File processed = new File(_dumpPath2ProcessedPath(crash.getAbsolutePath()));
|
||||
if (!processed.delete()) {
|
||||
Log.d(TAG, "Could not unlink processed : " + processed);
|
||||
}
|
||||
}
|
||||
|
||||
File javaCrashFile = _javaCrashFile(activity);
|
||||
if (!javaCrashFile.delete()) {
|
||||
Log.d(TAG, "Could not unlink java crash : " + javaCrashFile);
|
||||
}
|
||||
|
||||
// remove previous log file
|
||||
_writeTempLogFile(new StringBuilder());
|
||||
return;
|
||||
}
|
||||
|
||||
if (previouslyRanCrashCheck) {
|
||||
// don't keep asking on return from backgrounding
|
||||
return;
|
||||
}
|
||||
|
||||
final AlertDialog crashDialog = new AlertDialog.Builder(activity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.crasher_send).setMessage(R.string.crasher_send_message).setNegativeButton(R.string.no, null).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
|
||||
// assuming that the actual native processing works quickly ...
|
||||
|
||||
final Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
final int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(activity);
|
||||
final int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(activity, /*isStereo:*/false);
|
||||
final int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(activity, /*isStereo:*/true);
|
||||
|
||||
StringBuilder allCrashData = new StringBuilder();
|
||||
|
||||
// prepend information about this device
|
||||
allCrashData.append(Build.BRAND);
|
||||
allCrashData.append("\n");
|
||||
allCrashData.append(Build.MODEL);
|
||||
allCrashData.append("\n");
|
||||
allCrashData.append(Build.MANUFACTURER);
|
||||
allCrashData.append("\n");
|
||||
allCrashData.append(Build.DEVICE);
|
||||
allCrashData.append("\n");
|
||||
allCrashData.append("Device sample rate:");
|
||||
allCrashData.append(sampleRate);
|
||||
allCrashData.append("\n");
|
||||
allCrashData.append("Device mono buffer size:");
|
||||
allCrashData.append(monoBufferSize);
|
||||
allCrashData.append("\n");
|
||||
allCrashData.append("Device stereo buffer size:");
|
||||
allCrashData.append(stereoBufferSize);
|
||||
allCrashData.append("\n");
|
||||
|
||||
File[] nativeCrashes = _nativeCrashFiles(activity);
|
||||
if (nativeCrashes == null) {
|
||||
nativeCrashes = new File[0];
|
||||
}
|
||||
|
||||
// iteratively process native crashes
|
||||
int idx = 0;
|
||||
for (File crash : nativeCrashes) {
|
||||
|
||||
String crashPath = crash.getAbsolutePath();
|
||||
Log.d(TAG, "Processing crash : " + crashPath);
|
||||
|
||||
String processedPath = _dumpPath2ProcessedPath(crashPath);
|
||||
nativeProcessCrash(crashPath, processedPath); // Run Breakpad minidump_stackwalk
|
||||
|
||||
StringBuilder crashData = new StringBuilder();
|
||||
if (!_readFile(new File(processedPath), crashData)) {
|
||||
Log.e(TAG, "Error processing crash : " + crashPath);
|
||||
}
|
||||
allCrashData.append(">>>>>>> NATIVE CRASH [").append(crashPath).append("]\n");
|
||||
allCrashData.append(crashData);
|
||||
}
|
||||
|
||||
StringBuilder javaCrashData = new StringBuilder();
|
||||
File javaCrashFile = _javaCrashFile(activity);
|
||||
if (javaCrashFile.exists()) {
|
||||
Log.d(TAG, "Reading java crashes file");
|
||||
if (!_readFile(javaCrashFile, javaCrashData)) {
|
||||
Log.e(TAG, "Error processing java crash : " + javaCrashFileName);
|
||||
}
|
||||
}
|
||||
|
||||
allCrashData.append(">>>>>>> JAVA CRASH DATA\n");
|
||||
allCrashData.append(javaCrashData);
|
||||
|
||||
// send report with all the data
|
||||
_sendEmailToDeveloperWithCrashData(activity, allCrashData);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}).create();
|
||||
activity.registerAndShowDialog(crashDialog);
|
||||
}
|
||||
|
||||
public void performCrash(int crashType) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
nativePerformCrash(crashType);
|
||||
|
@ -281,4 +404,6 @@ public class Apple2CrashHandler {
|
|||
|
||||
private static native void nativeOnUncaughtException(String home, String trace);
|
||||
|
||||
private static native void nativeProcessCrash(String crashFilePath, String crashProcessedPath);
|
||||
|
||||
}
|
||||
|
|
|
@ -28,13 +28,18 @@ LOCAL_SRC_FILES := $(APPLE2_SRC_PATH)/breakpad.C jnicrash.c
|
|||
LOCAL_CFLAGS := $(APPLE2_BASE_CFLAGS) $(BREAKPAD_CFLAGS)
|
||||
LOCAL_LDLIBS := $(APPLE2_BASE_LDLIBS)
|
||||
|
||||
# Add assembly files first ... mostly for the benefit of the ARM assembler ...
|
||||
ifeq ($(TARGET_ARCH_ABI),x86)
|
||||
LOCAL_SRC_FILES += $(APPLE2_X86_SRC)
|
||||
else
|
||||
LOCAL_SRC_FILES += $(APPLE2_ARM_SRC)
|
||||
endif
|
||||
|
||||
ifeq ($(EMBEDDED_STACKWALKER),1)
|
||||
LOCAL_CPPFLAGS += -DEMBEDDED_STACKWALKER=1
|
||||
else
|
||||
$(error OOPS, for now you should build with EMBEDDED_STACKWALKER=1)
|
||||
endif
|
||||
|
||||
LOCAL_SRC_FILES += $(APPLE2_MAIN_SRC) $(APPLE2_META_SRC) $(APPLE2_VIDEO_SRC) $(APPLE2_AUDIO_SRC)
|
||||
|
||||
# Build a shared library and let Java/Dalvik drive
|
||||
|
|
|
@ -104,11 +104,12 @@ else
|
|||
ln -s apple2ix.mk Android.mk
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# build native sources
|
||||
if test "x$do_release" = "x1" ; then
|
||||
ndk-build V=1 NDK_MODULE_PATH=. # NDK_TOOLCHAIN_VERSION=clang
|
||||
ndk-build V=1 NDK_MODULE_PATH=. EMBEDDED_STACKWALKER=1 # NDK_TOOLCHAIN_VERSION=clang
|
||||
else
|
||||
ndk-build V=1 NDK_MODULE_PATH=. NDK_DEBUG=1 # NDK_TOOLCHAIN_VERSION=clang
|
||||
ndk-build V=1 NDK_MODULE_PATH=. NDK_DEBUG=1 EMBEDDED_STACKWALKER=1 # NDK_TOOLCHAIN_VERSION=clang
|
||||
fi
|
||||
ret=$?
|
||||
if test "x$ret" != "x0" ; then
|
||||
|
|
|
@ -22,6 +22,19 @@ enum {
|
|||
|
||||
#include <jni.h>
|
||||
|
||||
// cribbed from AOSP and modified with usleep() and to also ignore EAGAIN (should this be a different errno than EINTR)
|
||||
#define TEMP_FAILURE_RETRY_FOPEN(exp) ({ \
|
||||
typeof (exp) _rc; \
|
||||
do { \
|
||||
_rc = (exp); \
|
||||
if (_rc == NULL && (errno == EINTR || errno == EAGAIN) ) { \
|
||||
usleep(10); \
|
||||
} else { \
|
||||
break; \
|
||||
} \
|
||||
} while (1); \
|
||||
_rc; })
|
||||
|
||||
static volatile int __attribute__((noinline)) _crash_null_deref(void) {
|
||||
static volatile uintptr_t *ptr = NULL;
|
||||
while ((ptr+1)) {
|
||||
|
@ -128,3 +141,47 @@ void Java_org_deadc0de_apple2ix_Apple2CrashHandler_nativeOnUncaughtException(JNI
|
|||
TEMP_FAILURE_RETRY(close(fd));
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2CrashHandler_nativeProcessCrash(JNIEnv *env, jclass cls, jstring jCrashPath, jstring jOutputPath) {
|
||||
if (!(crashHandler && crashHandler->processCrash)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("...");
|
||||
|
||||
const char *crashPath = (*env)->GetStringUTFChars(env, jCrashPath, NULL);
|
||||
const char *outputPath = (*env)->GetStringUTFChars(env, jOutputPath, NULL);
|
||||
FILE *outputFILE = NULL;
|
||||
char *symbolsPath = NULL;
|
||||
|
||||
do {
|
||||
outputFILE = TEMP_FAILURE_RETRY_FOPEN(fopen(outputPath, "w"));
|
||||
if (!outputFILE) {
|
||||
ERRLOG("could not open %s", outputPath);
|
||||
break;
|
||||
}
|
||||
|
||||
if (android_armArchV7A) {
|
||||
asprintf(&symbolsPath, "%s/symbols/armeabi-v7a", data_dir);
|
||||
} else /*if (android_armArch)*/ {
|
||||
asprintf(&symbolsPath, "%s/symbols/armeabi", data_dir);
|
||||
} /*else { moar archs ... } */
|
||||
|
||||
bool success = crashHandler->processCrash(crashPath, symbolsPath, outputFILE);
|
||||
if (!success) {
|
||||
RELEASE_LOG("CRASH REPORT PROCESSING FAILED ...");
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (outputFILE) {
|
||||
TEMP_FAILURE_RETRY(fflush(outputFILE));
|
||||
TEMP_FAILURE_RETRY(fclose(outputFILE));
|
||||
}
|
||||
|
||||
if (symbolsPath) {
|
||||
FREE(symbolsPath);
|
||||
}
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, jCrashPath, crashPath);
|
||||
(*env)->ReleaseStringUTFChars(env, jOutputPath, outputPath);
|
||||
}
|
||||
|
||||
|
|
|
@ -103,8 +103,8 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobje
|
|||
}
|
||||
|
||||
data_dir = strdup(dataDir);
|
||||
if (initializeCrashHandler) {
|
||||
initializeCrashHandler(data_dir);
|
||||
if (crashHandler && crashHandler->init) {
|
||||
crashHandler->init(data_dir);
|
||||
}
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, j_dataDir, dataDir);
|
||||
|
@ -249,10 +249,13 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnQuit(JNIEnv *env, jobject
|
|||
cpu_resume();
|
||||
|
||||
emulator_shutdown();
|
||||
|
||||
if (crashHandler && crashHandler->shutdown) {
|
||||
crashHandler->shutdown();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnKeyDown(JNIEnv *env, jobject obj, jint keyCode, jint metaState) {
|
||||
if (UNLIKELY(shuttingDown)) {
|
||||
return;
|
||||
|
|
|
@ -17,26 +17,52 @@
|
|||
|
||||
#include "common.h"
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#if EMBEDDED_STACKWALKER
|
||||
# include "processor/stackwalk_common.h"
|
||||
#endif
|
||||
|
||||
static CrashHandler_s breakpadHandler = { 0 };
|
||||
|
||||
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());
|
||||
LOG("Dump path: %s\n", descriptor.path());
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
static void initializeBreakpadHandler(const char *dumpDir) {
|
||||
static void initBreakpadHandler(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);
|
||||
}
|
||||
|
||||
static void shutdownBreakpadHandler(void) {
|
||||
delete eh;
|
||||
eh = nullptr;
|
||||
}
|
||||
|
||||
static bool processCrashWithBreakpad(const char *crash, const char *symbolsPath, const FILE *outputFile) {
|
||||
#if EMBEDDED_STACKWALKER
|
||||
LOG("Running breakpad stackwalker on crash ...");
|
||||
stackwalker_setOutputFile(outputFile);
|
||||
int err = stackwalker_main(crash, symbolsPath, /*machineReadable:*/true);
|
||||
stackwalker_setOutputFile(NULL);
|
||||
return err == 0;
|
||||
#else
|
||||
#warning NOT COMPILING WITH EMBEDDED STACKWALKER
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
__attribute__((constructor(CTOR_PRIORITY_EARLY)))
|
||||
static void _breakpad_registration(void) {
|
||||
LOG("Registering Breakpad as handler");
|
||||
initializeCrashHandler = &initializeBreakpadHandler;
|
||||
LOG("Registering Breakpad as native crash handler");
|
||||
breakpadHandler.init = &initBreakpadHandler;
|
||||
breakpadHandler.shutdown = &shutdownBreakpadHandler;
|
||||
breakpadHandler.processCrash = &processCrashWithBreakpad;
|
||||
crashHandler = &breakpadHandler;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +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;
|
||||
CrashHandler_s *crashHandler = NULL;
|
||||
|
||||
__attribute__((constructor(CTOR_PRIORITY_FIRST)))
|
||||
static void _init_common() {
|
||||
|
|
27
src/misc.h
27
src/misc.h
|
@ -30,7 +30,30 @@ 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);
|
||||
//
|
||||
// Crash handling ...
|
||||
//
|
||||
|
||||
typedef struct CrashHandler_s {
|
||||
|
||||
/**
|
||||
* Initialize crash handler (if available)
|
||||
*/
|
||||
void (*init)(const char *dumpDir);
|
||||
|
||||
/**
|
||||
* Shutdown crash handler (if available)
|
||||
*/
|
||||
void (*shutdown)(void);
|
||||
|
||||
/**
|
||||
* Processes a crash dump (assuming this is a non-crashing contest).
|
||||
* Returns success value. On failure, the outputFile may contain the reason processing failed
|
||||
*/
|
||||
bool (*processCrash)(const char *crash, const char *symbolsPath, const FILE *outputFile);
|
||||
|
||||
} CrashHandler_s;
|
||||
|
||||
extern CrashHandler_s *crashHandler;
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue
Block a user