当前位置: 首页 > 知识库问答 >
问题:

在本机代码中捕获信号(SIGSEGV)后,从JNI回调到Java Android应用程序代码中

楚羽
2023-03-14

我有一个最小的Android应用程序,使用启用了c支持的新项目向导创建。该应用程序的目的是允许c在捕获信号(SIGSEGV)后回调到java中。程序的顺序简短而甜蜜,伪代码就像这样:

    < li >输入本机方法< code>handleSegv() < ol > < li >本机代码回调到java中作为测试 < li >本机代码设置SIGSEGV处理程序
  1. 本机代码引发/发送SIGSEGV
    < li >本机代码捕获信号并记录下来 < li >本机代码回调到java中 < li >本机代码再次记录以显示其通过回调的步骤

上面唯一不起作用的步骤是步骤3.2。似乎在捕获SIGSEGV之后,当本机代码尝试回调到java时,没有任何反应。我已经在模拟器和设备上尝试了此操作,结果相同。我不确定在这一点上我是否做错了什么,或者处理信号是否有一些基本的东西,不允许我在捕获它后回调到java中。

我有演示这一点的代码,可以从github上的repo克隆,但实际上只有两个源文件

CrashActivity.java:

package com.kevinkreiser.crashtest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class CrashActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_crash);

        //setup segv handler
        handleSegv();
        //cause a segv
        sendSegv();
    }

    /**
     * Sets up signal handler for SIGSEGV which will call the callback function below
     * @return true if the handler was set
     */
    public native boolean handleSegv();

    /**
     * Raises the SIGSEGV signal which will cause the handler to be called
     */
    public native void sendSegv();

    /**
     * A function that the native code will call back when it receives SIGSEGV
     * as an illustration it just logs
     *
     * @param message  The message coming back from c++
     */
    public void callback(String message) {
        Log.e("CrashActivity.callback", message);
    }
}

native-lib.cpp:

#include <android/log.h>
#include <jni.h>
#include <string.h>
#include <signal.h>
#include <string>

//globals persisting between calls from javaland
static JavaVM* vm = NULL;
static jobject activity = NULL;
static jmethodID callback = NULL;

//gets called first when a signal is sent to the running pid
static void signal_handler(int signal, siginfo_t*, void*) {
    //get an env so we can call back to java
    JNIEnv* env;
    if(vm->AttachCurrentThread(&env, NULL) != JNI_OK)
        return;

    //call back to java with a message
    __android_log_print(ANDROID_LOG_ERROR, "native-lib.signal_handler", "Calling with signal %d", signal);
    std::string message = "Got signal " + std::to_string(signal);
    jstring msg = env->NewStringUTF(message.c_str());
    env->CallVoidMethod(activity, callback, msg);
    __android_log_print(ANDROID_LOG_ERROR, "native-lib.signal_handler", "Called with signal %d", signal);
}

