How to catch JNI Crashes as exceptions using Signa

2019-09-14 01:49发布

问题:

This question already has an answer here:

  • How to catch JNI/Java Exception 2 answers

I developed a Java tool and it has many JNI functions, I am getting JNI crashes often. Is there any possibility to avoid those crashes or to catch these crashes as exceptions. I surfed internet and found it is possible through signal processing, signal chanining, sigaction(), etc. But I could not get a reliable source to direct me. Please do guide me on this.

回答1:

JNI exceptions are considered as signals. You could set up your signal handlers by sigaction and then you could try to unwind the crash stack, for example by libcorkscrew, to save it on the disk. After that you could invoke a Java method via JNI interfaces to process the info you saved.

Sample:

int watched_signals[] = { SIGABRT, SIGILL, SIGSEGV, SIGINT, SIGKILL };

void sighandler_func(int sig, siginfo_t* sig_info, void* ptr)
{
    // Dump the callstack by libcorkscrew
    ...
    // Call a JNI interface to process the stack info
    ...
}

struct sigaction sighandler;
sighandler.sa_sigaction = &sighandler_func;
sighandler.sa_mask = 0;
sighandler.sa_flags = SA_SIGINFO | SA_ONSTACK;

for(int signal : watched_signals)
{
    sigaction(signal, &sighandler, nullptr);
}

Suppose you have involved libcorkscrew into you ndk project then you could get the crash stack:

#include <dlfcn.h>
#include <ucontext.h>
#include <corkscrew/backtrace.h>
#include <backtrace-arch.h>

void dump_stack(int sig, siginfo_t* sig_info, void* ptr)
{
    const size_t BACKTRACE_FRAMES_MAX = 0xFF;

    static backtrace_frame_t frames[BACKTRACE_FRAMES_MAX];
    static backtrace_symbol_t symbols[BACKTRACE_FRAMES_MAX];

    map_info_t* const info = acquire_my_map_info_list();
    const ssize_t size = unwind_backtrace_signal_arch(sig_info, ptr, info, frames, 0, BACKTRACE_FRAMES_MAX);
    get_backtrace_symbols(frames, size, symbols);

    for (int i = 0; i < size; i++)
    {
        backtrace_symbol_t& symbol = symbols[i];
        // You could change the printf to fwrite if you want to save the info on disk
        printf("#%02d pc %08X  %s (%s+%d)",
                    i,
                    symbol.relative_pc,
                    symbol.map_name ? symbol.map_name : "<unknown>",
                    symbol.demangled_name ? symbol.demangled_name : symbol.symbol_name,
                    symbol.relative_pc - symbol.relative_symbol_addr);
    }
    free_backtrace_symbols(symbols, size);
    release_my_map_info_list(info);
}

Even through you could continue the program after you handled the signal but I strongly suggest you to save the info on disk and process it in the next app launch. Because most of the time you program would fail when a signal raised.