[SOLVED] Does anyone have Firebase Crashlytics successfully logging NDK crashes

I’ve used Crashlytics in other projects so am reasonably familiar with it.

For my JUCE project, I have setup Crashlytics following the relevant guides for Installing Crashlytics and enabling NDK crash monitoring.

Also from this forum I checked out:

When I run up my app in Android Studio, Crashlytics seems to start up okay:

2020-11-17 17:23:11.361 19708-19708/com.cburn.revdesktop I/FirebaseCrashlytics: Initializing Crashlytics 17.3.0
2020-11-17 17:23:11.457 19708-19730/com.cburn.revdesktop D/libcrashlytics: Initializing libcrashlytics version 3.0.0
2020-11-17 17:23:11.457 19708-19730/com.cburn.revdesktop D/libcrashlytics: Initializing native crash handling successful.
2020-11-17 17:23:11.458 19708-19730/com.cburn.revdesktop I/FirebaseCrashlytics: Crashlytics NDK initialization successful

And if I throw an exception on the java side e.g.

public class JuceActivity   extends Activity {

    private native void appNewIntent(Intent intent);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        throw new RuntimeException("JuceActivity test exception");

    }

Then I can see the crash both in Logcat :

2020-11-17 17:56:08.125 21292-21376/com.cburn.revdesktop D/libcrashlytics: Initializing libcrashlytics version 3.0.0
2020-11-17 17:56:08.125 21292-21376/com.cburn.revdesktop D/libcrashlytics: Initializing native crash handling successful.
2020-11-17 17:56:08.125 21292-21376/com.cburn.revdesktop I/FirebaseCrashlytics: Crashlytics NDK initialization successful
2020-11-17 17:56:08.416 21292-21379/com.cburn.revdesktop D/TransportRuntime.SQLiteEventStore: Storing event with priority=HIGHEST, name=FIREBASE_CRASHLYTICS_REPORT for destination cct

and immediately afterwards :

2020-11-17 17:56:14.361 21396-21421/com.cburn.revdesktop D/TransportRuntime.CctTransportBackend: Making request to: https://crashlyticsreports-pa.googleapis.com/v1/firelog/legacy/batchlog

The crash then appears correctly in the Firebase Crashlytics console.

However when I generate a NDK crash in a .cpp e.g by calling :

void crashTest() {
int a=0;
int b=1;
printf(“%d”,b/a);
}

My JUCE app crashes as expected but I don’t get any of the above logging to indicate that Crashlytics caught the crash, nor is the crash uploaded to the Firebase console.

My question is - Is there anything else I need to do on the JUCE side to get Crashlytics to catch things.

My build environment is

  • JUCE 5.4.7
  • Android Studio 4.1.1
  • Gradle 6.5
  • Android gradle plugin 4.1.1

In my project level build.gradle :

dependencies {
   ...
   classpath 'com.google.gms:google-services:4.3.4'
   classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
}

And in the app build.gradle :

apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
.
.
.

dependencies {

    ...

    // Import the BoM for the Firebase platform
    implementation platform('com.google.firebase:firebase-bom:26.1.0')

    // Declare the dependencies for the Crashlytics and Analytics libraries
    // When using the BoM, you don't specify versions in Firebase library dependencies
    implementation 'com.google.firebase:firebase-crashlytics-ndk'
    implementation 'com.google.firebase:firebase-analytics'             
}

  debug_ {
              
              ....
              
              // Add this extension
              firebaseCrashlytics {
                  // Enable processing and uploading of native symbols to Crashlytics servers.
                  // By default, this is disabled to improve build speeds.
                  // This flag must be enabled to see properly-symbolicated native
                  // stack traces in the Crashlytics dashboard.
                  nativeSymbolUploadEnabled true
              }

          }
      }

After a fair bit of mucking around finally got this working.

Gotchas

There are a few things either the docs don’t tell you or don’t stress enough.

Gotcha #1

The most important is that you tell Firebase Crashlytics where to find your compiled libraries and hence the symbols.

For me this was:

Debug libraries

build/intermediates/merged_native_libs/debug_Debug/out/lib

Release libraries

build/intermediates/merged_native_libs/release_Release/out/lib


