-
Notifications
You must be signed in to change notification settings - Fork 912
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
63 changed files
with
1,620 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ appStartup: | |
enabled: false | ||
appSize: | ||
enabled: false | ||
disableRetries: true | ||
disableRetries: false | ||
|
||
flows: | ||
- "**" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
project(crash-ndk) | ||
cmake_minimum_required(VERSION 3.4.1) | ||
|
||
add_library( # Sets the name of the library. | ||
crash-ndk | ||
|
||
# Sets the library as a shared library. | ||
SHARED | ||
|
||
# Provides a relative path to your source file(s). | ||
src/main/cpp/ndk-crash.cpp | ||
src/main/cpp/jni.cpp | ||
src/main/cpp/pixel.cpp | ||
) | ||
|
||
find_library( # Sets the name of the path variable. | ||
log-lib | ||
|
||
# Specifies the name of the NDK library that | ||
# you want CMake to locate. | ||
log) | ||
|
||
target_link_libraries( | ||
# Specifies the target library. | ||
crash-ndk | ||
|
||
# Links the target library to the log library | ||
# included in the NDK. | ||
${log-lib} ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#ifndef ANDROID_JNI_H | ||
#define ANDROID_JNI_H | ||
|
||
#include <cstdio> | ||
#include <android/log.h> | ||
|
||
void __platform_log_print(int prio, const char *tag, const char *fmt, ...); | ||
|
||
// loglevel will be assigned during library initialisation, it always has a default value | ||
extern int loglevel; | ||
extern char pname[256]; | ||
extern char appVersion[256]; | ||
extern bool isCustomTab; | ||
|
||
// Use this method to print log messages into the console | ||
#define log_print(prio, format, ...) do { if (prio >= loglevel) __platform_log_print(prio, "ndk-crash", format, ##__VA_ARGS__); } while (0) | ||
|
||
#endif // ANDROID_JNI_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
#include <jni.h> | ||
#include <android/log.h> | ||
#include <exception> | ||
#include <string.h> // strncpy | ||
|
||
#include "android.h" | ||
#include "ndk-crash.h" | ||
#include "pixel.h" | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
|
||
static JavaVM *JVM = NULL; | ||
jclass clsCrash; | ||
jobject CLASS_JVM_CRASH = NULL; | ||
|
||
|
||
static jobject jniGlobalRef(JNIEnv *env, jobject cls); | ||
static jclass jniFindClass(JNIEnv *env, const char *name); | ||
static jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature); | ||
|
||
int loglevel = 0; | ||
char appVersion[256]; | ||
char pname[256]; | ||
bool isCustomTab = false; | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
|
||
|
||
void __platform_log_print(int prio, const char *tag, const char *fmt, ...) { | ||
char line[1024]; | ||
va_list argptr; | ||
va_start(argptr, fmt); | ||
vsprintf(line, fmt, argptr); | ||
__android_log_print(prio, tag, "%s", line); | ||
va_end(argptr); | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
// JNI utils | ||
/////////////////////////////////////////////////////////////////////////// | ||
|
||
static jobject jniGlobalRef(JNIEnv *env, jobject cls) { | ||
jobject gcls = env->NewGlobalRef(cls); | ||
if (gcls == NULL) | ||
log_print(ANDROID_LOG_ERROR, "Global ref failed (out of memory?)"); | ||
return gcls; | ||
} | ||
|
||
static jclass jniFindClass(JNIEnv *env, const char *name) { | ||
jclass cls = env->FindClass(name); | ||
if (cls == NULL) | ||
log_print(ANDROID_LOG_ERROR, "Class %s not found", name); | ||
return cls; | ||
} | ||
|
||
static jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature) { | ||
jmethodID method = env->GetMethodID(cls, name, signature); | ||
if (method == NULL) { | ||
log_print(ANDROID_LOG_ERROR, "Method %s %s not found", name, signature); | ||
} | ||
return method; | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
// JNI lifecycle | ||
/////////////////////////////////////////////////////////////////////////// | ||
|
||
jint JNI_OnLoad(JavaVM *vm, void *reserved) { | ||
JNIEnv *env; | ||
if ((vm)->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { | ||
log_print(ANDROID_LOG_INFO, "JNI load GetEnv failed"); | ||
return -1; | ||
} | ||
|
||
jint rs = env->GetJavaVM(&JVM); | ||
if (rs != JNI_OK) { | ||
log_print(ANDROID_LOG_ERROR, "Could not get JVM"); | ||
return -1; | ||
} | ||
|
||
return JNI_VERSION_1_6; | ||
} | ||
|
||
void JNI_OnUnload(JavaVM *vm, void *reserved) { | ||
log_print(ANDROID_LOG_INFO, "JNI unload"); | ||
|
||
JNIEnv *env; | ||
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) | ||
log_print(ANDROID_LOG_INFO, "JNI load GetEnv failed"); | ||
else { | ||
env->DeleteGlobalRef(clsCrash); | ||
} | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
// native<>JVM interface | ||
/////////////////////////////////////////////////////////////////////////// | ||
|
||
extern "C" JNIEXPORT void JNICALL | ||
Java_com_duckduckgo_app_anr_ndk_NativeCrashInit_jni_1register_1sighandler( | ||
JNIEnv* env, | ||
jobject instance, | ||
jint loglevel_, | ||
jstring version_, | ||
jstring pname_, | ||
jboolean customtab_ | ||
) { | ||
|
||
if (!native_crash_handler_init()) { | ||
log_print(ANDROID_LOG_ERROR, "Error initialising crash handler."); | ||
return; | ||
} | ||
|
||
// get and set loglevel | ||
loglevel = loglevel_; | ||
|
||
// get and set app vesrion | ||
const char *versionChars = env->GetStringUTFChars(version_, nullptr); | ||
strncpy(appVersion, versionChars, sizeof(appVersion) - 1); | ||
appVersion[sizeof(appVersion) - 1] = '\0'; // Ensure null-termination | ||
env->ReleaseStringUTFChars(version_, versionChars); | ||
|
||
// get and set process name | ||
const char *pnameChars = env->GetStringUTFChars(pname_, nullptr); | ||
strncpy(pname, pnameChars, sizeof(pname) - 1); | ||
pname[sizeof(pname) - 1] = '\0'; // Ensure null-termination | ||
env->ReleaseStringUTFChars(pname_, pnameChars); | ||
|
||
// get and set isCustomTabs | ||
isCustomTab = customtab_; | ||
|
||
clsCrash = env->GetObjectClass(instance); | ||
const char *emptyParamVoidSig = "()V"; | ||
CLASS_JVM_CRASH = env->NewGlobalRef(instance); | ||
|
||
send_crash_handle_init_pixel(); | ||
|
||
log_print(ANDROID_LOG_ERROR, "Native crash handler successfully initialized."); | ||
} | ||
|
||
extern "C" JNIEXPORT void JNICALL | ||
Java_com_duckduckgo_app_anr_ndk_NativeCrashInit_jni_1unregister_sighandler( | ||
JNIEnv* env, | ||
jobject /* this */) { | ||
native_crash_handler_fini(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
#include "jni.h" | ||
#include "android.h" | ||
#include "pixel.h" | ||
|
||
#include <csignal> | ||
#include <cstdio> | ||
#include <cstring> | ||
#include <memory> | ||
#include <cxxabi.h> | ||
#include <unistd.h> | ||
|
||
#define sizeofa(array) sizeof(array) / sizeof(array[0]) | ||
|
||
// sometimes signal handlers inlinux consume crashes entirely. For those cases we trigger a signal so that we ultimately | ||
// crash properly | ||
#define __NR_tgkill 270 | ||
|
||
// Caught signals | ||
static const int SIGNALS_TO_CATCH[] = { | ||
SIGABRT, | ||
SIGBUS, | ||
SIGFPE, | ||
SIGSEGV, | ||
SIGILL, | ||
SIGSTKFLT, | ||
SIGTRAP, | ||
}; | ||
|
||
// Signal handler context | ||
struct CrashInContext { | ||
// Old handlers of signals that we restore on de-initialization. Keep values for all possible | ||
// signals, for unused signals nullptr value is stored. | ||
struct sigaction old_handlers[NSIG]; | ||
}; | ||
|
||
// Crash handler function signature | ||
typedef void (*CrashSignalHandler)(int, siginfo*, void*); | ||
|
||
// Global instance of context. As the app can only crash once per process lifetime, this can be global | ||
static CrashInContext* crashInContext = nullptr; | ||
|
||
|
||
// Main signal handling function. | ||
static void native_crash_sig_handler(int signo, siginfo* siginfo, void* ctxvoid) { | ||
// Restoring an old handler to make built-in Android crash mechanism work. | ||
sigaction(signo, &crashInContext->old_handlers[signo], nullptr); | ||
|
||
// Log crash message | ||
__android_log_print(ANDROID_LOG_ERROR, "ndk-crash", "Terminating with uncaught exception of type %d", signo); | ||
send_crash_pixel(); | ||
|
||
// sometimes signal handlers inlinux consume crashes entirely. For those cases we trigger a signal so that we ultimately | ||
// crash properly, ie. to run standard bionic handler | ||
if (siginfo->si_code <= 0 || signo == SIGABRT) { | ||
if (syscall(__NR_tgkill, getpid(), gettid(), signo) < 0) { | ||
_exit(1); | ||
} | ||
} | ||
} | ||
|
||
// Register signal handler for crashes | ||
static bool register_sig_handler(CrashSignalHandler handler, struct sigaction old_handlers[NSIG]) { | ||
struct sigaction sigactionstruct; | ||
memset(&sigactionstruct, 0, sizeof(sigactionstruct)); | ||
sigactionstruct.sa_flags = SA_SIGINFO; | ||
sigactionstruct.sa_sigaction = handler; | ||
|
||
// Register new handlers for all signals | ||
for (int index = 0; index < sizeofa(SIGNALS_TO_CATCH); ++index) { | ||
const int signo = SIGNALS_TO_CATCH[index]; | ||
|
||
if (sigaction(signo, &sigactionstruct, &old_handlers[signo])) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
// Unregister already register signal handler | ||
static void unregister_sig_handler(struct sigaction old_handlers[NSIG]) { | ||
// Recover old handler for all signals | ||
for (int signo = 0; signo < NSIG; ++signo) { | ||
const struct sigaction* old_handler = &old_handlers[signo]; | ||
|
||
if (!old_handler->sa_handler) { | ||
continue; | ||
} | ||
|
||
sigaction(signo, old_handler, nullptr); | ||
} | ||
} | ||
|
||
bool native_crash_handler_fini() { | ||
// Check if already deinitialized | ||
if (!crashInContext) return false; | ||
|
||
// Unregister signal handlers | ||
unregister_sig_handler(crashInContext->old_handlers); | ||
|
||
// Free singleton crash handler context | ||
free(crashInContext); | ||
crashInContext = nullptr; | ||
|
||
log_print(ANDROID_LOG_ERROR, "Native crash handler successfully deinitialized."); | ||
|
||
return true; | ||
} | ||
|
||
bool native_crash_handler_init() { | ||
// Check if already initialized | ||
if (crashInContext) { | ||
log_print(ANDROID_LOG_INFO, "Native crash handler is already initialized."); | ||
return false; | ||
} | ||
|
||
// Initialize singleton crash handler context | ||
crashInContext = static_cast<CrashInContext *>(malloc(sizeof(CrashInContext))); | ||
memset(crashInContext, 0, sizeof(CrashInContext)); | ||
|
||
// Trying to register signal handler. | ||
if (!register_sig_handler(&native_crash_sig_handler, crashInContext->old_handlers)) { | ||
native_crash_handler_fini(); | ||
log_print(ANDROID_LOG_ERROR, "Native crash handler initialization failed."); | ||
return false; | ||
} | ||
|
||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// Java | ||
#ifndef NDK_CRASH_H | ||
#define NDK_CRASH_H | ||
|
||
// Call this method to register native crash handling | ||
bool native_crash_handler_init(); | ||
// Call this method to de-register native crash handling | ||
bool native_crash_handler_fini(); | ||
|
||
#endif // NDK_CRASH_H |
Oops, something went wrong.