Android NDK 비정상 종료 보고서 받기

Android 앱에 네이티브 라이브러리가 포함되어 있는 경우 Firebase Crashlytics에서 앱의 빌드 구성을 소규모 업데이트하여 네이티브 코드에 대한 전체 스택 트레이스와 상세한 비정상 종료 보고서를 사용할 수 있습니다.

이 가이드에서는 NDK용 Firebase Crashlytics SDK를 사용하여 비정상 종료 보고를 구성하는 방법을 설명합니다.

Unity 프로젝트에서 Crashlytics를 시작하는 방법을 알아보려면 Unity 시작 가이드를 참조하세요.

시작하기 전에

  1. 아직 추가하지 않았다면 Android 프로젝트에 Firebase를 추가합니다. Android 앱이 없다면 샘플 앱을 다운로드하면 됩니다.

  2. 권장: 비정상 종료, 심각하지 않은 문제 또는 ANR 이벤트로 이어지는 사용자 작업을 파악하기 위해 탐색경로 로그를 자동으로 가져오려면 Firebase 프로젝트에서 Google Analytics를 사용 설정해야 합니다.

    • 기존 Firebase 프로젝트에 Google Analytics가 사용 설정되어 있지 않은 경우 Firebase Console의 > 프로젝트 설정에 있는 통합에서 Google Analytics를 사용 설정할 수 있습니다.

    • 새 Firebase 프로젝트를 만드는 경우 프로젝트 생성 워크플로에서 Google Analytics를 사용 설정합니다.

  3. 앱에 다음과 같은 최소 필수 버전이 있는지 확인합니다.

    • Gradle 8.0
    • Android Gradle 플러그인 8.1.0
    • Google 서비스 Gradle 플러그인 4.4.1

1단계: 앱에 NDK용 Crashlytics SDK 추가

모듈(앱 수준) Gradle 파일(일반적으로 <project>/<app-module>/build.gradle.kts 또는 <project>/<app-module>/build.gradle)에서 Android용 Crashlytics NDK 라이브러리의 종속 항목을 추가합니다. 라이브러리 버전 관리 제어에는 Firebase Android BoM을 사용하는 것이 좋습니다.

Crashlytics 사용 환경을 최적화하려면 Firebase 프로젝트에서 Google Analytics를 사용 설정하고 Google 애널리틱스용 Firebase SDK를 앱에 추가하는 것이 좋습니다.

dependencies {
    // Import the BoM for the Firebase platform
    implementation(platform("com.google.firebase:firebase-bom:33.6.0"))

    // Add the dependencies for the Crashlytics NDK 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")
}

Firebase Android BoM을 사용하면 앱에서 항상 호환되는 Firebase Android 라이브러리 버전만 사용합니다.

(대안) BoM을 사용하지 않고 Firebase 라이브러리 종속 항목을 추가합니다.

Firebase BoM을 사용하지 않도록 선택한 경우에는 종속 항목 줄에 각 Firebase 라이브러리 버전을 지정해야 합니다.

앱에서 여러 Firebase 라이브러리를 사용하는 경우 모든 버전이 호환되도록 BoM을 사용하여 라이브러리 버전을 관리하는 것이 좋습니다.

dependencies {
    // Add the dependencies for the Crashlytics NDK and Analytics libraries
    // When NOT using the BoM, you must specify versions in Firebase library dependencies
    implementation("com.google.firebase:firebase-crashlytics-ndk:19.2.1")
    implementation("com.google.firebase:firebase-analytics:22.1.2")
}
Kotlin 전용 라이브러리 모듈을 찾고 계신가요? 2023년 10월(Firebase BoM 32.5.0)부터 Kotlin 및 Java 개발자 모두 기본 라이브러리 모듈을 사용할 수 있습니다. 자세한 내용은 이 이니셔티브에 관한 FAQ를 참조하세요.

