您的位置:首页 > 其它

JNI - Prevent JVM from crashing on Error Signals

2016-05-22 01:47 399 查看
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
4000
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: