Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not synthesize discarded events #2108

Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 37 additions & 36 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# supported CodeQL languages.
#
name: "CodeQL"
permissions: read-all

on:
push:
Expand Down Expand Up @@ -42,49 +43,49 @@ jobs:
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: recursive
- uses: gradle/wrapper-validation-action@v1
- name: Checkout repository
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 #v3.6.0
with:
submodules: recursive
- uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4 #v1.1.0

- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 11
- uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 #v3.13.0
with:
distribution: 'zulu'
java-version: 11

- name: Gradle cache
uses: gradle/gradle-build-action@v2
- name: Gradle cache
uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa #v2.12.0

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@396bb3e45325a47dd9ef434068033c6d5bb0d11a #v3.27.3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.

# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality


# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@396bb3e45325a47dd9ef434068033c6d5bb0d11a #v3.27.3

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@396bb3e45325a47dd9ef434068033c6d5bb0d11a #v3.27.3
with:
category: "/language:${{matrix.language}}"
1 change: 1 addition & 0 deletions .github/workflows/downstream_updates.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: downstream-updates
permissions: read-all

on:
release:
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/signing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Sign release assets

on:
release:
types: [released]
workflow_dispatch:
inputs:
tag:
description: 'Tag to sign'
required: true
type: string
jobs:
sign-assets:
runs-on: ubuntu-latest
steps:
- name: Install gpg
run: |
sudo apt-get update
sudo apt-get install -y gnupg
- name: Import GPG key
run: |
echo "${{ secrets.PLATFORMS_GPG_KEY_BASE64 }}" | base64 --decode | gpg --batch --import
- name: Sign assets
uses: bugsnag/platforms-release-signer@main
with:
github_token: ${{ secrets.PLATFORMS_SIGNING_GITHUB_TOKEN }}
full_repository: ${{ github.repository }}
release_tag: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.event.release.tag_name }}
key_id: ${{ secrets.PLATFORMS_GPG_KEY_ID }}
key_passphrase: ${{ secrets.PLATFORMS_GPG_KEY_PASSPHRASE }}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ internal class EventStore(
override val logger: Logger

var onEventStoreEmptyCallback: () -> Unit = {}
var onDiscardEventCallback: (EventPayload) -> Unit = {}
private var isEmptyEventCallbackCalled: Boolean = false

/**
Expand Down Expand Up @@ -183,7 +184,6 @@ internal class EventStore(
}

DeliveryStatus.UNDELIVERED -> undeliveredEventPayload(eventFile)

DeliveryStatus.FAILURE -> {
val exc: Exception = RuntimeException("Failed to deliver event payload")
handleEventFlushFailure(exc, eventFile)
Expand All @@ -196,11 +196,13 @@ internal class EventStore(
logger.w(
"Discarding over-sized event (${eventFile.length()}) after failed delivery"
)
discardEvents(eventFile)
deleteStoredFiles(setOf(eventFile))
} else if (isTooOld(eventFile)) {
logger.w(
"Discarding historical event (from ${getCreationDate(eventFile)}) after failed delivery"
)
discardEvents(eventFile)
deleteStoredFiles(setOf(eventFile))
} else {
cancelQueuedFiles(setOf(eventFile))
Expand Down Expand Up @@ -273,6 +275,19 @@ internal class EventStore(
}
}

private fun discardEvents(eventFile: File) {
val eventFilenameInfo = fromFile(eventFile, config)
onDiscardEventCallback(
EventPayload(
eventFilenameInfo.apiKey,
null,
eventFile,
notifier,
config
)
)
}

companion object {
private const val LAUNCH_CRASH_TIMEOUT_MS: Long = 2000
val EVENT_COMPARATOR: Comparator<in File?> = Comparator { lhs, rhs ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,18 @@ public final class com/bugsnag/android/BugsnagExitInfoPlugin$Companion {

public final class com/bugsnag/android/ExitInfoPluginConfiguration {
public fun <init> ()V
public fun <init> (ZZZZZ)V
public synthetic fun <init> (ZZZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (ZZZ)V
public fun <init> (ZZZZ)V
public synthetic fun <init> (ZZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getDisableProcessStateSummaryOverride ()Z
public final fun getIncludeLogcat ()Z
public final fun getListOpenFds ()Z
public final fun getReportUnmatchedAnrs ()Z
public final fun getReportUnmatchedNativeCrashes ()Z
public final fun getReportUnmatchedANR ()Z
public fun hashCode ()I
public final fun setDisableProcessStateSummaryOverride (Z)V
public final fun setIncludeLogcat (Z)V
public final fun setListOpenFds (Z)V
public final fun setReportUnmatchedAnrs (Z)V
public final fun setReportUnmatchedNativeCrashes (Z)V
public final fun setReportUnmatchedANR (Z)V
}

7 changes: 3 additions & 4 deletions bugsnag-plugin-android-exitinfo/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>CyclomaticComplexMethod:EventSynthesizer.kt$EventSynthesizer$private fun getExitInfoImportance(importance: Int): String</ID>
<ID>CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$@SuppressLint("SwitchIntDef") @Suppress("DEPRECATION") private fun importanceDescriptionOf(exitInfo: ApplicationExitInfo)</ID>
<ID>CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$private fun exitReasonOf(exitInfo: ApplicationExitInfo)</ID>
<ID>CyclomaticComplexMethod:CodeStrings.kt$@RequiresApi(Build.VERSION_CODES.R) @SuppressLint("SwitchIntDef") @Suppress("DEPRECATION") internal fun importanceDescriptionOf(exitInfo: ApplicationExitInfo)</ID>
<ID>CyclomaticComplexMethod:CodeStrings.kt$@RequiresApi(Build.VERSION_CODES.R) internal fun exitReasonOf(exitInfo: ApplicationExitInfo)</ID>
<ID>LongParameterList:TombstoneParser.kt$TombstoneParser$( exitInfo: ApplicationExitInfo, listOpenFds: Boolean, includeLogcat: Boolean, threadConsumer: (BugsnagThread) -> Unit, fileDescriptorConsumer: (Int, String, String) -> Unit, logcatConsumer: (String) -> Unit )</ID>
<ID>MagicNumber:BugsnagExitInfoPlugin.kt$BugsnagExitInfoPlugin$100</ID>
<ID>MagicNumber:TraceParser.kt$TraceParser$16</ID>
<ID>MagicNumber:TraceParser.kt$TraceParser$3</ID>
<ID>MaxLineLength:ExitInfoCallback.kt$ExitInfoCallback$val allExitInfo: List&lt;ApplicationExitInfo> = am.getHistoricalProcessExitReasons(context.packageName, 0, MAX_EXIT_INFO)</ID>
<ID>MaxLineLength:ExitInfoCallbackTest.kt$ExitInfoCallbackTest$exitInfoCallback = ExitInfoCallback(context, nativeEnhancer, anrEventEnhancer, null, ApplicationExitInfoMatcher(context, 100))</ID>
<ID>MaxLineLength:TraceParserInvalidStackframesTest.kt$TraceParserInvalidStackframesTest.Companion$"#01 pc 0000000000000c5c /data/app/~~sKQbJGqVJA5glcnvEjZCMg==/com.example.bugsnag.android-fVuoJh5GpAL7sRAeI3vjSw==/lib/arm64/libentrypoint.so "</ID>
<ID>MaxLineLength:TraceParserInvalidStackframesTest.kt$TraceParserInvalidStackframesTest.Companion$"#01 pc 0000000000000c5c /data/app/~~sKQbJGqVJA5glcnvEjZCMg==/com.example.bugsnag.android-fVuoJh5GpAL7sRAeI3vjSw==/lib/arm64/libentrypoint.so (Java_com_example_bugsnag_android_BaseCrashyActivity_anrFromCXX+20"</ID>
<ID>MaxLineLength:TraceParserInvalidStackframesTest.kt$TraceParserInvalidStackframesTest.Companion$"#01 pc 0000000000000c5c /data/app/~~sKQbJGqVJA5glcnvEjZCMg==/com.example.bugsnag.android-fVuoJh5GpAL7sRAeI3vjSw==/lib/arm64/libentrypoint.so (Java_com_example_bugsnag_android_BaseCrashyActivity_anrFromCXX+20) ("</ID>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.bugsnag.android

import android.app.ActivityManager
import android.app.ApplicationExitInfo
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi

@RequiresApi(Build.VERSION_CODES.R)
internal class ApplicationExitInfoMatcher(
private val context: Context,
private val pid: Int
) {
fun matchExitInfo(event: Event): ApplicationExitInfo? {
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 null
val exitInfo: ApplicationExitInfo =
findExitInfoBySessionId(allExitInfo, sessionIdBytes)
?: findExitInfoByPid(allExitInfo) ?: return null
return exitInfo
}

internal fun findExitInfoBySessionId(
allExitInfo: List<ApplicationExitInfo>,
sessionIdBytes: ByteArray
) = allExitInfo.find {
it.processStateSummary?.contentEquals(sessionIdBytes) == true
}

internal fun findExitInfoByPid(allExitInfo: List<ApplicationExitInfo>) =
allExitInfo.find { it.pid == pid }

internal companion object {
private const val MAX_EXIT_INFO = 100
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ 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
import android.os.Process
import androidx.annotation.RequiresApi

@RequiresApi(Build.VERSION_CODES.R)
Expand Down Expand Up @@ -36,26 +38,54 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(
client.immutableConfig.projectPackages
)

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

val exitInfoMatcher = ApplicationExitInfoMatcher(
context = client.appContext,
pid = exitInfoPluginStore.previousPid
)

val exitInfoCallback = createExitInfoCallback(
client,
exitInfoPluginStore.previousPid, exitInfoPluginStore,
exitInfoPluginStore,
tombstoneEventEnhancer,
traceEventEnhancer
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
)
}
client.addOnSend(exitInfoCallback)

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

private fun addAllExitInfoAtFirstRun(
Expand All @@ -79,30 +109,27 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(

private fun createExitInfoCallback(
client: Client,
oldPid: Int?,
exitInfoPluginStore: ExitInfoPluginStore,
tombstoneEventEnhancer: TombstoneEventEnhancer,
traceEventEnhancer: TraceEventEnhancer
traceEventEnhancer: TraceEventEnhancer,
applicationExitInfoMatcher: ApplicationExitInfoMatcher
): ExitInfoCallback = ExitInfoCallback(
client.appContext,
oldPid,
tombstoneEventEnhancer,
traceEventEnhancer,
exitInfoPluginStore
exitInfoPluginStore,
applicationExitInfoMatcher
)

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 @@ -124,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
Loading