Skip to content

Commit

Permalink
Provide a way to cause an example native crash from Flutter (#2239)
Browse files Browse the repository at this point in the history
* add SentryFlutter.nativeCrash() for Android and iOS

* add changelog entry

* remove unused variable

* improved kotlin implementation

* fix kotlin analysis warnings

* Update CHANGELOG.md

Co-authored-by: Giancarlo Buenaflor <giancarlo_buenaflor@yahoo.com>

* fix kotlin linter errors

* remove whitespace

* add Description for nativeCrash

---------

Co-authored-by: Giancarlo Buenaflor <giancarlo_buenaflor@yahoo.com>
  • Loading branch information
martinhaintz and buenaflor authored Aug 21, 2024
1 parent 256df44 commit 9555112
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 19 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Features

- Add `SentryFlutter.nativeCrash()` using MethodChannels for Android and iOS ([#2239](https://github.com/getsentry/sentry-dart/pull/2239))
- This can be used to test if native crash reporting works
- Add `ignoreRoutes` parameter to `SentryNavigatorObserver`. ([#2218](https://github.com/getsentry/sentry-dart/pull/2218))
- This will ignore the Routes and prevent the Route from being pushed to the Sentry server.
- Ignored routes will also create no TTID and TTFD spans.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.sentry.flutter
import android.app.Activity
import android.content.Context
import android.os.Build
import android.os.Looper
import android.util.Log
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
Expand Down Expand Up @@ -49,8 +50,8 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {

sentryFlutter =
SentryFlutter(
androidSdk = androidSdk,
nativeSdk = nativeSdk,
androidSdk = ANDROID_SDK,
nativeSdk = NATIVE_SDK,
)
}

Expand All @@ -74,6 +75,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
"removeTag" -> removeTag(call.argument("key"), result)
"loadContexts" -> loadContexts(result)
"displayRefreshRate" -> displayRefreshRate(result)
"nativeCrash" -> crash()
else -> result.notImplemented()
}
}
Expand Down Expand Up @@ -413,16 +415,17 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}

companion object {
private const val FLUTTER_SDK = "sentry.dart.flutter"
private const val ANDROID_SDK = "sentry.java.android.flutter"
private const val NATIVE_SDK = "sentry.native.android.flutter"
private const val NATIVE_CRASH_WAIT_TIME = 500L

private const val flutterSdk = "sentry.dart.flutter"
private const val androidSdk = "sentry.java.android.flutter"
private const val nativeSdk = "sentry.native.android.flutter"
private fun setEventOriginTag(event: SentryEvent) {
event.sdk?.let {
when (it.name) {
flutterSdk -> setEventEnvironmentTag(event, "flutter", "dart")
androidSdk -> setEventEnvironmentTag(event, environment = "java")
nativeSdk -> setEventEnvironmentTag(event, environment = "native")
FLUTTER_SDK -> setEventEnvironmentTag(event, "flutter", "dart")
ANDROID_SDK -> setEventEnvironmentTag(event, environment = "java")
NATIVE_SDK -> setEventEnvironmentTag(event, environment = "native")
else -> return
}
}
Expand All @@ -439,7 +442,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {

private fun addPackages(event: SentryEvent, sdk: SdkVersion?) {
event.sdk?.let {
if (it.name == flutterSdk) {
if (it.name == FLUTTER_SDK) {
sdk?.packageSet?.forEach { sentryPackage ->
it.addPackage(sentryPackage.name, sentryPackage.version)
}
Expand All @@ -449,6 +452,13 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}
}
}

private fun crash() {
val exception = RuntimeException("FlutterSentry Native Integration: Sample RuntimeException")
val mainThread = Looper.getMainLooper().thread
mainThread.uncaughtExceptionHandler.uncaughtException(mainThread, exception)
mainThread.join(NATIVE_CRASH_WAIT_TIME)
}
}

private fun loadContexts(result: Result) {
Expand Down
12 changes: 12 additions & 0 deletions flutter/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,12 @@ class AndroidExample extends StatelessWidget {
},
child: const Text('Platform exception'),
),
ElevatedButton(
onPressed: () async {
SentryFlutter.nativeCrash();
},
child: const Text('Sentry.nativeCrash'),
),
]);
}
}
Expand Down Expand Up @@ -870,6 +876,12 @@ class CocoaExample extends StatelessWidget {
},
child: const Text('Objective-C SEGFAULT'),
),
ElevatedButton(
onPressed: () async {
SentryFlutter.nativeCrash();
},
child: const Text('Sentry.nativeCrash'),
),
],
);
}
Expand Down
7 changes: 7 additions & 0 deletions flutter/ios/Classes/SentryFlutterPluginApple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin {
case "resumeAppHangTracking":
resumeAppHangTracking(result)

case "nativeCrash":
crash()

default:
result(FlutterMethodNotImplemented)
}
Expand Down Expand Up @@ -729,6 +732,10 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin {
SentrySDK.resumeAppHangTracking()
result("")
}

