From 97e48d1c5596c231d1cfee0b01b4fbef7848a2b6 Mon Sep 17 00:00:00 2001 From: jason Date: Wed, 26 Feb 2025 15:55:41 +0000 Subject: [PATCH 1/3] fix(ndk): fixed a date calculation in the device time formatting in NDK crashes --- CHANGELOG.md | 3 + .../android/ndk/NativeJsonSerializeTest.kt | 86 ++++++++++++++++++- .../serializer/BSG_KSCrashStringConversion.c | 17 +++- .../src/test/cpp/main.c | 5 +- 4 files changed, 104 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dabff11c9..3acd49adfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ * Excess threads (over `Configuration.getMaxReportedThreads`) are trimmed more reliably when the payload is modified before sending (in an `OnSendCallback` for example) [#2148](https://github.com/bugsnag/bugsnag-android/pull/2148) +* Fixed an error calculating the device time during NDK crashes + []() + ## 6.12.0 (2025-02-18) ### Enhancements diff --git a/bugsnag-plugin-android-ndk/src/androidTest/java/com/bugsnag/android/ndk/NativeJsonSerializeTest.kt b/bugsnag-plugin-android-ndk/src/androidTest/java/com/bugsnag/android/ndk/NativeJsonSerializeTest.kt index f866a97837..795feb2a91 100644 --- a/bugsnag-plugin-android-ndk/src/androidTest/java/com/bugsnag/android/ndk/NativeJsonSerializeTest.kt +++ b/bugsnag-plugin-android-ndk/src/androidTest/java/com/bugsnag/android/ndk/NativeJsonSerializeTest.kt @@ -3,8 +3,15 @@ package com.bugsnag.android.ndk import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before +import org.junit.Ignore import org.junit.Test import java.io.File +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Date +import java.util.GregorianCalendar +import java.util.Locale +import java.util.TimeZone class NativeJsonSerializeTest { @@ -16,6 +23,8 @@ class NativeJsonSerializeTest { } private val path = File(System.getProperty("java.io.tmpdir"), this::class.simpleName!!) + private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US) + .apply { timeZone = TimeZone.getTimeZone("UTC") } @Before fun setupTmpdir() { @@ -27,13 +36,84 @@ class NativeJsonSerializeTest { path.deleteRecursively() } - external fun run(outputDir: String): Int + external fun run(outputDir: String, timestamp: Long): Int @Test - fun testPassesNativeSuite() { - verifyNativeRun(run(path.absolutePath)) + fun testPassesNativeSuiteEpoch() { + verifyNativeRun(run(path.absolutePath, 7609)) val jsonFile = path.listFiles()!!.maxByOrNull { it.lastModified() }!! val expected = loadJson("event_serialization.json") assertEquals(expected, jsonFile.readText()) } + + @Test + fun testRegression2024() { + val timestamp = GregorianCalendar(2024, 11, 30, 16, 0, 0).timeInMillis + val datestamp = dateFormat.format(Date(timestamp)) + + verifyNativeRun(run(path.absolutePath, timestamp / 1000L)) + val jsonFile = path.listFiles()!!.maxByOrNull { it.lastModified() }!! + val expected = loadJson("event_serialization.json") + .replace("\"1970-01-01T02:06:49Z\"", "\"${datestamp}\"") + assertEquals(expected, jsonFile.readText()) + } + + @Test + fun testPassesNativeSuite2024() { + val timestamp = GregorianCalendar(2024, 0, 1).timeInMillis + val datestamp = dateFormat.format(Date(timestamp)) + + verifyNativeRun(run(path.absolutePath, timestamp / 1000L)) + val jsonFile = path.listFiles()!!.maxByOrNull { it.lastModified() }!! + val expected = loadJson("event_serialization.json") + .replace("\"1970-01-01T02:06:49Z\"", "\"${datestamp}\"") + assertEquals(expected, jsonFile.readText()) + } + + @Test + fun testPassesNativeSuite2025() { + val timestamp = GregorianCalendar(2025, 1, 1).timeInMillis + val datestamp = dateFormat.format(Date(timestamp)) + + verifyNativeRun(run(path.absolutePath, timestamp / 1000L)) + val jsonFile = path.listFiles()!!.maxByOrNull { it.lastModified() }!! + val expected = loadJson("event_serialization.json") + .replace("\"1970-01-01T02:06:49Z\"", "\"${datestamp}\"") + assertEquals(expected, jsonFile.readText()) + } + + @Test + fun testPassesNativeSuiteToday() { + val now = System.currentTimeMillis() + val datestamp = dateFormat.format(Date(now)) + + verifyNativeRun(run(path.absolutePath, now / 1000L)) + val jsonFile = path.listFiles()!!.maxByOrNull { it.lastModified() }!! + val expected = loadJson("event_serialization.json") + .replace("\"1970-01-01T02:06:49Z\"", "\"${datestamp}\"") + assertEquals(expected, jsonFile.readText()) + } + + @Test + @Ignore("useful when working on the date formatting code") + fun testDecadesOfDates() { + val calendar = Calendar.getInstance().apply { add(Calendar.YEAR, -10) } + val end = Calendar.getInstance().apply { add(Calendar.YEAR, 10) } + + while (calendar < end) { + val instant = calendar.timeInMillis + val datestamp = dateFormat.format(Date(instant)) + + verifyNativeRun(run(path.absolutePath, instant / 1000L)) + val jsonFile = path.listFiles()!!.maxByOrNull { it.lastModified() }!! + val expected = loadJson("event_serialization.json") + .replace("\"1970-01-01T02:06:49Z\"", "\"${datestamp}\"") + assertEquals(expected, jsonFile.readText()) + + // move the date along 6 hours at a time + calendar.add(Calendar.HOUR, 6) + + jsonFile.delete() + } + } } diff --git a/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/BSG_KSCrashStringConversion.c b/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/BSG_KSCrashStringConversion.c index e727ec3407..229be92ec2 100644 --- a/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/BSG_KSCrashStringConversion.c +++ b/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/BSG_KSCrashStringConversion.c @@ -7,6 +7,7 @@ // #include "BSG_KSCrashStringConversion.h" +#include "utils/logger.h" #include #include #include @@ -218,9 +219,19 @@ static void safe_gmtime_r(time_t time, struct tm *out) { days -= quotient * days_per_4years; years += quotient * 4; - quotient = days / 365; - days -= quotient * 365; - years += quotient; + while (days >= 365) { + if (years % 4 == 0 && (years % 100 != 0 || years % 400 == 0)) { + if (days >= 366) { + days -= 366; + years += 1; + } else { + break; + } + } else { + days -= 365; + years += 1; + } + } out->tm_year = years - 1900; out->tm_yday = days; diff --git a/bugsnag-plugin-android-ndk/src/test/cpp/main.c b/bugsnag-plugin-android-ndk/src/test/cpp/main.c index 2ab71894fb..95b6dc3004 100644 --- a/bugsnag-plugin-android-ndk/src/test/cpp/main.c +++ b/bugsnag-plugin-android-ndk/src/test/cpp/main.c @@ -59,7 +59,7 @@ Java_com_bugsnag_android_ndk_NativeStringTest_run(JNIEnv *_env, jobject _this) { extern bool bsg_event_write(bsg_environment *env); JNIEXPORT int JNICALL Java_com_bugsnag_android_ndk_NativeJsonSerializeTest_run( - JNIEnv *_env, jobject _this, jstring _dir) { + JNIEnv *_env, jobject _this, jstring _dir, jlong timestamp) { const char *dir = (*_env)->GetStringUTFChars(_env, _dir, NULL); if (dir == NULL) { @@ -71,12 +71,15 @@ JNIEXPORT int JNICALL Java_com_bugsnag_android_ndk_NativeJsonSerializeTest_run( bugsnag_event *event = init_event(); memcpy(&env.next_event, event, sizeof(bugsnag_event)); + env.next_event.device.time = (time_t) timestamp; env.event_path = strdup(dir); + env.static_json_data = NULL; strcpy(env.event_uuid, "test-uuid"); bsg_event_write(&env); free(event); + free(env.event_path); (*_env)->ReleaseStringUTFChars(_env, _dir, dir); From 8dfe1a1ea00c3c6aba473c2528239f2a12900607 Mon Sep 17 00:00:00 2001 From: jason Date: Thu, 27 Feb 2025 10:17:27 +0000 Subject: [PATCH 2/3] test(mazerunner): added the missing @skip_android_14 --- features/support/env.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/features/support/env.rb b/features/support/env.rb index df8670cf39..b1af3f19f4 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -60,6 +60,10 @@ skip_this_scenario("Skipping scenario") if Maze.config.os_version < 5 end +Before('@skip_android_14') do |scenario| + skip_this_scenario("Skipping scenario") if Maze.config.os_version.floor == 14 +end + Before('@skip_android_13') do |scenario| skip_this_scenario("Skipping scenario") if Maze.config.os_version.floor == 13 end From 7b95973e4d3a4daef5f60bf5a5d660e993735fab Mon Sep 17 00:00:00 2001 From: jason Date: Thu, 27 Feb 2025 10:54:19 +0000 Subject: [PATCH 3/3] chore(changelog): added the PR number #2158 to the CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3acd49adfb..2459ebdf92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ [#2148](https://github.com/bugsnag/bugsnag-android/pull/2148) * Fixed an error calculating the device time during NDK crashes - []() + [#2158](https://github.com/bugsnag/bugsnag-android/pull/2158) ## 6.12.0 (2025-02-18)