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

Release/v6.6.0 #2042

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
baadae6
feat(correlation): added the trace correlation property to the Event …
lemnik Apr 25, 2024
187e954
Merge pull request #2023 from bugsnag/PLAT-12049/correlation-event-model
lemnik Apr 29, 2024
b00e1b8
Merge pull request #2029 from bugsnag/master
lemnik May 16, 2024
084df8b
Add Android 14 tests
twometresteve May 15, 2024
950d5a2
Merge branch 'next' into tms/android-14
twometresteve May 18, 2024
c61bd2b
Merge pull request #2032 from bugsnag/tms/android-14
twometresteve May 21, 2024
df65b5f
refactor(ndk): added BSG_KSJSONCodec to the NDK plugin
lemnik May 1, 2024
6c59e8f
refactor(ndk): add simplified ISO 8601 time formatting to BSG_KSCrash…
lemnik May 2, 2024
7681abf
refactor(ndk): rewrote the event_writer.c to output JSON instead of a…
lemnik May 2, 2024
481b0de
refactor(ndk): removed old binary-dump event logic & added a new test…
lemnik May 9, 2024
6d74432
fix(ndk): fixed a bug where the breadcrumb metadata was double-nested
lemnik May 9, 2024
6f06a86
fix(ndk): moved the NDK JSON payloads back to their own directory so …
lemnik May 9, 2024
a1882f6
fix(ndk): BugsnagEventMapper now handles date formats in the "t{epoch…
lemnik May 13, 2024
dd2f56a
fix(ndk): added thread.type to ndk events, and removed invalid / paus…
lemnik May 13, 2024
09751aa
fix(ndk): fixed NDK thread writing behaviour
lemnik May 13, 2024
ece1825
fix(ndk): added the static usage metrics data to the native events
lemnik May 14, 2024
0abb887
test(ndk): added a basic ReportDiscardScanner test
lemnik May 15, 2024
6c17539
refactor(ndk event): replaced STRING_**_EMPTY macros with inline func…
lemnik May 21, 2024
74b6001
refactor(ndk event): copy the entire event_path instead of relying on…
lemnik May 21, 2024
3161e39
refactor(ndk event): NaN & Inf values are encoded to JSON as 'null'
lemnik May 22, 2024
308c95f
Merge pull request #2027 from bugsnag/PLAT-12009/json-event
lemnik May 22, 2024
6c29641
Update AGP and related versions for mazerunner (#2030)
YYChen01988 May 22, 2024
9f62d7a
fix(Session Tracker) fixed logic of discarding session (#2033)
YYChen01988 May 24, 2024
5bc8d99
fix(errorCallback) separating throw exception out (#2036)
YYChen01988 May 31, 2024
2201a50
refactor(ndk event): iterate directly over metadata elements in NDK i…
lemnik May 30, 2024
9a6104c
test(ndk): fixed event_serialization.json to align with the expected …
lemnik May 30, 2024
af3f5bc
test(ndk): manually cause a stack-overflow in CXXStackoverflowScenari…
lemnik May 30, 2024
2cce055
Merge pull request #2035 from bugsnag/PLAT-12075/ndk-metadata-iterator
lemnik Jun 4, 2024
d5ac07d
Reinstate instrumentation tests using MacOS test boxes as a platform …
Cawllec Jun 6, 2024
843bdb7
Merge branch 'next' into integration/error-correlation
lemnik Jun 12, 2024
5040101
feat(native): support kernels running with a 16kB page size
lemnik Jun 13, 2024
341930e
feat(event correlation): changed the casing of the `traceid`->`traceI…
lemnik Jun 13, 2024
e87cc38
Merge pull request #2040 from bugsnag/PLAT-12268/16kb-page-support
lemnik Jun 13, 2024
e7eaf36
Merge branch 'next' into integration/error-correlation
lemnik Jun 13, 2024
1f49c06
chore(changelog): fixed the CHANGELOG entry for PR#2040
lemnik Jun 13, 2024
02dfd6e
Merge pull request #2038 from bugsnag/integration/error-correlation
lemnik Jun 13, 2024
e8d6bdc
release/v6.6.0
YYChen01988 Jun 19, 2024
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
Prev Previous commit
Next Next commit
fix(ndk): moved the NDK JSON payloads back to their own directory so …
…that enabledReleaseStage & discardClasses can be checked before delivery
  • Loading branch information
lemnik committed May 22, 2024
commit 6f06a86afd1ab6770a94754f5ceea43fa1640c4c
1 change: 1 addition & 0 deletions bugsnag-android-core/api/bugsnag-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ public class com/bugsnag/android/NativeInterface {
public static fun addMetadata (Ljava/lang/String;Ljava/util/Map;)V
public static fun clearMetadata (Ljava/lang/String;Ljava/lang/String;)V
public static fun createEvent (Ljava/lang/Throwable;Lcom/bugsnag/android/Client;Lcom/bugsnag/android/SeverityReason;)Lcom/bugsnag/android/Event;
public static fun deliverReport (Ljava/io/File;)V
public static fun deliverReport ([B[B[BLjava/lang/String;Z)V
public static fun getApp ()Ljava/util/Map;
public static fun getAppVersion ()Ljava/lang/String;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Map;
import java.util.regex.Pattern;


/**
* Used as the entry point for native code to allow proguard to obfuscate other areas if needed
*/
Expand Down Expand Up @@ -81,7 +82,15 @@ public static String getContext() {
*/
@NonNull
public static File getNativeReportPath() {
return getClient().getEventStore().getStorageDir();
return getNativeReportPath(getPersistenceDirectory());
}

private static @NonNull File getNativeReportPath(@NonNull File persistenceDirectory) {
return new File(persistenceDirectory, "bugsnag/native");
}

private static @NonNull File getPersistenceDirectory() {
return getClient().getConfig().getPersistenceDirectory().getValue();
}

/**
Expand Down Expand Up @@ -422,6 +431,25 @@ public static void deliverReport(@Nullable byte[] releaseStageBytes,
}
}

/**
* Attempt to deliver an existing event file that is not current enqueued for delivery. The
* filename is expected to be in the standard {@link EventFilenameInfo} format, and the file
* should contain a correctly formatted {@link Event} object. This method will attempt to
* move the file into place, and flush the queue asynchronously. If the file cannot be moved
* into the queue directory, the file is deleted before returning.
*
* @param reportFile the file to enqueue for delivery
*/
public static void deliverReport(@NonNull File reportFile) {
EventStore eventStore = getClient().eventStore;
File eventFile = new File(eventStore.getStorageDir(), reportFile.getName());
if (reportFile.renameTo(eventFile)) {
eventStore.flushAsync();
} else {
reportFile.delete();
}
}

/**
* Notifies using the Android SDK
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import com.bugsnag.android.internal.ImmutableConfig
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertSame
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
Expand All @@ -22,6 +21,7 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnitRunner
import java.io.File
import java.nio.file.Files

/**
Expand Down Expand Up @@ -87,9 +87,10 @@ internal class NativeInterfaceApiTest {
@Test
fun getNativeReportPathPersistenceDirectory() {
val customDir = Files.createTempDirectory("custom").toFile()
`when`(eventStore.storageDir).thenReturn(customDir)
`when`(immutableConfig.persistenceDirectory).thenReturn(lazy { customDir })
val observed = NativeInterface.getNativeReportPath()
assertSame(customDir, observed)
val expected = File(customDir, "bugsnag/native")
assertEquals(expected, observed)
}

@Test
Expand Down
3 changes: 1 addition & 2 deletions bugsnag-plugin-android-ndk/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>CyclomaticComplexMethod:NativeBridge.kt$NativeBridge$override fun onStateChange(event: StateEvent)</ID>
<ID>LongMethod:EventOnDiskTests.kt$EventOnDiskTests$@Test fun testEvent()</ID>
<ID>LongParameterList:NativeBridge.kt$NativeBridge$( apiKey: String, reportingDirectory: String, lastRunInfoPath: String, eventUUID: String, consecutiveLaunchCrashes: Int, autoDetectNdkCrashes: Boolean, apiLevel: Int, is32bit: Boolean, threadSendPolicy: Int, maxBreadcrumbs: Int, )</ID>
<ID>NestedBlockDepth:NativeBridge.kt$NativeBridge$private fun deliverPendingReports()</ID>
<ID>SwallowedException:ReportDiscardScanner.kt$ReportDiscardScanner$ex: Exception</ID>
<ID>TooManyFunctions:NativeBridge.kt$NativeBridge : StateObserver</ID>
<ID>UseCheckOrError:ResourceUtils.kt$throw IllegalStateException("Failed to read JSON from $resourceName")</ID>
</CurrentIssues>
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,79 @@
{"context":"Foo","metaData":{},"severity":"info","unhandled":true,"severityReason":{"unhandledOverridden":false,"type":"signal","attributes":{"signalType":"SIGSEGV"}},"exceptions":[{"errorClass":"SIGSEGV","message":"Whoops!","type":"c","stacktrace":[{"frameAddress":"0x0","symbolAddress":"0x0","loadAddress":"0x0","lineNumber":58,"isPC":true,"file":"Something.c","method":"foo()"}]}],"user":{"id":"123","name":"Bob Bobbiton","email":"bob@example.com"},"app":{"version":"1.0","id":"fa02","type":"C","releaseStage":"dev","versionCode":55,"buildUUID":"123","binaryArch":"x86","duration":9019,"durationInForeground":7017,"inForeground":true,"isLaunching":true},"device":{"osName":"android","id":"my-id-123","locale":"en","osVersion":"9.1","manufacturer":"Google","model":"Nexus","orientation":"portrait","runtimeVersions":{"androidApiLevel":0,"osBuild":""},"cpuAbi":[],"totalMemory":1095092340,"jailbroken":true,"time":"1970-01-01T02:06:49Z"},"breadcrumbs":[],"groupingHash":"Bar","usage":{"callbacks":{}},"session":{"id":"","startedAt":"","events":{"handled":0,"unhandled":0}}}
{
"context": "Foo",
"metaData": {},
"severity": "info",
"unhandled": true,
"severityReason": {
"unhandledOverridden": false,
"type": "signal",
"attributes": {
"signalType": "SIGSEGV"
}
},
"exceptions": [
{
"errorClass": "SIGSEGV",
"message": "Whoops!",
"type": "c",
"stacktrace": [
{
"frameAddress": "0x0",
"symbolAddress": "0x0",
"loadAddress": "0x0",
"lineNumber": 58,
"isPC": true,
"file": "Something.c",
"method": "foo()"
}
]
}
],
"user": {
"id": "123",
"name": "Bob Bobbiton",
"email": "bob@example.com"
},
"app": {
"version": "1.0",
"id": "fa02",
"type": "C",
"releaseStage": "dev",
"versionCode": 55,
"buildUUID": "123",
"binaryArch": "x86",
"duration": 9019,
"durationInForeground": 7017,
"inForeground": true,
"isLaunching": true
},
"device": {
"osName": "android",
"id": "my-id-123",
"locale": "en",
"osVersion": "9.1",
"manufacturer": "Google",
"model": "Nexus",
"orientation": "portrait",
"runtimeVersions": {
"androidApiLevel": 0,
"osBuild": ""
},
"cpuAbi": [],
"totalMemory": 1095092340,
"jailbroken": true,
"time": "1970-01-01T02:06:49Z"
},
"breadcrumbs": [],
"groupingHash": "Bar",
"usage": {
"callbacks": {}
},
"session": {
"id": "",
"startedAt": "",
"events": {
"handled": 0,
"unhandled": 0
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.bugsnag.android.StateEvent.AddBreadcrumb
import com.bugsnag.android.StateEvent.AddMetadata
import com.bugsnag.android.StateEvent.ClearMetadataSection
import com.bugsnag.android.StateEvent.ClearMetadataValue
import com.bugsnag.android.StateEvent.DeliverPending
import com.bugsnag.android.StateEvent.Install
import com.bugsnag.android.StateEvent.NotifyHandled
import com.bugsnag.android.StateEvent.NotifyUnhandled
Expand Down Expand Up @@ -107,6 +108,7 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse

when (event) {
is Install -> handleInstallMessage(event)
is DeliverPending -> deliverPendingReports()
is AddMetadata -> handleAddMetadata(event)
is ClearMetadataSection -> clearMetadataTab(event.section)
is ClearMetadataValue -> removeMetadata(
Expand Down Expand Up @@ -169,6 +171,17 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse
}
}

private fun deliverPendingReports() {
val discardScanner = ReportDiscardScanner(logger)
reportDirectory.listFiles()?.forEach { reportFile ->
if (discardScanner.shouldDiscard(reportFile)) {
reportFile.delete()
} else {
NativeInterface.deliverReport(reportFile)
}
}
}

private fun isInvalidMessage(msg: Any?): Boolean {
if (msg == null || msg !is StateEvent) {
return true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.bugsnag.android.ndk

import android.util.JsonReader
import android.util.JsonToken
import com.bugsnag.android.Logger
import com.bugsnag.android.NativeInterface
import java.io.File

internal class ReportDiscardScanner(private val logger: Logger) {
/**
* Checks whether a given report file should be discarded due to its `releaseStage` or any of
* the configured `discardClasses`.
*
* @return true if the report should be discarded instead of being sent
*/
fun shouldDiscard(report: File): Boolean {
return try {
report.bufferedReader().use { reader ->
JsonReader(reader).use { json -> shouldDiscard(json) }
}
} catch (ex: Exception) {
false
}
}

private fun shouldDiscard(json: JsonReader): Boolean {
json.beginObject()
var pendingAppCheck = true
var pendingExceptionsCheck = true

while (json.hasNext() && (pendingAppCheck || pendingExceptionsCheck)) {
val nextName = json.nextName()
val discard: Boolean = when (nextName) {
"app" -> {
pendingAppCheck = false
shouldDiscardForApp(json)
}

"exceptions" -> {
pendingExceptionsCheck = false
shouldDiscardForExceptions(json)
}

else -> {
json.skipValue()
false
}
}

if (discard) {
return true
}
}

return false
}

private fun shouldDiscardForApp(json: JsonReader): Boolean {
val enabledReleaseStages = NativeInterface.getEnabledReleaseStages()
if (enabledReleaseStages.isNullOrEmpty()) {
json.skipValue()
return false
}

json.beginObject()
while (json.peek() != JsonToken.END_OBJECT) {
val nextName = json.nextName()
when (nextName) {
"releaseStage" -> {
val releaseStage = json.nextString()
if (releaseStage !in enabledReleaseStages) {
logger.d("Discarding native report due to releaseStage")
return true
}
// do not early exit, make sure the entire "app" object is consumed
// before returning
}

else -> json.skipValue()
}
}
json.endObject()

return false
}

private fun shouldDiscardForExceptions(json: JsonReader): Boolean {
json.beginArray()

while (json.peek() != JsonToken.END_ARRAY) {
if (shouldDiscardException(json)) {
logger.d("Discarding native report due to errorClass")
return true
}
}

json.endArray()

return false
}

private fun shouldDiscardException(json: JsonReader): Boolean {
json.beginObject()

while (json.peek() != JsonToken.END_OBJECT) {
val name = json.nextName()
when (name) {
"errorClass" -> {
val errorClass = json.nextString()
if (NativeInterface.isDiscardErrorClass(errorClass)) {
return true
}
// do not early exit, make sure the entire "exception" object is consumed
// before returning
}

else -> json.skipValue()
}
}

json.endObject()

return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,13 @@ static bool bsg_write_metadata_value(BSG_KSJSONEncodeContext *json,
CHECKED(JSON_LIMITED_STRING_ELEMENT(value->name, value->char_value));
break;
case BSG_METADATA_NUMBER_VALUE:
CHECKED(bsg_ksjsonaddFloatingPointElement(json, value->name,
value->double_value));
if (value->double_value == (double)((long long)value->double_value)) {
CHECKED(bsg_ksjsonaddIntegerElement(json, value->name,
(long long)value->double_value));
} else {
CHECKED(bsg_ksjsonaddFloatingPointElement(json, value->name,
value->double_value));
}
break;
case BSG_METADATA_OPAQUE_VALUE:
CHECKED(bsg_ksjsonbeginElement(json, value->name));
Expand Down