diff --git a/CHANGELOG.md b/CHANGELOG.md index a7b0e579bc..23aef939fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Normalize data properties of `SentryUser` and `Breadcrumb` before sending over method channel ([#1591](https://github.com/getsentry/sentry-dart/pull/1591)) - Fixing memory leak issue in SentryFlutterPlugin (Android Plugin) ([#1588](https://github.com/getsentry/sentry-dart/pull/1588)) +- Discard empty stack frames ([#1625](https://github.com/getsentry/sentry-dart/pull/1625)) - Disable scope sync for cloned scopes ([#1628](https://github.com/getsentry/sentry-dart/pull/1628)) ### Dependencies diff --git a/flutter/lib/src/jvm/jvm_exception.dart b/flutter/lib/src/jvm/jvm_exception.dart index adaf71cac1..352092b1ed 100644 --- a/flutter/lib/src/jvm/jvm_exception.dart +++ b/flutter/lib/src/jvm/jvm_exception.dart @@ -256,8 +256,12 @@ class JvmException { frames.add(trimmed); } - final thisExceptionFrames = - thisException.map((e) => JvmFrame.parse(e)).toList(growable: false); + final thisExceptionFrames = thisException + // Sometimes stringified exceptions from the native side have + // empty lines. Discard those! + .where((line) => line.trim().isNotEmpty) + .map((e) => JvmFrame.parse(e)) + .toList(growable: false); final suppressedExceptions = supressed .map((e) => JvmException.parse(e.join(_newLine))) diff --git a/flutter/test/jvm/jvm_exception_test.dart b/flutter/test/jvm/jvm_exception_test.dart index 3202da097d..de8a3b4e29 100644 --- a/flutter/test/jvm/jvm_exception_test.dart +++ b/flutter/test/jvm/jvm_exception_test.dart @@ -40,7 +40,7 @@ void main() { 'Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]'); expect(thirdCause.thread, null); expect(thirdCause.type, 'java.sql.SQLException'); - expect(thirdCause.stackTrace.length, 6); + expect(thirdCause.stackTrace.length, 5); expect(thirdCause.causes, null); expect(thirdCause.suppressed, null); }); @@ -85,6 +85,16 @@ void main() { expect(exception.stackTrace[0].fileName, 'StandardMessageCodec.java'); expect(exception.stackTrace[0].lineNumber, 292); }); + + test('parse drops empty frames', () { + final exception = JvmException.parse(platformExceptionWithEmptyStackFrames); + expect(exception.stackTrace.length, 13); + expect(exception.stackTrace.last.className, + 'com.android.internal.os.ZygoteInit'); + expect(exception.stackTrace.last.fileName, 'ZygoteInit.java'); + expect(exception.stackTrace.last.method, 'main'); + expect(exception.stackTrace.last.lineNumber, 936); + }); } const javaExceptionWithCauses = ''' @@ -194,3 +204,21 @@ java.lang.IllegalArgumentException: Unsupported value: '[Ljava.lang.StackTraceEl at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit\$MethodAndArgsCaller.run(RuntimeInit.java:556) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1037)'''; + +const platformExceptionWithEmptyStackFrames = ''' +java.lang.RuntimeException: Catch this platform exception! + at io.sentry.samples.flutter.MainActivity\$configureFlutterEngine\$1.onMethodCall(MainActivity.kt:40) + at io.flutter.plugin.common.MethodChannel\$IncomingMethodCallHandler.onMessage(MethodChannel.java:258) + at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295) + at io.flutter.embedding.engine.dart.DartMessenger.lambda\$dispatchMessageToQueue\$0\$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322) + at io.flutter.embedding.engine.dart.DartMessenger\$\$ExternalSyntheticLambda0.run(Unknown Source:12) + at android.os.Handler.handleCallback(Handler.java:942) + at android.os.Handler.dispatchMessage(Handler.java:99) + at android.os.Looper.loopOnce(Looper.java:201) + at android.os.Looper.loop(Looper.java:288) + at android.app.ActivityThread.main(ActivityThread.java:7872) + at java.lang.reflect.Method.invoke + at com.android.internal.os.RuntimeInit\$MethodAndArgsCaller.run(RuntimeInit.java:548) + at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) + + '''; diff --git a/flutter/test/sentry_navigator_observer_test.dart b/flutter/test/sentry_navigator_observer_test.dart index d3871b4390..d4c2499efc 100644 --- a/flutter/test/sentry_navigator_observer_test.dart +++ b/flutter/test/sentry_navigator_observer_test.dart @@ -756,6 +756,7 @@ class _MockHub extends MockHub { @override final options = SentryOptions(dsn: fakeDsn); + @override late final scope = Scope(options); @override diff --git a/sqflite/lib/src/sentry_database.dart b/sqflite/lib/src/sentry_database.dart index 71147c3464..d148eb7ded 100644 --- a/sqflite/lib/src/sentry_database.dart +++ b/sqflite/lib/src/sentry_database.dart @@ -58,8 +58,11 @@ class SentryDatabase extends SentryDatabaseExecutor implements Database { @internal Hub? hub, }) : _hub = hub ?? HubAdapter(), dbName = p.basenameWithoutExtension(_database.path), - super(_database, - hub: hub, dbName: p.basenameWithoutExtension(_database.path)) { + super( + _database, + hub: hub, + dbName: p.basenameWithoutExtension(_database.path), + ) { // ignore: invalid_use_of_internal_member final options = _hub.options; options.sdk.addIntegration('SentrySqfliteTracing'); @@ -132,8 +135,12 @@ class SentryDatabase extends SentryDatabaseExecutor implements Database { setDatabaseAttributeData(span, dbName); Future newAction(Transaction txn) async { - final executor = SentryDatabaseExecutor(txn, - parentSpan: span, hub: _hub, dbName: dbName); + final executor = SentryDatabaseExecutor( + txn, + parentSpan: span, + hub: _hub, + dbName: dbName, + ); final sentrySqfliteTransaction = SentrySqfliteTransaction(executor, hub: _hub, dbName: dbName); diff --git a/sqflite/test/mocks/mocks.dart b/sqflite/test/mocks/mocks.dart index 3f0af8274c..bcafb94302 100644 --- a/sqflite/test/mocks/mocks.dart +++ b/sqflite/test/mocks/mocks.dart @@ -34,7 +34,8 @@ ISentrySpan startTransactionShim( ], customMocks: [ MockSpec( - fallbackGenerators: {#startTransaction: startTransactionShim}), + fallbackGenerators: {#startTransaction: startTransactionShim}, + ), ], ) void main() {} diff --git a/sqflite/test/sentry_batch_test.dart b/sqflite/test/sentry_batch_test.dart index ce6f59e0d5..e3b24d811a 100644 --- a/sqflite/test/sentry_batch_test.dart +++ b/sqflite/test/sentry_batch_test.dart @@ -296,7 +296,9 @@ SELECT * FROM Product'''; final span = fixture.tracer.children.last; expect(span.data[SentryDatabase.dbSystemKey], SentryDatabase.dbSystem); expect( - span.data[SentryDatabase.dbNameKey], (db as SentryDatabase).dbName); + span.data[SentryDatabase.dbNameKey], + (db as SentryDatabase).dbName, + ); await db.close(); }); @@ -313,7 +315,9 @@ SELECT * FROM Product'''; final span = fixture.tracer.children.last; expect(span.data[SentryDatabase.dbSystemKey], SentryDatabase.dbSystem); expect( - span.data[SentryDatabase.dbNameKey], (db as SentryDatabase).dbName); + span.data[SentryDatabase.dbNameKey], + (db as SentryDatabase).dbName, + ); await db.close(); }); diff --git a/sqflite/test/sentry_database_test.dart b/sqflite/test/sentry_database_test.dart index e05bd2367c..5c954426bb 100644 --- a/sqflite/test/sentry_database_test.dart +++ b/sqflite/test/sentry_database_test.dart @@ -112,7 +112,9 @@ void main() { expect(insertSpan.context.parentSpanId, trSpan.context.spanId); expect(insertSpan.status, SpanStatus.ok()); expect( - insertSpan.data[SentryDatabase.dbSystemKey], SentryDatabase.dbSystem); + insertSpan.data[SentryDatabase.dbSystemKey], + SentryDatabase.dbSystem, + ); expect(insertSpan.data[SentryDatabase.dbNameKey], inMemoryDatabasePath); expect(