Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Feature/crash loop detection #80

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,5 @@ hs_err_pid*
obj/
.externalNativeBuild
**/.cxx

backtrace-library/src/main/jniLibs/**
28 changes: 28 additions & 0 deletions backtrace-library/src/main/cpp/backends/backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,32 @@ void Disable() {
"Disable not supported on this backend");
#endif
}

bool EnableCrashLoopDetection() {
#ifdef CRASHPAD_BACKEND
return EnableCrashLoopDetectionCrashpad();
#elif BREAKPAD_BACKEND
__android_log_print(ANDROID_LOG_ERROR, "Backtrace-Android", "EnableCrashLoopDetection not supported on this backend");
return false;
#endif
}

bool IsSafeModeRequired() {
#ifdef CRASHPAD_BACKEND
return IsSafeModeRequiredCrashpad();
#elif BREAKPAD_BACKEND
__android_log_print(ANDROID_LOG_ERROR, "Backtrace-Android", "IsSafeModeRequired not supported on this backend");
return false;
#endif
}

int ConsecutiveCrashesCount() {
#ifdef CRASHPAD_BACKEND
return ConsecutiveCrashesCountCrashpad();
#elif BREAKPAD_BACKEND
__android_log_print(ANDROID_LOG_ERROR, "Backtrace-Android", "ConsecutiveCrashesCount not supported on this backend");
return 0;
#endif
}

}
24 changes: 22 additions & 2 deletions backtrace-library/src/main/cpp/backends/crashpad-backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ extern std::atomic_bool disabled;
static crashpad::CrashpadClient *client;
static std::unique_ptr<crashpad::CrashReportDatabase> database;

static int consecutive_crashes_count = 0;
bool InitializeCrashpad(jstring url,
jstring database_path,
jstring handler_path,
Expand Down Expand Up @@ -121,8 +122,11 @@ bool InitializeCrashpad(jstring url,
// Start crash handler
client = new crashpad::CrashpadClient();

initialized = client->StartHandlerAtCrash(handler, db, db, backtraceUrl, attributes,
arguments);
// Get consecutive crashes count BEFORE any handler started,
// as it writes extra line into CSV, what leads to getting 0 for each next ConsecutiveCrashesCount call
consecutive_crashes_count = crashpad::CrashpadClient::ConsecutiveCrashesCount(db);

initialized = client->StartHandlerAtCrash(handler, db, db, backtraceUrl, attributes, arguments);

env->ReleaseStringUTFChars(url, backtraceUrl);
env->ReleaseStringUTFChars(handler_path, handlerPath);
Expand Down Expand Up @@ -226,3 +230,19 @@ void ReEnableCrashpad() {
disabled = false;
}
}

bool EnableCrashLoopDetectionCrashpad() {
if (client != nullptr) {
return client->EnableCrashLoopDetection();
} else {
return false;
}
}

bool IsSafeModeRequiredCrashpad() {
return consecutive_crashes_count >= 5;
}

int ConsecutiveCrashesCountCrashpad() {
return consecutive_crashes_count;
}
15 changes: 15 additions & 0 deletions backtrace-library/src/main/cpp/backtrace-native.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,19 @@ Java_backtraceio_library_BacktraceDatabase_disable(JNIEnv *env, jobject thiz) {
Disable();
}

JNIEXPORT jboolean JNICALL
Java_backtraceio_library_BacktraceDatabase_EnableCrashLoopDetection(JNIEnv *env, jclass clazz) {
return EnableCrashLoopDetection();
}

JNIEXPORT jboolean JNICALL
Java_backtraceio_library_BacktraceDatabase_IsSafeModeRequired(JNIEnv *env, jclass clazz) {
return IsSafeModeRequired();
}

JNIEXPORT int JNICALL
Java_backtraceio_library_BacktraceDatabase_ConsecutiveCrashesCount(JNIEnv *env, jclass clazz) {
return ConsecutiveCrashesCount();
}

}
2 changes: 1 addition & 1 deletion backtrace-library/src/main/cpp/crashpad
Submodule crashpad updated 1124 files
6 changes: 6 additions & 0 deletions backtrace-library/src/main/cpp/include/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ void DumpWithoutCrash(jstring message, jboolean set_main_thread_as_faulting_thre
void AddAttribute(jstring key, jstring value);

void Disable();

bool EnableCrashLoopDetection();

bool IsSafeModeRequired();

int ConsecutiveCrashesCount();
}

#endif //BACKTRACE_ANDROID_BACKEND_H
4 changes: 4 additions & 0 deletions backtrace-library/src/main/cpp/include/crashpad-backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,8 @@ void DisableCrashpad();

void ReEnableCrashpad();

bool EnableCrashLoopDetectionCrashpad();
bool IsSafeModeRequiredCrashpad();
int ConsecutiveCrashesCountCrashpad();

#endif //BACKTRACE_ANDROID_CRASHPAD_BACKEND_H
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,28 @@ public class BacktraceClient extends BacktraceBase {
*/
private BacktraceANRWatchdog anrWatchdog;

/**
* Enables Crash loop detection to verify if the application is in the crash loop.
*/
public static boolean EnableCrashLoopDetection() {
return BacktraceDatabase.EnableCrashLoopDetection();
}

