[FIXED] Firebase Crashlytics Android NDK: empty symbols on crash reports

Issue

I have a project in Android Studio that consist on Android Java Services that uses some native libraries through JNI calls.

Basically, I have 2 libraries I compile and another library that is precompiled, so I don’t have access to the source code. As the precompiled library is only built for armeabi-v7a, I have an abiFilter.

Here, my /build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        google()
        jcenter()

        maven { url 'https://maven.fabric.io/public' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.2'
        classpath 'io.fabric.tools:gradle:1.31.2'
        classpath 'com.google.gms:google-services:4.3.3'  // Google Services plugin

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()

    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

and my /app/build.gradle (omitted the sensible code)

apply plugin: 'com.android.application'
apply plugin: 'io.fabric'

def enableCrashlyticsNdk = true

repositories {
    jcenter()
    maven { url 'https://maven.fabric.io/public' }
}

android {
    signingConfigs {
        platformSignature {
            keyAlias "${platform_keystore_alias}"
            keyPassword "${platform_keystore_password}"
            storeFile file("${platform_keystore_path}")
            storePassword "${platform_keystore_password}"
        }
    }

    compileSdkVersion 29
    buildToolsVersion "29.0.2"

    lintOptions {
        abortOnError false
    }

    defaultConfig {
        applicationId com.example.stackoverflowapp
        minSdkVersion 26
        targetSdkVersion 28
        versionCode 1
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11 -Werror"
            }
        }
        ndk {
            // Specifies the ABI configurations of your native
            // libraries Gradle should build and package with your APK.
            abiFilters 'armeabi-v7a'
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            jniDebuggable false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.platformSignature
        }
        debug {
            debuggable true
            jniDebuggable true
            versionNameSuffix = " (debug)"
            signingConfig signingConfigs.platformSignature
        }
    }

    android.applicationVariants.all { variant ->
        variant.outputs.all {
            outputFileName = buildOutputName(variant)
        }
    }

    packagingOptions {
        exclude 'jsr305_annotations/Jsr305_annotations.gwt.xml'
        exclude 'error_prone/Annotations.gwt.xml'
        exclude 'third_party/java_src/error_prone/project/annotations/Annotations.gwt.xml'
        exclude 'third_party/java_src/error_prone/project/annotations/Google_internal.gwt.xml'
    }

    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

ext {
    appCompatVersion = '28.0.0'
}

crashlytics {
    enableNdk enableCrashlyticsNdk
}

tasks.whenTaskAdded { task ->
    if (enableCrashlyticsNdk && task.name.startsWith('assemble')) {
        task.finalizedBy "crashlyticsUploadSymbols" + task.name.substring('assemble'.length())
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    implementation 'androidx.leanback:leanback:1.0.0'
    implementation 'androidx.appcompat:appcompat:1.0.0'
    implementation 'com.google.android.exoplayer:exoplayer:r1.5.14'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
    implementation 'com.google.code.gson:gson:2.8.5'

    implementation 'com.google.firebase:firebase-analytics:17.2.1'
    implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
    implementation 'com.crashlytics.sdk.android:crashlytics-ndk:2.1.1'
}

apply plugin: 'com.google.gms.google-services'  // Google Play services Gradle plugin

As my app is part of the Android system, it is signed with a platform signature.

I compile my 2 libraries with CMake.

I have followed all steps from the firebase web:

  1. https://firebase.google.com/docs/android/setup
  2. https://firebase.google.com/docs/crashlytics/get-started?platform=android
  3. https://firebase.google.com/docs/crashlytics/ndk-reports

I’ve added a crash intentionally within one of my libraries and when the app reaches crash, The Crashlytics gathered the crash and upload the crash report successfully.

CrashlyticsCore: Crashlytics report upload complete: 5DE66A450116-0001-1A8B-A3EE77BA9366

Then, when I go to the firebase console, I see that all stack frames are (missing)

Crashed: Thread #1
SIGSEGV 0x0000000000000028
-------------------------------------------
0 MyApp.apk (Missing)
1 libart.so (Missing)
2 (Missing)
3 (Missing)
4 (Missing)
5 (Missing)
6 (Missing)
7 libart.so (Missing)
8 libart.so (Missing)
9 libart.so (Missing)
10 (Missing)
11 libart.so (Missing)
12 (Missing)

Reviewing the Crashlytics build logs, it seems like the cSym files are uploaded properly.

[DEBUG] (Execution worker for ':' Thread 6) com.crashlytics  - cSYM file(s) uploaded.

I’m not really sure if the symbols are properly created and uploaded or if there is an issue with the crash reports created by the device. I’m afraid of the problem would be related with android permissions as my app is a system app.

I guess I’ve read all the stackoverflow posts related with Crashlytics and NDK. Also, I’ve also googled any kind of combination related with “crashlytics, ndk and symbols”.

Finally, the tool versions I’m using:

  • Android Studio: 3.5.1
  • Gradle: 5.4.1
  • Gradle plugin: 3.5.2
  • Fabric gradle plugin: 1.31.2

Thank you so much.

Solution

You may want to consider upgrading to the new (non fabric) Crashlytics SDK. It is, assumedly, the roadmap replacement for fabric.

https://firebase.google.com/docs/crashlytics/upgrade-sdk?platform=android

and

https://firebase.google.com/docs/crashlytics/ndk-reports-new-sdk

While it is in Beta as of this writing, I recently converted to it fairly easily. In the process I was also able to stop using the fabric native library (libCrashlytics) and its header in my native code.

Additionally, ensure you are properly uploading the symbols to Firebase / Fabric as part of your build process. You may need to do this explicitly as part of an afterEvaluate block like so:

afterEvaluate {
    if (gradle.startParameter.taskNames.contains(":app:assemble<Flavor><BuildType>")) {
        assemble<Flavor><BuildType>.finalizedBy(uploadCrashlyticsSymbolFile<Flavor><BuildType>)
    }
}

Be sure to replace <Flavor> and <BuildType> with the flavor and build type(s) you defined in your productFlavors and buildTypes blocks.

For example, the below flavor / build types:

buildTypes {
    release {
        signingConfig signingConfigs.release
        buildConfigField "boolean", "RELEASE", "true"
        shrinkResources true
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
    debug {
        buildConfigField "boolean", "RELEASE", "false"
        applicationIdSuffix '.debug'
        versionNameSuffix '-DEBUG'
        ext.alwaysUpdateBuildId = false
        crunchPngs false
    }
}

flavorDimensions "all"

productFlavors {
    fat {
        ndk {
            abiFilters "x86_64", "x86", "arm64-v8a", "armeabi-v7a"
        }
        dimension "all"
    }
}

would result in assembleFatDebug and assembleFatRelease tasks that you’d need to finalize with uploadCrashlyticsSymbolFileFatDebug and uploadCrashlyticsSymbolFileFatRelease tasks accordingly.

NOTE:
If you are still using Fabric, the task you want to finalize your assemble* task with is crashlyticsUploadSymbols<Flavor><BuildType>.

Answered By – ShellDude

Answer Checked By – Senaida (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published