Gotcha #2

There was an issue get symbols for release builds in recent versions of Firebase Crashyltics Android SDK as described here:

This is now fixed and the working versions of the relevant files I used are:

  • gradle wrapper v6.5
  • gradle android plugin 4.1.1
  • gradle firebase-crashlytics plugin 2.4.1
  • google servers plugin 4.3.4
  • com.google.firebase:firebase-analytics:17.5.0
  • com.google.firebase:firebase-crashlytics-ndk:17.3.0



Getting it working

In my project-level build.gradle I have:

buildscript {
   repositories {
       google()
       jcenter()
   }
   dependencies {
       classpath 'com.android.tools.build:gradle:4.1.1'
       // Check that you have the Google Services Gradle plugin v4.3.2 or later
       // (if not, add it).
       classpath 'com.google.gms:google-services:4.3.4'

       // Add the Crashlytics Gradle plugin (be sure to add version
       // 2.0.0 or later if you built your app with Android Studio 4.1).
       classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'

   }
}

And in my module-level *build.gradle* I have:
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'

android {
 .
 .
 .

	dependencies {
		.
		.
		.
		
		implementation 'com.google.firebase:firebase-analytics:17.5.0'
		implementation 'com.google.firebase:firebase-crashlytics-ndk:17.3.0'
        }

	buildTypes {
		debug  {
		    // Add this extension
		    firebaseCrashlytics {
		      // Enable processing and uploading of native symbols to Crashlytics servers.
		      // By default, this is disabled to improve build speeds.
		      // This flag must be enabled to see properly-symbolicated native
		      // stack traces in the Crashlytics dashboard.
		      nativeSymbolUploadEnabled true
		      unstrippedNativeLibsDir 'build/intermediates/merged_native_libs/debug_Debug/out/lib'
		      strippedNativeLibsDir 'build/intermediates/stripped_native_libs/debug_Debug/out/lib'
		    }
		}
		release  {
		    // Add this extension
		    firebaseCrashlytics {
			// Enable processing and uploading of native symbols to Crashlytics servers.
			// By default, this is disabled to improve build speeds.
			// This flag must be enabled to see properly-symbolicated native
			// stack traces in the Crashlytics dashboard.
		       nativeSymbolUploadEnabled true
		       unstrippedNativeLibsDir 'build/intermediates/merged_native_libs/release_Release/out/lib'
		       strippedNativeLibsDir 'build/intermediates/stripped_native_libs/release_Release/out/lib'
		    }
		}
	}
 
 . 
 .
 .

}

(as well as the other usual gradle stuff generated by Projucer.)

Build steps

The build process is slightly different for debug vs. release builds.

Debug builds

./gradlew clean
./gradlew assembleDebug
./gradlew uploadCrashlyticsSymbolFileDebug

Release builds

./gradlew clean
./gradlew assembleRelease

Then sign the .apk from Android Studio using Build —> Generate Bundle / Signed APK

Then upload the symbols:

./gradlew uploadCrashlyticsSymbolFileRelease

Follow these steps and I claim you’ll be able to generate a crash in your app, then when you restart your app the details will be uploaded. Wait a few minutes and it’ll pop up in the Firebase console.

Notes & references

Studying the following links got me going:

Some additional steps for me that helped understand how it worked was to add an intermediate step before uploading

./gradlew generateCrashlyticsSymbolFileRelease

I could then check that the .cSYM symbol file was correctly generated in:

build/crashlytics/Release_Release/nativeSymbols

This file has a build-id which must match the build-id in your lib_jucejni.so

You can check this by comparing the id in the .cSYM file name with the output from:

readelf -n app/build/intermediates/merged_native_libs/release_Release/out/lib/armeabi-v7a/libjuce_jni.so

Conclusion

It’s early days, but it looks like Firebase Crashlytics can work well with JUCE to generate stacktraces for apps in the field. This is very useful because SystemStats::getStackBacktrace is not yet implemented for Android.

p.s. can anyone tell me how I add a [SOLVED] to this topic title.

I think only frequent posters are allowed to edit their own & other members topic titles. I edited it for you… Although I’m not in the Android business, thank for sharing your results in such a detailed way!