2단계: 앱에 Crashlytics Gradle 플러그인 추가

  1. 루트 수준(프로젝트 수준) Gradle 파일(<project>/build.gradle.kts 또는 <project>/build.gradle)에서 Crashlytics Gradle 플러그인을 plugins 블록에 추가합니다.

    Kotlin

    plugins {
        // Make sure that you have the AGP plugin 8.1+ dependency
        id("com.android.application") version "8.1.4" apply false
        // ...
    
        // Make sure that you have the Google services Gradle plugin 4.4.1+ dependency
        id("com.google.gms.google-services") version "4.4.2" apply false
    
        // Add the dependency for the Crashlytics Gradle plugin
        id("com.google.firebase.crashlytics") version "3.0.2" apply false
    }

    Groovy

    plugins {
        // Make sure that you have the AGP plugin 8.1+ dependency
        id 'com.android.application' version '8.1.4' apply false
        // ...
    
        // Make sure that you have the Google services Gradle plugin 4.4.1+ dependency
        id 'com.google.gms.google-services' version '4.4.2' apply false
    
        // Add the dependency for the Crashlytics Gradle plugin
        id 'com.google.firebase.crashlytics' version '3.0.2' apply false
    }
  2. 모듈(앱 수준) Gradle 파일(일반적으로 <project>/<app-module>/build.gradle.kts 또는 <project>/<app-module>/build.gradle)에서 Crashlytics Gradle 플러그인을 추가합니다.

    Kotlin

    plugins {
      id("com.android.application")
      // ...
    
      // Make sure that you have the Google services Gradle plugin
      id("com.google.gms.google-services")
    
      // Add the Crashlytics Gradle plugin
      id("com.google.firebase.crashlytics")
    }

    Groovy

    plugins {
      id 'com.android.application'
      // ...
    
      // Make sure that you have the Google services Gradle plugin
      id 'com.google.gms.google-services'
    
      // Add the Crashlytics Gradle plugin
      id 'com.google.firebase.crashlytics'
    }

3단계: 빌드에 Crashlytics 확장 프로그램 추가

모듈(앱 수준) Gradle 파일(일반적으로 <project>/<app-module>/build.gradle.kts 또는 <project>/<app-module>/build.gradle)에서 Crashlytics 확장 프로그램을 구성합니다.

Kotlin

import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension

// ...

android {
  // ...
  buildTypes {
      getByName("release") {
          // Add this extension
          configure<CrashlyticsExtension> {
              // Enable processing and uploading of native symbols to Firebase 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
          }
      }
  }
}

Groovy

// ...

android {
  // ...
  buildTypes {
      release {
          // Add this extension
          firebaseCrashlytics {
              // Enable processing and uploading of native symbols to Firebase 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
          }
      }
  }
}

4단계: 네이티브 기호 자동 업로드 설정

NDK 비정상 종료로부터 읽기 가능한 스택 트레이스를 생성하려면 Crashlytics에서 네이티브 바이너리의 기호에 대해 파악해야 합니다. Crashlytics Gradle 플러그인에는 이 과정을 자동화하는 uploadCrashlyticsSymbolFileBUILD_VARIANT 태스크가 포함되어 있습니다.

  1. 자동화된 기호 업로드를 위한 태스크에 액세스할 수 있도록 모듈(앱 수준) Gradle 파일에서 nativeSymbolUploadEnabledtrue로 설정되어 있는지 확인합니다.

  2. 스택 트레이스에 나타나는 메서드 이름의 경우 NDK 라이브러리를 빌드할 때마다 uploadCrashlyticsSymbolFileBUILD_VARIANT 태스크를 명시적으로 호출해야 합니다. 예를 들면 다음과 같습니다.

    >./gradlew app:assembleBUILD_VARIANT\
               app:uploadCrashlyticsSymbolFileBUILD_VARIANT
  3. NDK용 Crashlytics SDK와 Crashlytics Gradle 플러그인은 모두 네이티브 공유 객체 내에 GNU 빌드 ID가 있어야 합니다.

    각 바이너리에서 readelf -n을 실행하여 이 ID가 있는지 확인할 수 있습니다. 빌드 ID가 없는 경우 빌드 시스템의 플래그에 -Wl,--build-id를 추가하여 문제를 해결하세요.

5단계: 테스트 비정상 종료를 강제로 적용하여 설정 완료

