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:
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
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
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
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
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 seriouslybroken 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
longjmpto 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
raiseor
killinstead 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
相关文章推荐
- Windows 下 Python & PyCharm & Scrapy的安装和配置
- pl/sql 分页存储过程
- [leetcode] 【数组】4. Median of Two Sorted Arrays
- FireMonkey下的异形窗体拖动(句柄转换)
- Discuz 迁移后数据接口配置
- 2016/5/22
- MIME类型、Unicode、UTF-8的概念
- java面试
- 一个屌丝程序猿的人生(十四)
- [Design Pattern] DAO Pattern 简单案例
- c++学习笔记(7)——关于类的实现(以vector为例)
- iOS包大小分析和减包大小
- 51单片机开发流程
- HDU 1073 - Online Judge
- 安全卫士11————设备管理器,锁屏、初始化手机
- 【GOF23设计模式】_原型模式JAVA233-234
- 安全卫士10————获取定位信息
- Codeforces Round #353 (Div. 2) Editorial
- Android 笔记—2
- 安全卫士9————接收短信,拦截短信,发送报警音乐