/**
* Determine if application requires a safe mode to launch.
*/
public static boolean IsSafeModeRequired() {
return BacktraceDatabase.IsSafeModeRequired();
}

/**
* Returns the number of consecutive application crashes
*/
public static int ConsecutiveCrashesCount() {
return BacktraceDatabase.ConsecutiveCrashesCount();
}


/**
* Initializing Backtrace client instance with BacktraceCredentials
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -464,4 +464,8 @@ private boolean validateDatabaseSize() {
public long getDatabaseSize() {
return backtraceDatabaseContext.getDatabaseSize();
}

public static native boolean EnableCrashLoopDetection();
public static native boolean IsSafeModeRequired();
public static native int ConsecutiveCrashesCount();
}
41 changes: 30 additions & 11 deletions example-app/src/main/java/backtraceio/backtraceio/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package backtraceio.backtraceio;

import static backtraceio.backtraceio.BuildConfig.BACKTRACE_SUBMISSION_URL;

import android.content.Context;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
Expand All @@ -27,6 +31,7 @@
import backtraceio.library.enums.BacktraceBreadcrumbType;
import backtraceio.library.enums.database.RetryBehavior;
import backtraceio.library.enums.database.RetryOrder;
import backtraceio.library.models.BacktraceData;
import backtraceio.library.models.BacktraceExceptionHandler;
import backtraceio.library.models.BacktraceMetricsSettings;
import backtraceio.library.models.database.BacktraceDatabaseSettings;
Expand All @@ -41,18 +46,33 @@ public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
System.loadLibrary("backtrace-native");
}

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

// Crash Loop Detector example
BacktraceClient.EnableCrashLoopDetection();
boolean isCLSafeModeReq = BacktraceClient.IsSafeModeRequired();
int crashesCount = BacktraceClient.ConsecutiveCrashesCount();
Log.i("BacktraceAndroid", String.format("ConsecutiveCrashesCount: %d", crashesCount));

// Set this value in your local.properties
if (BuildConfig.BACKTRACE_SUBMISSION_URL != null) {
backtraceClient = initializeBacktrace(BuildConfig.BACKTRACE_SUBMISSION_URL);
}


View viewBackground = findViewById(R.id.viewBackground);
if (viewBackground != null) {
viewBackground.setBackgroundColor(isCLSafeModeReq
? getResources().getColor(R.color.colorAccent)
: getResources().getColor(R.color.colorWhite));
}

symlinkAndWriteFile();
}

Expand Down Expand Up @@ -89,7 +109,7 @@ private BacktraceClient initializeBacktrace(final String submissionUrl) {
put("custom.attribute", "My Custom Attribute");
}};

List<String> attachments = new ArrayList<String>(){{
List<String> attachments = new ArrayList<String>() {{
add(context.getFilesDir() + "/" + "myCustomFile.txt");
}};

Expand All @@ -113,29 +133,28 @@ private BacktraceClient initializeBacktrace(final String submissionUrl) {
public native void cppCrash();

public native boolean registerNativeBreadcrumbs(BacktraceBase backtraceBase);

public native boolean addNativeBreadcrumb();

public native boolean addNativeBreadcrumbUserError();

public native void cleanupNativeBreadcrumbHandler();

private List<String> equippedItems;

public List<String> getWarriorArmor()
{
public List<String> getWarriorArmor() {
return new ArrayList<String>(Arrays.asList("Tough Boots", "Strong Sword", "Sturdy Shield", "Magic Wand"));
}

int findEquipmentIndex(List<String> armor, String equipment)
{
int findEquipmentIndex(List<String> armor, String equipment) {
return armor.indexOf(equipment);
}

void removeEquipment(List<String> armor, int index)
{
void removeEquipment(List<String> armor, int index) {
armor.remove(index);
}

void equipItem(List<String> armor, int index)
{
void equipItem(List<String> armor, int index) {
equippedItems.add(armor.get(index));
}

Expand All @@ -154,7 +173,7 @@ public void handledException(View view) {

public void getSaveData() throws IOException {
// I know for sure this file is there (spoiler alert, it's not)
File mySaveData = new File("mySave.sav");
File mySaveData = new File("mySave.sav");
FileReader mySaveDataReader = new FileReader(mySaveData);
char[] saveDataBuffer = new char[255];
mySaveDataReader.read(saveDataBuffer);
Expand Down Expand Up @@ -202,7 +221,7 @@ private void writeMyCustomFile(String filePath) {
outputStreamWriter.write(fileData);
outputStreamWriter.close();
} catch (IOException e) {
Log.e("BacktraceAndroid", "File write failed due to: " + e.toString());
Log.e("BacktraceAndroid", "File write failed due to: " + e.toString());
}
}

Expand Down
8 changes: 7 additions & 1 deletion example-app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<View
android:id="@+id/viewBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorWhite"/>
<Button
android:id="@+id/handledException"
android:layout_width="wrap_content"
Expand Down
1 change: 1 addition & 0 deletions example-app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
<color name="colorWhite">#FFFFFF</color>
</resources>