Crashlytics 설정을 완료하고 Firebase Console의 Crashlytics 대시보드에 초기 데이터를 표시하려면 테스트 비정상 종료를 강제로 적용해야 합니다.

  1. 테스트 비정상 종료를 강제로 적용하는 데 사용할 수 있는 코드를 앱에 추가합니다.

    앱의 MainActivity에서 다음 코드를 사용하여, 누르면 비정상 종료를 일으키는 버튼을 앱에 추가할 수 있습니다. 버튼에 'Test Crash'라는 라벨이 지정되어 있습니다.

    Kotlin+KTX

    val crashButton = Button(this)
    crashButton.text = "Test Crash"
    crashButton.setOnClickListener {
       throw RuntimeException("Test Crash") // Force a crash
    }
    
    addContentView(crashButton, ViewGroup.LayoutParams(
           ViewGroup.LayoutParams.MATCH_PARENT,
           ViewGroup.LayoutParams.WRAP_CONTENT))

    Java

    Button crashButton = new Button(this);
    crashButton.setText("Test Crash");
    crashButton.setOnClickListener(new View.OnClickListener() {
       public void onClick(View view) {
           throw new RuntimeException("Test Crash"); // Force a crash
       }
    });
    
    addContentView(crashButton, new ViewGroup.LayoutParams(
           ViewGroup.LayoutParams.MATCH_PARENT,
           ViewGroup.LayoutParams.WRAP_CONTENT));
  2. 앱을 빌드하고 실행합니다.

  3. 앱의 첫 번째 비정상 종료 보고서를 전송하려면 테스트 비정상 종료를 강제로 적용합니다.

    1. 테스트 기기 또는 에뮬레이터에서 앱을 엽니다.

    2. 앱에서 위 코드를 사용하여 추가한 'Test Crash' 버튼을 누릅니다.

    3. 앱이 비정상 종료된 후에는 앱을 다시 시작하여 비정상 종료 보고서를 Firebase에 전송할 수 있도록 합니다.

  4. Firebase Console의 Crashlytics 대시보드로 이동하여 테스트 비정상 종료를 확인합니다.

    Console을 새로고침하고 5분 후에도 테스트 비정상 종료가 표시되지 않으면 디버그 로깅을 사용 설정하여 앱에서 비정상 종료 보고서를 전송하는지 확인합니다.


모든 작업이 완료되었습니다. 이제 Crashlytics에서 앱의 비정상 종료를 모니터링합니다. Crashlytics 대시보드에서 비정상 종료 보고서와 통계를 확인하고 조사할 수 있습니다.

다음 단계

  • (권장) GWP-ASan 보고서를 수집하여 네이티브 메모리 오류로 인한 비정상 종료 디버깅 지원을 받으세요. 이러한 메모리 관련 오류는 앱 보안 취약점의 주요 원인인 앱 내의 메모리 손상과 관련이 있을 수 있습니다. 이 디버깅 기능을 활용하려면 앱에서 GWP-ASan을 명시적으로 사용 설정하고 NDK용 최신 Crashlytics SDK(v18.3.6 이상 또는 Firebase BoM v31.3.0 이상)를 사용해야 합니다.

  • 보고 선택 옵션, 로그, 키를 추가하고 심각하지 않은 오류를 추적하여 비정상 종료 보고서 설정을 맞춤설정하세요.

  • Google Play와 통합하여 Crashlytics 대시보드에서 직접 Android 앱의 비정상 종료 보고서를 Google Play 트랙별로 필터링할 수 있도록 합니다. 이렇게 하면 특정 빌드에 대시보드를 더 집중할 수 있습니다.

문제 해결

Firebase Console과 logcat에 서로 다른 스택 트레이스가 보이는 경우 문제 해결 가이드를 참조하세요.



기호 업로드를 위한 대체 옵션

이 페이지 상단의 기본 워크플로는 표준 Gradle 빌드에 적용할 수 있습니다. 그러나 일부 앱은 다른 구성 또는 도구(예: Gradle 이외의 빌드 프로세스)를 사용합니다. 이러한 상황에서는 다음 옵션을 사용하면 기호를 성공적으로 업로드하는 데 도움이 될 수 있습니다.

옵션: 라이브러리 모듈 및 외부 종속 항목 기호 업로드

이 옵션은 다음과 같은 상황에서 도움이 될 수 있습니다.

  • Gradle 내에서 맞춤설정된 NDK 빌드 프로세스를 사용하는 경우
  • 네이티브 라이브러리가 라이브러리/기능 모듈에서 빌드되었거나 서드 파티에서 제공되는 경우
  • 자동 기호 업로드 태스크가 실패하거나 대시보드에 기호화되지 않은 비정상 종료가 표시되는 경우

옵션: Gradle 이외의 빌드 또는 액세스할 수 없는 삭제되지 않은 네이티브 라이브러리의 기호 업로드

이 옵션은 다음과 같은 상황에서 도움이 될 수 있습니다.

  • Gradle 이외의 빌드 프로세스를 사용하는 경우

  • 삭제되지 않은 네이티브 라이브러리가 Gradle 빌드 중에 액세스할 수 없는 방식으로 제공된 경우