返回

Performance Optimization (XIII): Unraveling the Enigma of Native Crashes with Breakpad

Android

In the realm of contemporary Android development, the utilization of dynamic native libraries is ubiquitous. This is especially true for third-party libraries such as map SDKs, multimedia SDKs, and proprietary SDKs. Amidst this ubiquitous adoption, a fundamental question arises: what recourse do we have when users report app crashes and there are no corresponding logs relayed to our servers?

The Breakpad Solution

Breakpad, a potent crash reporting tool, emerges as the panacea for this conundrum. This invaluable utility empowers developers to decipher the root causes of native crashes, even in the absence of explicit user reports. By seamlessly integrating Breakpad into our apps, we can harness its capabilities to:

  • Capture Crash Reports: Breakpad acts as an indefatigable sentinel, vigilantly monitoring app executions. When a native crash occurs, it captures a detailed snapshot of the crash context, providing invaluable insights into the crash's origin.

  • Generate Minidump Files: Breakpad meticulously generates minidump files, concise yet information-rich representations of the app's memory state at the moment of the crash. These files serve as treasure troves of data, enabling developers to pinpoint the exact location and cause of the crash.

  • Upload Crash Reports: Breakpad seamlessly uploads crash reports and minidump files to designated servers, ensuring that developers have timely access to crucial diagnostic information.

Implementing Breakpad

Integrating Breakpad into your Android app is a straightforward endeavor, requiring minimal effort. The following steps provide a succinct guide:

  1. Add Breakpad Dependency: Begin by adding the Breakpad dependency to your app's build.gradle file:
dependencies {
    implementation 'com.google.firebase:firebase-crashlytics:18.2.13'
}
  1. Initialize Crashlytics: Initialize Crashlytics to activate Breakpad's crash reporting capabilities:
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true);
  1. Handle Native Crashes: Native crashes can be handled using the following code snippet:
#include <android/log.h>
#include <breakpad/client/linux/handler/minidump_descriptor.h>
#include <breakpad/client/linux/handler/minidump_generator.h>
#include <breakpad/client/linux/handler/signal_handler.h>
#include <cxxabi.h>
#include <cstdio>
#include <cstring>

using namespace google_breakpad;

// Custom crash handler function
void DumpAndExit(void *context, const char *filename) {
  MinidumpDescriptor descriptor(filename);
  MinidumpGenerator generator(descriptor);
  generator.DumpFromContext(context);
  exit(1);
}

// Signal handler to register custom crash handler
void SignalHandler(int signal, siginfo_t *info, void *reserved) {
  char name[256];
  int status;
  // Print signal name
  const char *signal_name = strsignal(signal);
  __android_log_print(ANDROID_LOG_ERROR, "Signal", "Signal %d (%s) received.",
                    signal, signal_name ? signal_name : "Unknown");
  // Print signal code
  const char *code_name = nullptr;
  switch (info->si_code) {
    case SI_USER:
      code_name = "SI_USER";
      break;
    case SI_KERNEL:
      code_name = "SI_KERNEL";
      break;
    case SI_QUEUE:
      code_name = "SI_QUEUE";
      break;
    case SI_TIMER:
      code_name = "SI_TIMER";
      break;
    case SI_MESGQ:
      code_name = "SI_MESGQ";
      break;
    case SI_ASYNCIO:
      code_name = "SI_ASYNCIO";
      break;
    case SI_SIGIO:
      code_name = "SI_SIGIO";
      break;
    case SI_TKILL:
      code_name = "SI_TKILL";
      break;
    case SI_DETHREAD:
      code_name = "SI_DETHREAD";
      break;
    case SI_SEGV_MAPERR:
      code_name = "SI_SEGV_MAPERR";
      break;
    case SI_SEGV_ACCERR:
      code_name = "SI_SEGV_ACCERR";
      break;
    case SI_BUS_ADRALN:
      code_name = "SI_BUS_ADRALN";
      break;
    case SI_BUS_OBJERR:
      code_name = "SI_BUS_OBJERR";
      break;
    case SI_BUS_MCEERR_AR:
      code_name = "SI_BUS_MCEERR_AR";
      break;
    case SI_BUS_MCEERR_AO:
      code_name = "SI_BUS_MCEERR_AO";
      break;
    case SI_KILL:
      code_name = "SI_KILL";
      break;
    case SI_STOP:
      code_name = "SI_STOP";
      break;
    case SI_TRAP:
      code_name = "SI_TRAP";
      break;
    case SI_CHLD:
      code_name = "SI_CHLD";
      break;
    case SI_FAULT:
      code_name = "SI_FAULT";
      break;
    case SI_POLL:
      code_name = "SI_POLL";
      break;
    case SI_PROF:
      code_name = "SI_PROF";
      break;
    case SI_SYS:
      code_name = "SI_SYS";
      break;
    case SI_RT:
      code_name = "SI_RT";
      break;
    default:
      code_name = "Unknown";
      break;
  }
  __android_log_print(ANDROID_LOG_ERROR, "Signal",
                    "Signal code: %d (%s)", info->si_code,
                    code_name ? code_name : "Unknown");
  // Print signal address
  __android_log_print(ANDROID_LOG_ERROR, "Signal", "Signal address: %p",
                    info->si_addr);
  // Print mangled signal handler name
  abi::__cxa_demangle(info->si_handler, name, &status, nullptr);
  __android_log_print(ANDROID_LOG_ERROR, "Signal", "Signal handler: %s", name);
  // Install a custom crash handler
  InstallCustomCrashHandler(DumpAndExit);
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_nativeCrash(
    JNIEnv *env, jclass type) {
  // Trigger a segmentation fault
  int *p = nullptr;
  *p = 10;
}
  1. Configure Proguard: Ensure that Breakpad symbols are excluded from Proguard optimizations by adding the following line to your app's proguard-rules.pro file:
-keep class com.google.breakpad.** { *; }

Conclusion

Breakpad has emerged as an indispensable tool for developers seeking to unravel the mysteries of native crashes. By harnessing its capabilities, we can significantly enhance our ability to diagnose and resolve these elusive issues, ultimately delivering a more stable and reliable user experience. The integration process is straightforward and requires minimal effort, making it a worthwhile investment for any Android developer.