private func crash() {
SentrySDK.crash()
}
}

// swiftlint:enable function_body_length
Expand Down
2 changes: 2 additions & 0 deletions flutter/lib/src/native/sentry_native_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,6 @@ abstract class SentryNativeBinding {
Future<void> pauseAppHangTracking();

Future<void> resumeAppHangTracking();

Future<void> nativeCrash();
}
3 changes: 3 additions & 0 deletions flutter/lib/src/native/sentry_native_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,7 @@ class SentryNativeChannel
@override
Future<void> resumeAppHangTracking() =>
_channel.invokeMethod('resumeAppHangTracking');

@override
Future<void> nativeCrash() => _channel.invokeMethod('nativeCrash');
}
31 changes: 21 additions & 10 deletions flutter/lib/src/sentry_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,7 @@ mixin SentryFlutter {
/// Only for iOS and macOS.
static Future<void> pauseAppHangTracking() {
if (_native == null) {
// ignore: invalid_use_of_internal_member
Sentry.currentHub.options.logger(
SentryLevel.debug,
'Native integration is not available. Make sure SentryFlutter is initialized before accessing the pauseAppHangTracking API.',
);
_logNativeIntegrationNotAvailable("pauseAppHangTracking");
return Future<void>.value();
}
return _native!.pauseAppHangTracking();
Expand All @@ -263,11 +259,7 @@ mixin SentryFlutter {
/// Only for iOS and macOS
static Future<void> resumeAppHangTracking() {
if (_native == null) {
// ignore: invalid_use_of_internal_member
Sentry.currentHub.options.logger(
SentryLevel.debug,
'Native integration is not available. Make sure SentryFlutter is initialized before accessing the resumeAppHangTracking API.',
);
_logNativeIntegrationNotAvailable("resumeAppHangTracking");
return Future<void>.value();
}
return _native!.resumeAppHangTracking();
Expand All @@ -280,4 +272,23 @@ mixin SentryFlutter {
static set native(SentryNativeBinding? value) => _native = value;

static SentryNativeBinding? _native;

/// Use `nativeCrash()` to crash the native implementation and test/debug the crash reporting for native code.
/// This should not be used in production code.
/// Only for Android, iOS and macOS
static Future<void> nativeCrash() {
if (_native == null) {
_logNativeIntegrationNotAvailable("nativeCrash");
return Future<void>.value();
}
return _native!.nativeCrash();
}

static void _logNativeIntegrationNotAvailable(String methodName) {
// ignore: invalid_use_of_internal_member
Sentry.currentHub.options.logger(
SentryLevel.debug,
'Native integration is not available. Make sure SentryFlutter is initialized before accessing the $methodName API.',
);
}
}
9 changes: 9 additions & 0 deletions flutter/test/sentry_native_channel_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,15 @@ void main() {

verify(channel.invokeMethod('resumeAppHangTracking'));
});

test('nativeCrash', () async {
when(channel.invokeMethod('nativeCrash'))
.thenAnswer((_) => Future.value());

await sut.nativeCrash();

verify(channel.invokeMethod('nativeCrash'));
});
});
}
}

0 comments on commit 9555112

Please sign in to comment.