Skip to content

Commit

Permalink
feat(ExitInfo)added log message to investigate
Browse files Browse the repository at this point in the history
  • Loading branch information
YYChen01988 authored and lemnik committed Nov 26, 2024
1 parent c9d9126 commit a1d71a4
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 180 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.bugsnag.android

import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.Application
import android.app.ApplicationExitInfo
import android.content.Context
import android.os.Build
Expand Down Expand Up @@ -37,8 +38,7 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(
client.immutableConfig.projectPackages
)

val exitInfoPluginStore =
ExitInfoPluginStore(client.immutableConfig)
val exitInfoPluginStore = ExitInfoPluginStore(client.immutableConfig)
addAllExitInfoAtFirstRun(client, exitInfoPluginStore)
exitInfoPluginStore.currentPid = Process.myPid()

Expand All @@ -54,21 +54,38 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(
traceEventEnhancer,
exitInfoMatcher
)
client.addOnSend(exitInfoCallback)

if (client.appContext.isPrimaryProcess()) {
configureEventSynthesizer(
client,
exitInfoPluginStore,
traceEventEnhancer,
exitInfoMatcher
)
}
}

private fun configureEventSynthesizer(
client: Client,
exitInfoPluginStore: ExitInfoPluginStore,
traceEventEnhancer: TraceEventEnhancer,
exitInfoMatcher: ApplicationExitInfoMatcher
) {
InternalHooks.setEventStoreEmptyCallback(client) {
synthesizeNewEvents(
client,
exitInfoPluginStore,
tombstoneEventEnhancer,
traceEventEnhancer
)
}

InternalHooks.setDiscardEventCallback(client) { eventPayload ->
val exitInfo = eventPayload.event?.let { exitInfoMatcher.matchExitInfo(it) }
exitInfo?.let { exitInfoPluginStore.addExitInfoKey(ExitInfoKey(exitInfo)) }
exitInfo?.let {
exitInfoPluginStore.addExitInfoKey(ExitInfoKey(exitInfo))
}
}

client.addOnSend(exitInfoCallback)
}

