I'm attempting to force an exception in a simple C library I've written that is being called via Andriod NDK. The goal of the exception handling is to pass any signal raised in the C library back to the android program, so it in turn can raise an exception. This way the C exception will appear in the android logs and allow for easy debugging.
I've followed this very helpful post by Chris Boyle and got everything running, however I'm struggling to actually cause an exception. In fact the sleep() call doesn't appear to be invoked either. Can someone suggest where I'm going wrong?
How can I catch SIGSEGV (segmentation fault) and get a stack trace under JNI on Android?
Here's my C library code...
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "com_example_jni_test_app_MainActivity.h"
/* Header for class com_example_jni_test_app_MainActivity */
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include <unistd.>
static jmethodID nativeCrashed;
static JNIEnv *env;
static jobject obj;
/*
* Class: com_example_jni_test_app_MainActivity
* Method: getStringFromNative
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_jni_1test_app_MainActivity_getStringFromNative
(JNIEnv *_env, jobject _obj){
env = _env;
obj = _obj;
//TEST - Force segfault
sleep(20);
raise(SIGSEGV);
return (*env)->NewStringUTF(env, "Hello from JNI!");
}
static struct sigaction old_sa[NSIG];
void android_sigaction(int signal, siginfo_t *info, void *reserved)
{
(*env)->CallVoidMethod(env, obj, nativeCrashed);
old_sa[signal].sa_handler(signal);
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
{
jclass cls, vcls;
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2)) return JNI_ERR;
cls = (*env)->FindClass(env, "com/example/jni_test/app/MainActivity");
//vcls = (*env)->FindClass(env, "name/boyle/chris/sgtpuzzles/GameView");
nativeCrashed = (*env)->GetMethodID(env, cls, "nativeCrashed", "()V");
// Try to catch crashes...
struct sigaction handler;
memset(&handler, 0, sizeof(sigaction));
handler.sa_sigaction = android_sigaction;
handler.sa_flags = SA_RESETHAND;
#define CATCHSIG(X) sigaction(X, &handler, &old_sa[X])
CATCHSIG(SIGILL);
CATCHSIG(SIGABRT);
CATCHSIG(SIGBUS);
CATCHSIG(SIGFPE);
CATCHSIG(SIGSEGV);
//CATCHSIG(SIGSTKFLT);
CATCHSIG(SIGPIPE);
return JNI_VERSION_1_2;
}
And here's the android code that invokes the C library..
package com.example.jni_test.app;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
public class MainActivity extends Activity {
static {
System.loadLibrary("Mylib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.my_textview);
tv.setText(getStringFromNative());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public native String getStringFromNative();
void nativeCrashed()
{
/*if (prefs != null) {
try {
System.err.println("saved game was:\n"+prefs.getString("savedGame",""));
} catch(Exception e) {}
}*/
new RuntimeException("crashed here (native trace should follow after the Java trace)").printStackTrace();
//startActivity(new Intent(this, CrashHandler.class));
}
}