diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java index e9a9c0fb..81ec919d 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java @@ -19,6 +19,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.GestureDetector; @@ -60,6 +61,7 @@ public class Apple2Activity extends Activity { private native void nativeGraphicsChanged(int width, int height); private native void nativeOnKeyDown(int keyCode, int metaState); private native void nativeOnKeyUp(int keyCode, int metaState); + private native void nativeOnUncaughtException(String home, String trace); public native void nativeOnResume(boolean isSystemResume); public native void nativeOnPause(); @@ -146,6 +148,49 @@ public class Apple2Activity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + // Immediately set up exception handler ... + final String homeDir = "/data/data/"+this.getPackageName(); + final Thread.UncaughtExceptionHandler defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); + Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread thread, Throwable t) { + try { + StackTraceElement[] stackTraceElements = t.getStackTrace(); + StringBuffer traceBuffer = new StringBuffer(); + + // prepend information about this device + traceBuffer.append(Build.BRAND); + traceBuffer.append("\n"); + traceBuffer.append(Build.MODEL); + traceBuffer.append("\n"); + traceBuffer.append(Build.MANUFACTURER); + traceBuffer.append("\n"); + traceBuffer.append(Build.DEVICE); + traceBuffer.append("\n"); + + // now append the actual stack trace + traceBuffer.append(t.getClass().getName()); + traceBuffer.append("\n"); + final int maxTraceSize = 2048+1024+512; // probably should keep this less than a standard Linux PAGE_SIZE + for (StackTraceElement elt : stackTraceElements) { + traceBuffer.append(elt.toString()); + traceBuffer.append("\n"); + if (traceBuffer.length() >= maxTraceSize) { + break; + } + } + traceBuffer.append("\n"); + + nativeOnUncaughtException(homeDir, traceBuffer.toString()); + } catch (Throwable terminator2) { + // Yo dawg, I hear you like exceptions in your exception handler! ... + } + + defaultExceptionHandler.uncaughtException(thread, t); + } + }); + Log.e(TAG, "onCreate()"); mDataDir = firstTimeInitialization(); diff --git a/Android/jni/jnihooks.c b/Android/jni/jnihooks.c index b671d48d..a49f400d 100644 --- a/Android/jni/jnihooks.c +++ b/Android/jni/jnihooks.c @@ -192,6 +192,42 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnQuit(JNIEnv *env, jobject } } +#define _JAVA_CRASH_NAME "/jcrash.txt" +#define _HALF_PAGE_SIZE (PAGE_SIZE>>1) + +void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnUncaughtException(JNIEnv *env, jobject obj, jstring jhome, jstring jstr) { + RELEASE_ERRLOG("Uncaught Java Exception ..."); + + // Write to /data/data/org.deadc0de.apple2ix.demo/jcrash.txt + const char *home = (*env)->GetStringUTFChars(env, jhome, NULL); + char *q = (char *)home; + char buf[_HALF_PAGE_SIZE] = { 0 }; + const char *p0 = &buf[0]; + char *p = (char *)p0; + while (*q && (p-p0 < _HALF_PAGE_SIZE-1)) { + *p++ = *q++; + } + (*env)->ReleaseStringUTFChars(env, jhome, home); + q = &_JAVA_CRASH_NAME[0]; + while (*q && (p-p0 < _HALF_PAGE_SIZE-1)) { + *p++ = *q++; + } + + int fd = TEMP_FAILURE_RETRY(open(buf, (O_CREAT|O_APPEND|O_WRONLY), (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))); + if (fd == -1) { + RELEASE_ERRLOG("OOPS, could not create/write to java crash file"); + return; + } + + const char *str = (*env)->GetStringUTFChars(env, jstr, NULL); + jsize len = (*env)->GetStringUTFLength(env, jstr); + TEMP_FAILURE_RETRY(write(fd, str, len)); + (*env)->ReleaseStringUTFChars(env, jstr, str); + + TEMP_FAILURE_RETRY(fsync(fd)); + TEMP_FAILURE_RETRY(close(fd)); +} + void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnKeyDown(JNIEnv *env, jobject obj, jint keyCode, jint metaState) { #if !TESTING android_keycode_to_emulator(keyCode, metaState, true); @@ -249,7 +285,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeSetColor(JNIEnv *env, jobje } void Java_org_deadc0de_apple2ix_Apple2Activity_nativeChooseDisk(JNIEnv *env, jobject obj, jstring jPath, jboolean driveA, jboolean readOnly) { - const char *path = (*env)->GetStringUTFChars(env, jPath, 0); + const char *path = (*env)->GetStringUTFChars(env, jPath, NULL); int drive = driveA ? 0 : 1; int ro = readOnly ? 1 : 0;