JNI - Prevent JVM from crashing on Error Signals

In the previous article, Divide by Zero. A comparison between Java and C, we concluded that C++ programs will crash when it meets a DivideByZero Exception. Even with a handler, it will crash anyway.

See this example:

#include <iostream>
#include <stdlib.h>
#include <csignal>
using namespace std;

//We can do something in the handler.
static void handler(int signum){
	cout <<"Signal  "<<signum <<" here!"<<endl;
	//change SIGFPE to the Default Value	
	signal(SIGFPE, SIG_IGN);
}

int main(){
	signal(SIGFPE,handler);

	int d = 5;
	d = d/(d-5);
	
	cout <<"I want this line to be printed."<<endl;
	return 0;
}

Result:

wsh@wsh-VirtualBox:~/JNI/JNI_CPP/OnlyCpp$ ./SystemHandler 
Signal  8 here!
Floating point exception (core dumped)

Even with the handler, process still exited with a coredump. And the statement "I want this line to be printed" will never be printed.

After checking the GNU manual, we can explain this phenomenon:

24.2.1 Program Error Signals

The following signals are generated when a serious program error is detected by the operating system or the computer itself. In general, all of these signals are indications that your program is seriously broken in some way, and there’s usually no way to continue the computation which encountered the error.

Some programs handle program error signals in order to tidy up before terminating; for example, programs that turn off echoing of terminal input should handle program error signals in order to turn echoing back on. The handler should end by specifying the default action for the signal that happened and then reraising it; this will cause the program to terminate with that signal, as if it had not had a handler. (See Termination in Handler.)

Termination is the sensible ultimate outcome from a program error in most programs. However, programming systems such as Lisp that can load compiled user programs might need to keep executing even if a user program incurs an error. These programs have handlers which use longjmp to return control to the command level.

The default action for all of these signals is to cause the process to terminate. If you block or ignore these signals or establish handlers for them that return normally, your program will probably break horribly when such signals happen, unless they are generated by raise or kill instead of a real error.

When one of these program error signals terminates a process, it also writes a core dump file which records the state of the process at the time of termination. The core dump file is named coreand is written in whichever directory is current in the process at the time. (On GNU/Hurd systems, you can specify the file name for core dumps with the environment variable COREFILE.) The purpose of core dump files is so that you can examine them with a debugger to investigate what caused the error.

http://www.gnu.org/software/libc/manual/html_node/Program-Error-Signals.html#Program-Error-Signals


In order to prevent the process from terminating, we can use longjmp in handler, which avoids the following core dump and crash statements after invoking handler. The usage of longjmp is well illustrated in the following program.


#include <iostream>
#include <stdlib.h>
#include <csignal>
#include <setjmp.h>
using namespace std;

jmp_buf return_to_top_level;

//We can do something in the handler.
static void handler(int signum){
	cout <<"Signal  "<<signum <<" here!"<<endl;
	//change SIGFPE to the Default Value	
	signal(SIGFPE, SIG_IGN);
	longjmp (return_to_top_level, 1);
}

int main(){
	signal(SIGFPE,handler);
	
	if (setjmp (return_to_top_level) == 0){
		int d = 5;
		d = d/(d-5);
	}

	cout <<"I want this line to be printed."<<endl;
	return 0;
}

 And the result is:

wsh@wsh-VirtualBox:~/JNI/JNI_CPP/OnlyCpp$ ./SystemHandler Signal  8 here!
I want this line to be printed.

The method is useful because programmers usually complain that they cannot collect enough information after native library crashes. The whole program/Application simply exit, therefore they don't know how to debug it.

If you use longjmp, you can avoid crash, and throw an exception like java. Let's see how to use it in JNI:

C file:

#include <jni.h>
#include <iostream>
#include "SystemHandler.h"
#include <iostream>
#include <csignal>
#include <stdlib.h>
#include <setjmp.h>

using namespace std;

jmp_buf return_to_top_level;

JNIEnv* env = NULL;
//We can do something in the handler.
void handler(int a){
	signal(SIGFPE, SIG_DFL);
	cout <<"Signal  "<<a <<" here!"<<endl;
	
	//throw an exception
	jclass newExcCls;
	newExcCls = env->FindClass("java/lang/IllegalArgumentException");
	if (newExcCls == NULL){
		cout <<"FindClass(\"java/lang/IllegalArgumentException\") failed!"<<endl;
		return;
	}
	env->ThrowNew( newExcCls, "thrown from C code");
	
	//change SIGFPE to the Default Value	

	signal(SIGFPE,handler);
	longjmp (return_to_top_level, 1);
}

JNIEXPORT void JNICALL Java_SystemHandler_print
  (JNIEnv *_env, jobject obj)
{
	env = _env;

	signal(SIGFPE,handler);

	if (setjmp (return_to_top_level) == 0){
		int d = 5;
		d = d/(d-5);
	}

	return;
}
Java file:

class SystemHandler{
	private native void print();
	public static void main(String[] args){
		SystemHandler sh = new SystemHandler();
		try{
			sh.print();	
		}
		catch (Exception e){
			System.out.println("In Java:\n\t" + e);	
		}
	}
	static{
		System.loadLibrary("SystemHandler");	
	}
}

As a result, the crash in C is caught and an exception is thrown to Java.

Result:

wsh@wsh-VirtualBox:~/JNI/JNI_CPP/SystemHandler$ java SystemHandler 
Signal  8 here!
In Java:
	java.lang.IllegalArgumentException: thrown from C code





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值