extern "C" JNIEXPORT void JNICALL
Java_com_kevinkreiser_crashtest_CrashActivity_sendSegv(JNIEnv*, jobject) {
    raise(SIGSEGV);
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_kevinkreiser_crashtest_CrashActivity_handleSegv(JNIEnv* env, jobject obj) {
    //get java hooks we need to make the callback
    env->GetJavaVM(&vm);
    activity = env->NewGlobalRef(obj);
    if (!activity)
        return false;
    jclass activity_class = env->GetObjectClass(activity);
    if (!activity_class)
        return false;
    callback = env->GetMethodID(activity_class, "callback", "(Ljava/lang/String;)V");
    if (!callback)
        return false;

    //try calling back to java with a message
    jstring message = env->NewStringUTF("No signal yet");
    env->CallVoidMethod(activity, callback, message);

    //register for SIGSEGV
    struct sigaction action;
    memset(&action, 0, sizeof(struct sigaction));
    action.sa_sigaction = signal_handler;
    action.sa_flags = SA_SIGINFO;
    sigaction(SIGSEGV, &action, NULL);

    return true;
}

当我运行程序并查看logcat的输出时,我看到以下内容:

2019-01-15 11:59:50.795 11183-11183/com.kevinkreiser.crashtest E/CrashActivity.callback: No signal yet
2019-01-15 11:59:50.795 11183-11183/com.kevinkreiser.crashtest E/native-lib.signal_handler: Calling with signal 11
2019-01-15 11:59:50.795 11183-11183/com.kevinkreiser.crashtest E/native-lib.signal_handler: Called with signal 11

如果我使用调试器单步执行程序,并在本机<code>signal_handler的行。在此之后,如果我跨过任何一行,其中包含使用<code>JNIEnv

我在这里看到了stackoverflow上的其他实现,它们基本上都能做到这一点,但我无法让它们中的任何一个正常工作。我还尝试从本机代码中抛出一个java异常,但最终也没有返回到javaland,并返回一条关于未决异常的消息。有人能看到这里出了什么问题吗?提前感谢!

共有1个答案

方宜
2023-03-14

@安德鲁·汉勒的评论是正确答案:

是的,你做错了什么:你通过在信号处理程序中调用非异步信号安全函数来调用未定义的行为。没有支持调用函数的特定留档,例如异步安全函数的POSIX列表,你真的不能从信号处理程序进行任何调用。C标准的脚注188甚至指出:“因此,信号处理程序通常不能调用标准库函数。”POSIX提供了一个可以安全调用的函数列表——在POSIX下。其他任何东西都是未定义的行为。

他之前在这里对这个问题给出了更详细的答案:https://stackoverflow.com/a/34553070/5251867

编辑:

纵观可用的功能,似乎有两种途径可以说服。

  1. 使用open关闭来丢弃一个文件,其中包含有关捕获的信号的相关信息,并稍后处理此文件(在应用程序重新启动时或从另一个监控此文件更改的服务中)
  2. 使用linkbindsend通过套接字将详细信息发送到其他html" target="_blank">进程

我想这两者在技术上都是IPC,因为它们都是让另一个进程访问信号处理程序发布的信息的手段。将此信息发送到另一个进程,您可以在其中对信息做些什么似乎是唯一合适的前进方式。

 类似资料:
  • 问题内容: 我正在使用c ++为node.js编写附加组件。 这里有一些片段: 我需要将一个JavaScript函数另存为回调,以便稍后调用。Client类是另一个对象的观察者,应从onAsyncMethodEnds调用javascript回调。不幸的是,当我调用函数“ BeInitiator”时,在回调Call()之前收到“ Bus error:10”错误 感谢建议 问题答案: 您不能从另一个线

  • 我正在用HERE SDK开发一个应用程序,到现在为止一切工作都很好。我得到这样的错误: 或以下错误: 它们让我的应用崩溃了。 并不总是相同的错误,但它们总是单独出现在我的Logcat中,没有其他信息。 在我使用的所有应用程序中,我都在使用对象和服务,即使打印stacktrace,我也没有得到关于错误的更多信息。 我只是注意到,这些错误几乎是随机出现的,但只有在我使用这些对象/服务时才会出现。 我用

  • 我正在使用visual studio代码IDE开发react原生应用程序,而不是使用expo library。 在此之前,我是在android工作室工作,调试是直接和简单的。 现在对于反应原生,我想知道如何使用可视化工作室代码IDE调试我的应用程序?

  • 问题内容: 我正在使用Visual Studio代码IDE来开发React本机应用程序,并且未在使用EXPO库。 在此之前,我在android studio上工作,在其中进行调试非常简单明了。 现在,对于React-native,我想知道如何使用Visual Studio Code IDE调试应用程序? 问题答案: 步骤1:通过摇动手机或在计算机中键入此命令来打开手机中的应用程序设置。 第2 步:

  • 问题内容: 我正在尝试为Linux上运行的程序编写多线程日志记录系统。 在主程序线程中对日志记录系统的调用会将包含要记录的数据的数据结构推送到FIFO队列中。专用线程选择队列中的数据并输出数据,而程序主线程继续执行其任务。 如果主程序导致SIGSEGV或其他信号发出,我需要在终止之前确保队列为空。 我的计划是使用除一个线程外的所有线程使用pthread_sigmask http://man7.or

  • 我想使用Android原生代码在代号中获取,以下是我的原生实现类,我还在构建提示中添加了权限,代码没有给出任何错误但返回 我添加了这段代码, < code >如果(!androidnativeutil.checkforpermission(manifest.permission‌。READ_PHONE_STATE,“这应该是显示给用户的描述……”)){您的代码在此处} 但得到的错误是 错误:如果(