private fun addAllExitInfoAtFirstRun(
Expand Down Expand Up @@ -107,15 +124,12 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(
private fun synthesizeNewEvents(
client: Client,
exitInfoPluginStore: ExitInfoPluginStore,
tombstoneEventEnhancer: TombstoneEventEnhancer,
traceEventEnhancer: TraceEventEnhancer
) {
val eventSynthesizer = EventSynthesizer(
traceEventEnhancer,
tombstoneEventEnhancer,
exitInfoPluginStore,
configuration.reportUnmatchedAnrs,
configuration.reportUnmatchedNativeCrashes
configuration.reportUnmatchedANR
)
val context = client.appContext
val am: ActivityManager = context.safeGetActivityManager() ?: return
Expand All @@ -137,6 +151,10 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(
null
}

private fun Context.isPrimaryProcess(): Boolean {
return Application.getProcessName() == packageName
}

companion object {
private const val MATCH_ALL = 0
private const val MAX_EXIT_REASONS = 100
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.bugsnag.android

import android.annotation.SuppressLint
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING_PRE_28
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE
import android.app.ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE
import android.app.ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE
import android.app.ApplicationExitInfo
import android.app.ApplicationExitInfo.REASON_ANR
import android.app.ApplicationExitInfo.REASON_CRASH
import android.app.ApplicationExitInfo.REASON_CRASH_NATIVE
import android.app.ApplicationExitInfo.REASON_DEPENDENCY_DIED
import android.app.ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE
import android.app.ApplicationExitInfo.REASON_EXIT_SELF
import android.app.ApplicationExitInfo.REASON_FREEZER
import android.app.ApplicationExitInfo.REASON_INITIALIZATION_FAILURE
import android.app.ApplicationExitInfo.REASON_LOW_MEMORY
import android.app.ApplicationExitInfo.REASON_OTHER
import android.app.ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE
import android.app.ApplicationExitInfo.REASON_PACKAGE_UPDATED
import android.app.ApplicationExitInfo.REASON_PERMISSION_CHANGE
import android.app.ApplicationExitInfo.REASON_SIGNALED
import android.app.ApplicationExitInfo.REASON_USER_REQUESTED
import android.app.ApplicationExitInfo.REASON_USER_STOPPED
import android.os.Build
import androidx.annotation.RequiresApi

private const val IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170

@RequiresApi(Build.VERSION_CODES.R)
internal fun exitReasonOf(exitInfo: ApplicationExitInfo) = when (exitInfo.reason) {
ApplicationExitInfo.REASON_UNKNOWN -> "unknown reason (${exitInfo.reason})"
REASON_EXIT_SELF -> "exit self"
REASON_SIGNALED -> "signaled"
REASON_LOW_MEMORY -> "low memory"
REASON_CRASH -> "crash"
REASON_CRASH_NATIVE -> "crash native"
REASON_ANR -> "ANR"
REASON_INITIALIZATION_FAILURE -> "initialization failure"
REASON_PERMISSION_CHANGE -> "permission change"
REASON_EXCESSIVE_RESOURCE_USAGE -> "excessive resource usage"
REASON_USER_REQUESTED -> "user requested"
REASON_USER_STOPPED -> "user stopped"
REASON_DEPENDENCY_DIED -> "dependency died"
REASON_OTHER -> "other"
REASON_FREEZER -> "freezer"
REASON_PACKAGE_STATE_CHANGE -> "package state change"
REASON_PACKAGE_UPDATED -> "package updated"
else -> "unknown reason (${exitInfo.reason})"
}

@RequiresApi(Build.VERSION_CODES.R)
@SuppressLint("SwitchIntDef")
@Suppress("DEPRECATION")
internal fun importanceDescriptionOf(exitInfo: ApplicationExitInfo) =
when (exitInfo.importance) {
IMPORTANCE_FOREGROUND -> "foreground"
IMPORTANCE_FOREGROUND_SERVICE -> "foreground service"
IMPORTANCE_TOP_SLEEPING -> "top sleeping"
IMPORTANCE_TOP_SLEEPING_PRE_28 -> "top sleeping"
IMPORTANCE_VISIBLE -> "visible"
IMPORTANCE_PERCEPTIBLE -> "perceptible"
IMPORTANCE_PERCEPTIBLE_PRE_26 -> "perceptible"
IMPORTANCE_CANT_SAVE_STATE, IMPORTANCE_CANT_SAVE_STATE_PRE_26 -> "can't save state"
IMPORTANCE_SERVICE -> "service"
IMPORTANCE_CACHED -> "cached/background"
IMPORTANCE_GONE -> "gone"
IMPORTANCE_EMPTY -> "empty"
REASON_PROVIDER_IN_USE -> "provider in use"
REASON_SERVICE_IN_USE -> "service in use"
else -> "unknown importance (${exitInfo.importance})"
}
Original file line number Diff line number Diff line change
@@ -1,57 +1,42 @@
package com.bugsnag.android

import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE
import android.app.ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE
import android.app.ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE
import android.app.ApplicationExitInfo
import android.app.ApplicationExitInfo.REASON_ANR
import android.app.ApplicationExitInfo.REASON_CRASH_NATIVE
import android.os.Build
import androidx.annotation.RequiresApi

@RequiresApi(Build.VERSION_CODES.R)
internal class EventSynthesizer(
private val anrEventEnhancer: (Event, ApplicationExitInfo) -> Unit,
private val nativeEnhancer: (Event, ApplicationExitInfo) -> Unit,
private val exitInfoPluginStore: ExitInfoPluginStore,
private val reportUnmatchedAnrs: Boolean,
private val reportUnmatchedNativeCrashes: Boolean
private val reportUnmatchedANRs: Boolean,
) {
fun createEventWithExitInfo(appExitInfo: ApplicationExitInfo): Event? {
val knownExitInfoKeys = exitInfoPluginStore.exitInfoKeys
val exitInfoKey = ExitInfoKey(appExitInfo)

if (knownExitInfoKeys.contains(exitInfoKey)) return null
else exitInfoPluginStore.addExitInfoKey(exitInfoKey)
if (knownExitInfoKeys.contains(exitInfoKey)) {
return null
}

when (appExitInfo.reason) {
REASON_ANR -> {
return createEventWithUnmatchedAnrsReport(exitInfoKey, appExitInfo)
}
exitInfoPluginStore.addExitInfoKey(exitInfoKey)

REASON_CRASH_NATIVE -> {
return createEventWithUnmatchedNativeCrashesReport(exitInfoKey, appExitInfo)
return when (appExitInfo.reason) {
REASON_ANR -> {
createEventWithUnmatchedANR(exitInfoKey, appExitInfo)
}

else -> return null
else -> null
}
}

private fun createEventWithUnmatchedAnrsReport(
private fun createEventWithUnmatchedANR(
exitInfoKey: ExitInfoKey,
appExitInfo: ApplicationExitInfo
): Event? {
if (reportUnmatchedAnrs) {
if (reportUnmatchedANRs) {
val newAnrEvent = InternalHooks.createEmptyANR(exitInfoKey.timestamp)
?: return null
addExitInfoMetadata(newAnrEvent, appExitInfo)
anrEventEnhancer(newAnrEvent, appExitInfo)
val thread = getErrorThread(newAnrEvent)
Expand All @@ -64,28 +49,9 @@ internal class EventSynthesizer(
}
}

private fun createEventWithUnmatchedNativeCrashesReport(
exitInfoKey: ExitInfoKey,
appExitInfo: ApplicationExitInfo
): Event? {
if (reportUnmatchedNativeCrashes) {
val newNativeEvent = InternalHooks.createEmptyCrash(exitInfoKey.timestamp)
addExitInfoMetadata(newNativeEvent, appExitInfo)
nativeEnhancer(newNativeEvent, appExitInfo)
val thread =
getErrorThread(newNativeEvent)
val error = newNativeEvent.addError("Native", appExitInfo.description)
thread?.let { error.stacktrace.addAll(it.stacktrace) }
return newNativeEvent
} else {
return null
}
}

private fun getErrorThread(newNativeEvent: Event): Thread? {
val thread =
newNativeEvent.threads.find { it.name == "main" }
?: newNativeEvent.threads.firstOrNull()
val thread = newNativeEvent.threads.find { it.name == "main" }
?: newNativeEvent.threads.firstOrNull()
return thread
}

Expand All @@ -97,7 +63,7 @@ internal class EventSynthesizer(
newEvent.addMetadata(
"exitinfo",
"importance",
getExitInfoImportance(appExitInfo.importance)
importanceDescriptionOf(appExitInfo)
)
newEvent.addMetadata(
"exitinfo", "Proportional Set Size (PSS)", "${appExitInfo.pss} kB"
Expand All @@ -107,25 +73,6 @@ internal class EventSynthesizer(
)
}

private fun getExitInfoImportance(importance: Int): String = when (importance) {
IMPORTANCE_FOREGROUND -> "foreground"
IMPORTANCE_FOREGROUND_SERVICE -> "foreground service"
IMPORTANCE_TOP_SLEEPING -> "top sleeping"
IMPORTANCE_TOP_SLEEPING_PRE_28 -> "top sleeping"
IMPORTANCE_VISIBLE -> "visible"
IMPORTANCE_PERCEPTIBLE -> "perceptible"
IMPORTANCE_PERCEPTIBLE_PRE_26 -> "perceptible"
IMPORTANCE_CANT_SAVE_STATE -> "can't save state"
IMPORTANCE_CANT_SAVE_STATE_PRE_26 -> "can't save state"
IMPORTANCE_SERVICE -> "service"
IMPORTANCE_CACHED -> "cached/background"
IMPORTANCE_GONE -> "gone"
IMPORTANCE_EMPTY -> "empty"
REASON_PROVIDER_IN_USE -> "provider in use"
REASON_SERVICE_IN_USE -> "service in use"
else -> "unknown importance ($importance)"
}

companion object {
const val IMPORTANCE_EMPTY = 500
const val IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
package com.bugsnag.android

import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING_PRE_28
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE
import android.app.ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE
import android.app.ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE
import android.app.ApplicationExitInfo
import android.content.Context
import android.os.Build
Expand All @@ -31,11 +16,14 @@ internal class ExitInfoCallback(
) : OnSendCallback {

override fun onSend(event: Event): Boolean {
val am: ActivityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val allExitInfo: List<ApplicationExitInfo> = am.getHistoricalProcessExitReasons(context.packageName, 0, MAX_EXIT_INFO)
val am: ActivityManager =
context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val allExitInfo: List<ApplicationExitInfo> =
am.getHistoricalProcessExitReasons(context.packageName, 0, MAX_EXIT_INFO)
val sessionIdBytes: ByteArray = event.session?.id?.toByteArray() ?: return true
val exitInfo: ApplicationExitInfo = applicationExitInfoMatcher?.findExitInfoBySessionId(allExitInfo, sessionIdBytes)
?: applicationExitInfoMatcher?.findExitInfoByPid(allExitInfo) ?: return true
val exitInfo: ApplicationExitInfo =
applicationExitInfoMatcher?.findExitInfoBySessionId(allExitInfo, sessionIdBytes)
?: applicationExitInfoMatcher?.findExitInfoByPid(allExitInfo) ?: return true
exitInfoPluginStore?.addExitInfoKey(ExitInfoKey(exitInfo.pid, exitInfo.timestamp))

try {
Expand All @@ -60,50 +48,7 @@ internal class ExitInfoCallback(
return true
}

private fun exitReasonOf(exitInfo: ApplicationExitInfo) = when (exitInfo.reason) {
ApplicationExitInfo.REASON_UNKNOWN -> "unknown reason (${exitInfo.reason})"
ApplicationExitInfo.REASON_EXIT_SELF -> "exit self"
ApplicationExitInfo.REASON_SIGNALED -> "signaled"
ApplicationExitInfo.REASON_LOW_MEMORY -> "low memory"
ApplicationExitInfo.REASON_CRASH -> "crash"
ApplicationExitInfo.REASON_CRASH_NATIVE -> "crash native"
ApplicationExitInfo.REASON_ANR -> "ANR"
ApplicationExitInfo.REASON_INITIALIZATION_FAILURE -> "initialization failure"
ApplicationExitInfo.REASON_PERMISSION_CHANGE -> "permission change"
ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> "excessive resource usage"
ApplicationExitInfo.REASON_USER_REQUESTED -> "user requested"
ApplicationExitInfo.REASON_USER_STOPPED -> "user stopped"
ApplicationExitInfo.REASON_DEPENDENCY_DIED -> "dependency died"
ApplicationExitInfo.REASON_OTHER -> "other"
ApplicationExitInfo.REASON_FREEZER -> "freezer"
ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE -> "package state change"
ApplicationExitInfo.REASON_PACKAGE_UPDATED -> "package updated"
else -> "unknown reason (${exitInfo.reason})"
}

@SuppressLint("SwitchIntDef")
@Suppress("DEPRECATION")
private fun importanceDescriptionOf(exitInfo: ApplicationExitInfo) = when (exitInfo.importance) {
IMPORTANCE_FOREGROUND -> "foreground"
IMPORTANCE_FOREGROUND_SERVICE -> "foreground service"
IMPORTANCE_TOP_SLEEPING -> "top sleeping"
IMPORTANCE_TOP_SLEEPING_PRE_28 -> "top sleeping"
IMPORTANCE_VISIBLE -> "visible"
IMPORTANCE_PERCEPTIBLE -> "perceptible"
IMPORTANCE_PERCEPTIBLE_PRE_26 -> "perceptible"
IMPORTANCE_CANT_SAVE_STATE -> "can't save state"
IMPORTANCE_CANT_SAVE_STATE_PRE_26 -> "can't save state"
IMPORTANCE_SERVICE -> "service"
IMPORTANCE_CACHED -> "cached/background"
IMPORTANCE_GONE -> "gone"
IMPORTANCE_EMPTY -> "empty"
REASON_PROVIDER_IN_USE -> "provider in use"
REASON_SERVICE_IN_USE -> "service in use"
else -> "unknown importance (${exitInfo.importance})"
}

internal companion object {
private const val MAX_EXIT_INFO = 100
private const val IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170
}
}
Loading

0 comments on commit a1d71a4

Please sign in to comment.