Skip to content

Commit f4d1987

Browse files
Replace Breakpad with Crashpad (#2133)
* Remove breakpad. Integrate Crashpad (#316) Co-authored-by: Matt Willis <[email protected]>
1 parent 1fb6c62 commit f4d1987

File tree

62 files changed

+1226
-744
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1226
-744
lines changed

.gitmodules

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
[submodule "firebase-crashlytics-ndk/src/third_party/breakpad"]
2-
path = firebase-crashlytics-ndk/src/third_party/breakpad
3-
url = https://chromium.googlesource.com/breakpad/breakpad
1+
[submodule "firebase-crashlytics-ndk/src/third_party/crashpad"]
2+
path = firebase-crashlytics-ndk/src/third_party/crashpad
3+
url = https://chromium.googlesource.com/crashpad/crashpad.git
4+
[submodule "firebase-crashlytics-ndk/src/third_party/mini_chromium"]
5+
path = firebase-crashlytics-ndk/src/third_party/mini_chromium
6+
url = https://chromium.googlesource.com/chromium/mini_chromium
47
[submodule "firebase-crashlytics-ndk/src/third_party/lss"]
58
path = firebase-crashlytics-ndk/src/third_party/lss
6-
url = https://chromium.googlesource.com/linux-syscall-support
9+
url = https://chromium.googlesource.com/linux-syscall-support.git

firebase-crashlytics-ndk/firebase-crashlytics-ndk-proguard.txt

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@
1515
# Prevent FirebaseCrashlytics from being obfuscated, because the Crashlytics
1616
# native libraries call FirebaseCrashlytics public methods dynamically via JNI.
1717
-keep public class com.google.firebase.crashlytics.FirebaseCrashlytics { public *; }
18+
-keep public class com.google.firebase.crashlytics.ndk.CrashpadMain { public *; }

firebase-crashlytics-ndk/firebase-crashlytics-ndk.gradle

+55-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
plugins {
1616
id 'firebase-library'
17+
id 'LicenseResolverPlugin'
1718
}
1819

1920
firebaseLibrary {
@@ -27,12 +28,17 @@ android {
2728
}
2829

2930
compileSdkVersion project.targetSdkVersion
30-
ndkVersion "17.2.4988734" // NDK r17c
3131
defaultConfig {
3232
minSdkVersion 16
3333
targetSdkVersion project.targetSdkVersion
3434
versionName version
3535

36+
externalNativeBuild {
37+
ndkBuild {
38+
arguments '-j4'
39+
}
40+
}
41+
3642
consumerProguardFiles 'firebase-crashlytics-ndk-proguard.txt'
3743
multiDexEnabled true
3844
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -48,11 +54,59 @@ android {
4854
targetCompatibility JavaVersion.VERSION_1_8
4955
}
5056

57+
packagingOptions {
58+
exclude './src/third_party/**'
59+
}
60+
5161
externalNativeBuild {
5262
ndkBuild {
5363
path "./src/main/jni/Android.mk"
5464
}
5565
}
66+
67+
libraryVariants.all { variant ->
68+
variant.outputs.each { output ->
69+
def func = fixTrampolineFilenames(variant.baseName)
70+
71+
tasks.findAll {
72+
it.name.startsWith("bundleReleaseAar")
73+
}.each {
74+
it.dependsOn func
75+
}
76+
77+
tasks.findAll {
78+
it.name.startsWith("externalNativeBuild")
79+
}.each {
80+
func.dependsOn it
81+
}
82+
}
83+
}
84+
}
85+
86+
import java.nio.file.Files
87+
88+
// There is not any normal way to package native executables in an Android APK.
89+
// It is normal to package native code as a loadable module but Android's APK
90+
// installer will ignore files not named like a shared object, so give the
91+
// handler executable an acceptable name
92+
def fixTrampolineFilenames(variantBaseName) {
93+
project.task("fixTrampolineFilenames${variantBaseName}").configure({
94+
}).doFirst {
95+
["x86", "x86_64", "armeabi-v7a", "arm64-v8a"].each { arch ->
96+
def initial = new File(
97+
"${buildDir}/intermediates/ndkBuild/${variantBaseName}/obj/local/${arch}/crashlytics-trampoline")
98+
def renamed = new File(
99+
"${buildDir}/intermediates/ndkBuild/${variantBaseName}/obj/local/${arch}/libcrashlytics-trampoline.so")
100+
101+
// There is no need to delete the original file, it will not be
102+
// packaged into the APK
103+
Files.copy(initial.toPath(), renamed.toPath())
104+
}
105+
}
106+
}
107+
108+
thirdPartyLicenses {
109+
add 'Crashpad', "file://${rootDir}/third_party/licenses/apache-2.0.txt"
56110
}
57111

58112
dependencies {

firebase-crashlytics-ndk/src/androidTest/java/com/google/firebase/crashlytics/ndk/BreakpadControllerTest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ private boolean setupTestFilesDirectory() {
7070

7171
public void testHasCrashDataForSession() throws IOException {
7272
assertTrue(setupTestFilesDirectory());
73-
assertTrue(new File(testFilesDirectory, "crash.dmp").createNewFile());
73+
final File pendingDirectory = new File(testFilesDirectory, "pending");
74+
assertTrue(pendingDirectory.mkdir());
75+
assertTrue(new File(pendingDirectory, "crash.dmp").createNewFile());
7476
final String sessionId = "test";
7577
when(mockFilesManager.hasSessionFileDirectory(sessionId)).thenReturn(true);
7678
when(mockFilesManager.getSessionFileDirectory(sessionId)).thenReturn(testFilesDirectory);

firebase-crashlytics-ndk/src/main/AndroidManifest.xml

-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
-->
2323
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2424
package="com.google.firebase.crashlytics.ndk" >
25-
2625
<application>
2726
<service android:name="com.google.firebase.components.ComponentDiscoveryService" android:exported="false">
2827
<meta-data android:name="com.google.firebase.components:com.google.firebase.crashlytics.ndk.CrashlyticsNdkRegistrar"

firebase-crashlytics-ndk/src/main/java/com/google/firebase/crashlytics/ndk/BreakpadController.java

+21-4
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,22 @@ public boolean finalizeSession(String sessionId) {
7979
@NonNull
8080
public SessionFiles getFilesForSession(String sessionId) {
8181
final File sessionFileDirectory = filesManager.getSessionFileDirectory(sessionId);
82+
final File sessionFileDirectoryForMinidump = new File(sessionFileDirectory, "pending");
83+
84+
Logger.getLogger()
85+
.d("Minidump directory: " + sessionFileDirectoryForMinidump.getAbsolutePath());
86+
87+
File minidump = getSingleFileWithExtension(sessionFileDirectoryForMinidump, ".dmp");
88+
89+
Logger.getLogger()
90+
.d("Minidump " + (minidump != null && minidump.exists() ? "exists" : "does not exist"));
91+
8292
final SessionFiles.Builder builder = new SessionFiles.Builder();
83-
if (sessionFileDirectory != null && sessionFileDirectory.exists()) {
93+
if (sessionFileDirectory != null
94+
&& sessionFileDirectory.exists()
95+
&& sessionFileDirectoryForMinidump.exists()) {
8496
builder
85-
.minidumpFile(getSingleFileWithExtension(sessionFileDirectory, ".dmp"))
86-
.binaryImagesFile(getSingleFileWithExtension(sessionFileDirectory, ".maps"))
97+
.minidumpFile(getSingleFileWithExtension(sessionFileDirectoryForMinidump, ".dmp"))
8798
.metadataFile(getSingleFileWithExtension(sessionFileDirectory, ".device_info"))
8899
.sessionFile(new File(sessionFileDirectory, SESSION_METADATA_FILE))
89100
.appFile(new File(sessionFileDirectory, APP_METADATA_FILE))
@@ -171,7 +182,13 @@ private static void writeTextFile(File file, String text) {
171182
/** Returns a single file matching the given extension from the given directory. */
172183
@Nullable
173184
private static File getSingleFileWithExtension(File directory, String extension) {
174-
for (File file : directory.listFiles()) {
185+
File[] files = directory.listFiles();
186+
187+
if (files == null) {
188+
return null;
189+
}
190+
191+
for (File file : files) {
175192
if (file.getName().endsWith(extension)) {
176193
return file;
177194
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.crashlytics.ndk;
16+
17+
public class CrashpadMain {
18+
public static void main(String[] args) {
19+
try {
20+
System.loadLibrary("crashlytics-handler");
21+
} catch (UnsatisfiedLinkError e) {
22+
throw new RuntimeException(e);
23+
}
24+
25+
crashpadMain(args);
26+
}
27+
28+
public static native void crashpadMain(String[] args);
29+
}

firebase-crashlytics-ndk/src/main/java/com/google/firebase/crashlytics/ndk/FirebaseCrashlyticsNdk.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ static FirebaseCrashlyticsNdk create(@NonNull Context context) {
3131
final File rootDir = new File(context.getFilesDir(), FILES_PATH);
3232

3333
final NativeComponentController controller =
34-
new BreakpadController(context, new JniNativeApi(), new NdkCrashFilesManager(rootDir));
34+
new BreakpadController(
35+
context, new JniNativeApi(context), new NdkCrashFilesManager(rootDir));
3536
return new FirebaseCrashlyticsNdk(controller);
3637
}
3738

firebase-crashlytics-ndk/src/main/java/com/google/firebase/crashlytics/ndk/JniNativeApi.java

+92-2
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,27 @@
1414

1515
package com.google.firebase.crashlytics.ndk;
1616

17+
import android.annotation.TargetApi;
18+
import android.content.Context;
19+
import android.content.pm.ApplicationInfo;
20+
import android.content.pm.PackageInfo;
21+
import android.content.pm.PackageManager;
22+
import android.content.pm.PackageManager.NameNotFoundException;
1723
import android.content.res.AssetManager;
24+
import android.os.Build;
25+
import android.text.TextUtils;
1826
import com.google.firebase.crashlytics.internal.Logger;
27+
import java.io.File;
28+
import java.util.ArrayList;
29+
import java.util.Collections;
30+
import java.util.List;
1931

2032
/** JNI implementation of the native API interface. */
2133
@SuppressWarnings("PMD.AvoidUsingNativeCode")
2234
class JniNativeApi implements NativeApi {
2335

2436
private static final boolean LIB_CRASHLYTICS_LOADED;
37+
private Context context;
2538

2639
static {
2740
boolean loadSuccessful = false;
@@ -45,10 +58,87 @@ class JniNativeApi implements NativeApi {
4558
LIB_CRASHLYTICS_LOADED = loadSuccessful;
4659
}
4760

61+
public JniNativeApi(Context context) {
62+
this.context = context;
63+
}
64+
65+
public static boolean isAtLeastLollipop() {
66+
return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP;
67+
}
68+
69+
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
70+
public static void addSplitSourceDirs(List<String> zipPaths, ApplicationInfo applicationInfo) {
71+
if (applicationInfo.splitSourceDirs != null) {
72+
Collections.addAll(zipPaths, applicationInfo.splitSourceDirs);
73+
}
74+
}
75+
76+
public String[] makePackagePaths(String arch) {
77+
try {
78+
PackageManager pm = context.getPackageManager();
79+
PackageInfo pi =
80+
pm.getPackageInfo(
81+
context.getPackageName(),
82+
PackageManager.GET_SHARED_LIBRARY_FILES | PackageManager.MATCH_UNINSTALLED_PACKAGES);
83+
84+
List<String> zipPaths = new ArrayList<>(10);
85+
zipPaths.add(pi.applicationInfo.sourceDir);
86+
87+
if (isAtLeastLollipop()) {
88+
addSplitSourceDirs(zipPaths, pi.applicationInfo);
89+
}
90+
91+
if (pi.applicationInfo.sharedLibraryFiles != null) {
92+
Collections.addAll(zipPaths, pi.applicationInfo.sharedLibraryFiles);
93+
}
94+
95+
List<String> libPaths = new ArrayList<>(10);
96+
File parent = new File(pi.applicationInfo.nativeLibraryDir).getParentFile();
97+
if (parent != null) {
98+
libPaths.add(new File(parent, arch).getPath());
99+
100+
// arch is the currently loaded library's ABI name. This is the name of the library
101+
// directory in an APK, but may differ from the library directory extracted to the
102+
// filesystem. ARM family abi names have a suffix specifying the architecture
103+
// version, but may be extracted to directories named "arm64" or "arm".
104+
// crbug.com/930342
105+
if (arch.startsWith("arm64")) {
106+
libPaths.add(new File(parent, "arm64").getPath());
107+
} else if (arch.startsWith("arm")) {
108+
libPaths.add(new File(parent, "arm").getPath());
109+
}
110+
}
111+
for (String zip : zipPaths) {
112+
if (zip.endsWith(".apk")) {
113+
libPaths.add(zip + "!/lib/" + arch);
114+
}
115+
}
116+
libPaths.add(System.getProperty("java.library.path"));
117+
libPaths.add(pi.applicationInfo.nativeLibraryDir);
118+
119+
return new String[] {
120+
TextUtils.join(File.pathSeparator, zipPaths), TextUtils.join(File.pathSeparator, libPaths)
121+
};
122+
} catch (NameNotFoundException e) {
123+
Logger.getLogger().e("Unable to compose package paths", e);
124+
throw new RuntimeException(e);
125+
}
126+
}
127+
48128
@Override
49129
public boolean initialize(String dataPath, AssetManager assetManager) {
50-
return LIB_CRASHLYTICS_LOADED && nativeInit(dataPath, assetManager);
130+
String[] paths = makePackagePaths(Build.CPU_ABI);
131+
132+
if (paths.length < 2) {
133+
return false;
134+
}
135+
136+
String classpath = paths[0];
137+
String libspath = paths[1];
138+
139+
return LIB_CRASHLYTICS_LOADED
140+
&& nativeInit(new String[] {classpath, libspath, dataPath}, assetManager);
51141
}
52142

53-
private native boolean nativeInit(String dataPath, Object assetManager);
143+
private native boolean nativeInit(String[] paths, Object assetManager);
54144
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
1+
APP_ABI := arm64-v8a armeabi-v7a x86_64 x86
22
APP_STL := c++_static
3-
NDK_TOOLCHAIN_VERSION=4.9
3+
APP_PLATFORM := android-16

firebase-crashlytics-ndk/src/main/jni/breakpad/Android.mk

-7
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
include $(call all-subdir-makefiles)
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
LOCAL_PATH := $(call my-dir)
2+
3+
THIRD_PARTY_PATH := ../../../../third_party
4+
5+
include $(CLEAR_VARS)
6+
7+
LOCAL_MODULE := crashpad_client
8+
LOCAL_C_INCLUDES := \
9+
$(LOCAL_PATH)/$(THIRD_PARTY_PATH)/.. \
10+
$(LOCAL_PATH)/$(THIRD_PARTY_PATH)/crashpad \
11+
12+
LOCAL_CPPFLAGS := \
13+
-D_FILE_OFFSET_BITS=64 \
14+
-DCRASHPAD_LSS_SOURCE_EXTERNAL \
15+
-Wall \
16+
-Os \
17+
-flto \
18+
-std=c++17 \
19+
20+
LOCAL_SRC_FILES := \
21+
$(THIRD_PARTY_PATH)/crashpad/client/annotation.cc \
22+
$(THIRD_PARTY_PATH)/crashpad/client/annotation_list.cc \
23+
$(THIRD_PARTY_PATH)/crashpad/client/crash_report_database.cc \
24+
$(THIRD_PARTY_PATH)/crashpad/client/crashpad_client_linux.cc \
25+
$(THIRD_PARTY_PATH)/crashpad/client/crashpad_info.cc \
26+
$(THIRD_PARTY_PATH)/crashpad/client/prune_crash_reports.cc \
27+
$(THIRD_PARTY_PATH)/crashpad/client/settings.cc \
28+
$(THIRD_PARTY_PATH)/crashpad/client/client_argv_handling.cc \
29+
$(THIRD_PARTY_PATH)/crashpad/client/crashpad_info_note.S \
30+
$(THIRD_PARTY_PATH)/crashpad/client/crash_report_database_generic.cc \
31+
32+
LOCAL_STATIC_LIBRARIES := crashpad_tool_support crashpad_util
33+
34+
include $(BUILD_STATIC_LIBRARY)

0 commit comments

Comments
 (0)