Bug 1875294 - Record breadbcrumbs before crashing with UnsatsisfiedLinkError

Report breadcrumbs to Sentry before crashing with
`UnsatsisfiedLinkError`:

  - The files inside the app directory, to detect if the library is
    present or not.
  - The files inside the APK, to detect if a file inside the APK didn't
    get installed correctly.
  - The name of the installer package, so that we can know if the APK
    was installed from a 3rd-party source.
  - The path to the APK.  I don't really see this being useful, but it's
    needed to determine all the other info, so we might as well report
    it.

The idea here is that hopefully one of these will detect that something
was off with their install.  For example, maybe they installed an APK
for the wrong arch or maybe `libjnidispatch` was not extracted
correctly.  If not, at least we can rule these possibilities out as the
root cause.
fenix/124.1.0
Ben Dean-Kawamura 4 months ago committed by mergify[bot]
parent c443d4a37f
commit 03883cc976

@ -112,8 +112,11 @@ import org.mozilla.fenix.session.VisibilityLifecycleCallback
import org.mozilla.fenix.utils.Settings
import org.mozilla.fenix.utils.Settings.Companion.TOP_SITES_PROVIDER_MAX_THRESHOLD
import org.mozilla.fenix.wallpapers.Wallpaper
import java.io.File
import java.io.FileInputStream
import java.util.UUID
import java.util.concurrent.TimeUnit
import java.util.zip.ZipInputStream
import kotlin.math.roundToLong
private const val RAM_THRESHOLD_MEGABYTES = 1024
@ -521,13 +524,76 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
* thread, early in the app startup sequence.
*/
private fun beginSetupMegazord() {
// Note: Megazord.init() must be called as soon as possible ...
Megazord.init()
try {
// Note: Megazord.init() must be called as soon as possible ...
Megazord.init()
initializeRustErrors(components.analytics.crashReporter)
// ... but RustHttpConfig.setClient() and RustLog.enable() can be called later.
initializeRustErrors(components.analytics.crashReporter)
// ... but RustHttpConfig.setClient() and RustLog.enable() can be called later.
RustLog.enable()
} catch (e: UnsatisfiedLinkError) {
@Suppress("TooGenericExceptionCaught")
try {
reportUnsatisfiedLinkErrorBreadcrumbs()
} catch (e: Throwable) {
// This shouldn't happen, but if it does it's better to ignore the exception from
// the breadcrumb code and rethrow the initial exception.
}
throw e
}
}
RustLog.enable()
private fun reportUnsatisfiedLinkErrorBreadcrumbs() {
val breadcrumbStrings = mutableListOf<String>()
val apkPath = applicationContext.getApplicationInfo().sourceDir
breadcrumbStrings.add("APK: $apkPath")
val apkDir = File(apkPath).getParentFile()
val installSourcePackage = if (SDK_INT >= Build.VERSION_CODES.R) {
packageManager.getInstallSourceInfo(packageName).installingPackageName
} else {
@Suppress("DEPRECATION")
packageManager.getInstallerPackageName(packageName)
}
breadcrumbStrings.add("Installer package name: $installSourcePackage")
val installDirFileSet = if (apkDir != null) {
apkDir.walk()
.filter { it != apkDir && !it.isDirectory() }
.map { it.relativeTo(apkDir).toString() }
.filter { it.startsWith("lib/") }
.toHashSet()
} else {
HashSet()
}
val apkFileSet = ZipInputStream(FileInputStream(apkPath)).use {
generateSequence { it.nextEntry }
.map { it.name }
.filter { it.startsWith("lib/") }
.toHashSet()
}
fun formatFileSet(filenames: Set<String>) = if (filenames.size > 0) {
filenames.joinToString(", ")
} else {
"<none>"
}
val installDirOnly = formatFileSet(installDirFileSet - apkFileSet)
val apkFileOnly = formatFileSet(apkFileSet - installDirFileSet)
val both = formatFileSet(installDirFileSet union apkFileSet)
breadcrumbStrings.add("Files only inside lib/ dir: $installDirOnly")
breadcrumbStrings.add("Files only inside APK lib/ dir: $apkFileOnly")
breadcrumbStrings.add("Files inside both lib/ dirs: $both")
for (breadcrumbString in breadcrumbStrings) {
components.analytics.crashReporter.recordCrashBreadcrumb(
Breadcrumb(
category = "Startup",
message = breadcrumbString,
level = Breadcrumb.Level.INFO,
),
)
}
}
@OptIn(DelicateCoroutinesApi::class) // GlobalScope usage

Loading…
Cancel
Save