我正在使用本机代码构建 Android 应用程序。我想处理SIGSEGV、SIGFPE、SIGILL等异常
我的目标是,在其中一种不太可能发生的情况下,我不会终止应用程序。
基本上,我遵循从here得到的方法:
// MainApplication.kt
package com.example.exceptionhandling
import android.app.Application
import android.content.Context
import android.util.Log
class MainApplication : Application () {
override fun onCreate()
{
Log.d ("MyApp", "MainApplication.onCreate")
super.onCreate ()
System.loadLibrary ("exceptionhandling")
}
companion object {
public var sActivityCtx : Context? = null
}
}
// MainActivity.kt
package com.example.exceptionhandling
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.example.exceptionhandling.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override
fun onCreate (pSavedInstanceState: Bundle?)
{
Log.d ("MyApp", "MainActivity.onCreate")
super.onCreate (pSavedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
MainApplication.sActivityCtx = this
NativeMethod ()
}
override
fun onStop()
{
super.onStop()
Log.d ("MyApp", "MainActivity.onStop")
}
external fun NativeMethod () : Unit
}
// ExceptionActivity.kt
package com.example.exceptionhandling
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.example.exceptionhandling.databinding.ActivityMainBinding
class ExceptionActivity : AppCompatActivity () {
companion object {
// Invoked from C++
@JvmStatic
public fun StartExceptionActivity ()
{
Log.e ("MyApp", "Inside ExceptionActivity.StartExceptionActivity")
MainApplication.sActivityCtx!!.startActivity (Intent (MainApplication.sActivityCtx, ExceptionActivity::class.java))
}
}
public override
fun onCreate (pSavedInstanceState: Bundle?)
{
Log.d ("MyApp", "Inside ExceptionActivity.onCreate")
super.onCreate (pSavedInstanceState)
val linear_layout : LinearLayout
val textview : TextView?
linear_layout = LinearLayout (this)
textview = TextView (this)
textview.text = "Something has gone wrong!!"
linear_layout.addView (textview)
setContentView (linear_layout)
}
}
// native-lib.cpp
#include <jni.h>
#include <string>
#include <android/log.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#define TRACE_LOG(x, ...) __android_log_print (ANDROID_LOG_DEBUG, "MyApp", x, ##__VA_ARGS__)
#define TRACEFUNC TRACE_LOG ("Inside %s", __PRETTY_FUNCTION__)
#define TRACELINE TRACE_LOG ("At line: %d", __LINE__)
JavaVM* gJavaVM;
jclass gExceptionActivityClass; // To store the class pointer of Exception Activity. Will be needed if its methods are to be invoked
thread_local JNIEnv * gThreadLocalEnv;
thread_local bool gIsWorkerThread {}; // Whether the thread is worker thread or main thread
using SignalHandlerFunc = void (*) (int, siginfo_t*, void *);
struct tSignals {
int uSignal;
SignalHandlerFunc uSignalHandlerFunc;
SignalHandlerFunc uDefaultSignalHandlerFunc;
};
void
ExceptionHandler (int pNum, siginfo_t *pInfo, void *pCtx) noexcept;
tSignals gSignals [] =
{
{SIGILL, ExceptionHandler, nullptr},
{SIGSEGV, ExceptionHandler, nullptr},
{SIGFPE, ExceptionHandler, nullptr},
};
void
ShouldNotGetCalled (int, siginfo_t*, void *) noexcept
{
TRACEFUNC;
}
SignalHandlerFunc
GetDefaultSignalHandlerFunc (int pSignal) noexcept
{
TRACEFUNC;
SignalHandlerFunc func = nullptr;
for (auto signal : gSignals) {
if (pSignal == signal.uSignal) {
func = signal.uDefaultSignalHandlerFunc;
break;
}
}
if (func)
return func;
return ShouldNotGetCalled;
}
void
StartExceptionActivity () noexcept
{
TRACEFUNC;
jmethodID method_id;
method_id = gThreadLocalEnv->GetStaticMethodID (gExceptionActivityClass, "StartExceptionActivity", "()V");
if (method_id == NULL) {
TRACELINE;
return;
}
gThreadLocalEnv->CallStaticVoidMethod (gExceptionActivityClass, method_id);
TRACELINE;
}
void
AttachThread () noexcept
{
TRACEFUNC;
gJavaVM->GetEnv ((void **) &gThreadLocalEnv, JNI_VERSION_1_6);
gJavaVM->AttachCurrentThread (&gThreadLocalEnv, nullptr);
}
void
DetachThread () noexcept
{
TRACEFUNC;
gJavaVM->DetachCurrentThread ();
}
[[noreturn]]
void
KillThread () noexcept
{
TRACEFUNC;
DetachThread ();
pthread_exit (nullptr);
}
void
ExceptionHandler (int pNum, siginfo_t* pInfo, void * pCtx) noexcept
{
TRACEFUNC;
TRACE_LOG ("Exception number = %d", pNum);
StartExceptionActivity();
GetDefaultSignalHandlerFunc (pNum) (pNum, pInfo, pCtx);
if (gIsWorkerThread)
KillThread ();
}
void
SetExceptionHandlers () noexcept
{
TRACEFUNC;
struct sigaction sa {};
struct sigaction sa_out {};
// SIGACTION Register
sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
for (auto & signal : gSignals) {
sa.sa_sigaction = signal.uSignalHandlerFunc;
sigaction (signal.uSignal, &sa, &sa_out);
signal.uDefaultSignalHandlerFunc = sa_out.sa_sigaction;
TRACE_LOG ("Default handler = %p\n", signal.uDefaultSignalHandlerFunc);
}
TRACE_LOG ("ExceptionHandler Set");
}
extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad (JavaVM * pJavaVM, [[maybe_unused]] void * pReserved)
{
TRACEFUNC;
jclass exception_activity_class;
JNIEnv * env;
gJavaVM = pJavaVM;
gIsWorkerThread = false;
if (pJavaVM->GetEnv ((void **) &env, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
exception_activity_class = env->FindClass ("com/example/exceptionhandling/ExceptionActivity");
if (exception_activity_class == nullptr) {
TRACELINE;
return JNI_ERR;
}
gExceptionActivityClass = (jclass) env->NewGlobalRef (exception_activity_class);
SetExceptionHandlers ();
return JNI_VERSION_1_6;
}
void
RaiseException () noexcept
{
TRACEFUNC;
int a = 5;
int b = 0;
int c;
c = a / b;
TRACE_LOG ("Num = %d", c);
}
void *
ThreadFunc (void* ) noexcept
{
TRACEFUNC;
gIsWorkerThread = true;
AttachThread ();
RaiseException ();
TRACELINE;
return nullptr;
}
// Invoked from MainActivity.onCreate
extern "C" JNIEXPORT void JNICALL
Java_com_example_exceptionhandling_MainActivity_NativeMethod (JNIEnv* pEnv, jobject pObject)
{
TRACEFUNC;
pthread_t thread_handle;
pthread_create (&thread_handle, nullptr, ThreadFunc, nullptr);
}
在 RaiseException 函数中,我引发了“除以零”异常,从而导致 SIGFPE。 ExceptionHandler 被调用,它调用 ExceptionActivity,然后调用默认处理程序,然后终止线程(因为它是工作线程)。
ExceptionActivity在自己的进程中运行,第一个进程就死掉了。我收到以下日志:
---------------------------- PROCESS STARTED (4523) for package com.example.exceptionhandling ----------------------------
2024-04-09 20:02:16.117 4523-4523 MyApp com.example.exceptionhandling D MainApplication.onCreate
2024-04-09 20:02:16.120 4523-4523 MyApp com.example.exceptionhandling D Inside jint JNI_OnLoad(JavaVM *, void *)
2024-04-09 20:02:16.144 4523-4523 MyApp com.example.exceptionhandling D Inside void SetExceptionHandlers()
2024-04-09 20:02:16.147 4523-4523 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 20:02:16.149 4523-4523 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 20:02:16.149 4523-4523 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 20:02:16.152 4523-4523 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 20:02:16.152 4523-4523 MyApp com.example.exceptionhandling D ExceptionHandler Set
2024-04-09 20:02:16.807 4523-4523 MyApp com.example.exceptionhandling D MainActivity.onCreate
2024-04-09 20:02:17.922 4523-4523 MyApp com.example.exceptionhandling D Inside void Java_com_example_exceptionhandling_MainActivity_NativeMethod(JNIEnv *, jobject)
2024-04-09 20:02:17.923 4523-4523 MyApp com.example.exceptionhandling D Inside void CreateThread()
2024-04-09 20:02:17.925 4523-4604 MyApp com.example.exceptionhandling D Inside void *ThreadFunc(void *)
2024-04-09 20:02:17.925 4523-4604 MyApp com.example.exceptionhandling D Inside void AttachThread()
2024-04-09 20:02:17.925 4523-4604 MyApp com.example.exceptionhandling D Inside void RaiseException()
2024-04-09 20:02:17.925 4523-4604 MyApp com.example.exceptionhandling D Inside void ExceptionHandler(int, siginfo_t *, void *)
2024-04-09 20:02:17.925 4523-4604 MyApp com.example.exceptionhandling D Exception number = 8
2024-04-09 20:02:17.925 4523-4604 MyApp com.example.exceptionhandling D Inside void StartExceptionActivity()
2024-04-09 20:02:17.967 4523-4604 MyApp com.example.exceptionhandling D At line: 46
2024-04-09 20:02:17.967 4523-4604 MyApp com.example.exceptionhandling D Inside SignalHandlerFunc GetDefaultSignalHandlerFunc(int)
---------------------------- PROCESS STARTED (4606) for package com.example.exceptionhandling ----------------------------
---------------------------- PROCESS STARTED (4616) for package com.example.exceptionhandling ----------------------------
2024-04-09 20:02:18.420 4523-4604 MyApp com.example.exceptionhandling D Inside void KillThread()
2024-04-09 20:02:18.420 4523-4604 MyApp com.example.exceptionhandling D Inside void DetachThread()
2024-04-09 20:02:18.580 4606-4606 MyApp com.example.exceptionhandling D MainApplication.onCreate
2024-04-09 20:02:18.597 4606-4606 MyApp com.example.exceptionhandling D Inside jint JNI_OnLoad(JavaVM *, void *)
2024-04-09 20:02:18.635 4606-4606 MyApp com.example.exceptionhandling D Inside void SetExceptionHandlers()
2024-04-09 20:02:18.636 4606-4606 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 20:02:18.637 4606-4606 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 20:02:18.637 4606-4606 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 20:02:18.638 4606-4606 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 20:02:18.638 4606-4606 MyApp com.example.exceptionhandling D ExceptionHandler Set
2024-04-09 20:02:18.825 4523-4523 MyApp com.example.exceptionhandling D MainActivity.onStop
2024-04-09 20:02:19.181 4606-4606 MyApp com.example.exceptionhandling D Inside ExceptionActivity.onCreate
---------------------------- PROCESS ENDED (4616) for package com.example.exceptionhandling ----------------------------
此行为符合预期。第一个进程调用第二个进程,然后第一个进程终止。由于保留了第二个流程,所以不会给用户带来不好的体验。
现在,如果我更改 RaiseException 函数(在工作线程上调用)的逻辑以引发 SIGSEGV,事情就会发生:(
// native-lib.cpp
void
RaiseException () noexcept
{
TRACEFUNC;
int * ptr = nullptr;
*ptr = 45;
TRACE_LOG ("Num = %d", *ptr);
}
我观察到 ExceptionHandler 是通过 SIGSEGV 异常代码调用的。在这里,我调用 StartExceptionActivity,这再次导致使用 SIGABRT 调用 ExceptionHandler。
导致该问题的行是 CallStaticVoidMethod,它调用了 Kotlin 中的 StartExceptionActivity。当它尝试startActivity启动ExceptionActivity时,它会抛出一堆异常。这种情况发生了很多次,然后一切都消失了。
---------------------------- PROCESS STARTED (1678) for package com.example.exceptionhandling ----------------------------
2024-04-09 19:49:49.729 1678-1678 MyApp com.example.exceptionhandling D MainApplication.onCreate
2024-04-09 19:49:49.731 1678-1678 MyApp com.example.exceptionhandling D Inside jint JNI_OnLoad(JavaVM *, void *)
2024-04-09 19:49:49.767 1678-1678 MyApp com.example.exceptionhandling D Inside void SetExceptionHandlers()
2024-04-09 19:49:49.767 1678-1678 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:49.767 1678-1678 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:49.767 1678-1678 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:49.768 1678-1678 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:49.775 1678-1678 MyApp com.example.exceptionhandling D ExceptionHandler Set
2024-04-09 19:49:50.709 1678-1678 MyApp com.example.exceptionhandling D MainActivity.onCreate
2024-04-09 19:49:51.497 1678-1678 MyApp com.example.exceptionhandling D Inside void Java_com_example_exceptionhandling_MainActivity_NativeMethod(JNIEnv *, jobject)
2024-04-09 19:49:51.497 1678-1678 MyApp com.example.exceptionhandling D Inside void CreateThread()
2024-04-09 19:49:51.499 1678-1758 MyApp com.example.exceptionhandling D Inside void *ThreadFunc(void *)
2024-04-09 19:49:51.499 1678-1758 MyApp com.example.exceptionhandling D Inside void AttachThread()
2024-04-09 19:49:51.499 1678-1758 MyApp com.example.exceptionhandling D Inside void RaiseException()
2024-04-09 19:49:51.499 1678-1758 MyApp com.example.exceptionhandling D Inside void ExceptionHandler(int, siginfo_t *, void *)
2024-04-09 19:49:51.499 1678-1758 MyApp com.example.exceptionhandling D Exception number = 11
2024-04-09 19:49:51.499 1678-1758 MyApp com.example.exceptionhandling D Inside void StartExceptionActivity()
2024-04-09 19:49:51.839 1678-1758 MyApp com.example.exceptionhandling D Inside void ExceptionHandler(int, siginfo_t *, void *)
2024-04-09 19:49:51.839 1678-1758 MyApp com.example.exceptionhandling D Exception number = 6
2024-04-09 19:49:51.839 1678-1758 MyApp com.example.exceptionhandling D Inside void StartExceptionActivity()
---------------------------- PROCESS ENDED (1678) for package com.example.exceptionhandling ----------------------------
---------------------------- PROCESS STARTED (1759) for package com.example.exceptionhandling ----------------------------
2024-04-09 19:49:52.588 1759-1759 MyApp com.example.exceptionhandling D MainApplication.onCreate
2024-04-09 19:49:52.591 1759-1759 MyApp com.example.exceptionhandling D Inside jint JNI_OnLoad(JavaVM *, void *)
2024-04-09 19:49:52.603 1759-1759 MyApp com.example.exceptionhandling D Inside void SetExceptionHandlers()
2024-04-09 19:49:52.604 1759-1759 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:52.604 1759-1759 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:52.604 1759-1759 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:52.604 1759-1759 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:52.604 1759-1759 MyApp com.example.exceptionhandling D ExceptionHandler Set
2024-04-09 19:49:52.967 1759-1759 MyApp com.example.exceptionhandling D MainActivity.onCreate
2024-04-09 19:49:53.767 1759-1759 MyApp com.example.exceptionhandling D Inside void Java_com_example_exceptionhandling_MainActivity_NativeMethod(JNIEnv *, jobject)
2024-04-09 19:49:53.767 1759-1759 MyApp com.example.exceptionhandling D Inside void CreateThread()
2024-04-09 19:49:53.771 1759-1788 MyApp com.example.exceptionhandling D Inside void *ThreadFunc(void *)
2024-04-09 19:49:53.771 1759-1788 MyApp com.example.exceptionhandling D Inside void AttachThread()
2024-04-09 19:49:53.771 1759-1788 MyApp com.example.exceptionhandling D Inside void RaiseException()
2024-04-09 19:49:53.771 1759-1788 MyApp com.example.exceptionhandling D Inside void ExceptionHandler(int, siginfo_t *, void *)
2024-04-09 19:49:53.771 1759-1788 MyApp com.example.exceptionhandling D Exception number = 11
2024-04-09 19:49:53.771 1759-1788 MyApp com.example.exceptionhandling D Inside void StartExceptionActivity()
2024-04-09 19:49:54.110 1759-1788 MyApp com.example.exceptionhandling D Inside void ExceptionHandler(int, siginfo_t *, void *)
2024-04-09 19:49:54.110 1759-1788 MyApp com.example.exceptionhandling D Exception number = 6
2024-04-09 19:49:54.110 1759-1788 MyApp com.example.exceptionhandling D Inside void StartExceptionActivity()
---------------------------- PROCESS ENDED (1759) for package com.example.exceptionhandling ----------------------------
---------------------------- PROCESS STARTED (1789) for package com.example.exceptionhandling ----------------------------
2024-04-09 19:49:54.911 1789-1789 MyApp com.example.exceptionhandling D MainApplication.onCreate
2024-04-09 19:49:54.912 1789-1789 MyApp com.example.exceptionhandling D Inside jint JNI_OnLoad(JavaVM *, void *)
2024-04-09 19:49:54.927 1789-1789 MyApp com.example.exceptionhandling D Inside void SetExceptionHandlers()
2024-04-09 19:49:54.928 1789-1789 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:54.928 1789-1789 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:54.928 1789-1789 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:54.929 1789-1789 MyApp com.example.exceptionhandling D Default handler = 0x7395f2d1ab91
2024-04-09 19:49:54.929 1789-1789 MyApp com.example.exceptionhandling D ExceptionHandler Set
2024-04-09 19:49:55.585 1789-1789 MyApp com.example.exceptionhandling D MainActivity.onCreate
2024-04-09 19:49:56.296 1789-1789 MyApp com.example.exceptionhandling D Inside void Java_com_example_exceptionhandling_MainActivity_NativeMethod(JNIEnv *, jobject)
2024-04-09 19:49:56.296 1789-1789 MyApp com.example.exceptionhandling D Inside void CreateThread()
2024-04-09 19:49:56.296 1789-1840 MyApp com.example.exceptionhandling D Inside void *ThreadFunc(void *)
2024-04-09 19:49:56.297 1789-1840 MyApp com.example.exceptionhandling D Inside void AttachThread()
2024-04-09 19:49:56.297 1789-1840 MyApp com.example.exceptionhandling D Inside void RaiseException()
2024-04-09 19:49:56.297 1789-1840 MyApp com.example.exceptionhandling D Inside void ExceptionHandler(int, siginfo_t *, void *)
2024-04-09 19:49:56.297 1789-1840 MyApp com.example.exceptionhandling D Exception number = 11
2024-04-09 19:49:56.297 1789-1840 MyApp com.example.exceptionhandling D Inside void StartExceptionActivity()
2024-04-09 19:49:56.783 1789-1840 MyApp com.example.exceptionhandling D Inside void ExceptionHandler(int, siginfo_t *, void *)
2024-04-09 19:49:56.783 1789-1840 MyApp com.example.exceptionhandling D Exception number = 6
2024-04-09 19:49:56.783 1789-1840 MyApp com.example.exceptionhandling D Inside void StartExceptionActivity()
---------------------------- PROCESS ENDED (1789) for package com.example.exceptionhandling ----------------------------
打开所有日志后,会显示
2024-04-09 19:57:50.419 1928-1956 MyApp com.example.exceptionhandling D Inside void StartExceptionActivity()
2024-04-09 19:57:50.424 1928-1956 ceptionhandling com.example.exceptionhandling A java_vm_ext.cc:591] JNI DETECTED ERROR IN APPLICATION: JNI ERROR (app bug): jobject is an invalid JNI transition frame reference: 0x7395e30c9d20 (use of invalid jobject)
java_vm_ext.cc:591] in call to IsInstanceOf
java_vm_ext.cc:591] from void android.os.Parcel.nativeMarkForBinder(long, android.os.IBinder)
出于某种原因,我在 Kotlin 中编写的任何代码都会导致此类错误(甚至是 Log.d)。
我的问题是:
我的目标是,在其中一种不太可能发生的情况下,我不会终止应用程序。
在 Java 进程内,您不能这样做。
首先,JVM 在内部使用
SIGSEGV
,因此您几乎无法安装自己的 SIGSEGV
处理程序。理论上,如果您能够区分源自 JVM 代码(您不想干扰信号处理)的信号和源自您确实想要干扰信号处理的代码的信号,您就可以安装您的处理程序。干扰信号处理。但在实践中,这几乎不可能可靠地完成,并且它不会解决在收到所谓的致命信号后尝试继续运行的所有其他问题。
您无法从其他信号的信号处理程序返回,因为只会再次引发相同的信号。所以你只剩下某个版本的 SIGSEGV
/
[sig]setjmp()
。
如果你使用它们,你就会面临它们发生在异步信号不安全函数调用内部,或者某些状态被互斥体或其他同步对象锁定的风险。
如果您[sig]longjmp()
脱离了异步信号-
功能,Linux
[sig]longjmp()
手册页解释了整个进程必须运行的一些限制:
setjmp()
跳出信号处理程序后,您必须将整个过程限制为仅进行异步信号安全调用。或者,您必须在每次异步信号不安全调用之前阻止信号。然而,您无法确保“整个 JVM”将遵循这些限制中的任何一个。事实上,您可以有效地确定 JVM 不会以这种方式工作。
这甚至还没有开始解决如果你跳出互斥锁被锁定的上下文或其他类似情况会发生什么。