Skip to content

Only enable load debug image integration for obfuscated apps #2907

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

Open
wants to merge 6 commits into
base: feat-web/debug-id
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
- [Flutter Web]: add debug ids to events ([#2917](https://github.com/getsentry/sentry-dart/pull/2917))
- This allows support for symbolication based on [debug ids](https://docs.sentry.io/platforms/javascript/sourcemaps/troubleshooting_js/debug-ids/)

### Enhancements

- Only enable load debug image integration for obfuscated apps ([#2907](https://github.com/getsentry/sentry-dart/pull/2907))

## 9.0.0-RC

### Various fixes & improvements
Expand Down
12 changes: 7 additions & 5 deletions dart/lib/src/load_dart_debug_images_integration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,22 @@ import 'protocol/sentry_stack_trace.dart';
import 'sentry_options.dart';

class LoadDartDebugImagesIntegration extends Integration<SentryOptions> {
static const integrationName = 'LoadDartDebugImagesIntegration';
static const integrationName = 'LoadDartDebugImages';

@override
void call(Hub hub, SentryOptions options) {
if (options.enableDartSymbolication) {
options.addEventProcessor(LoadImageIntegrationEventProcessor(options));
if (options.enableDartSymbolication &&
(options.runtimeChecker.isAppObfuscated() || options.platform.isWeb)) {
options.addEventProcessor(
LoadDartDebugImagesIntegrationEventProcessor(options));
options.sdk.addIntegration(integrationName);
}
}
}

@internal
class LoadImageIntegrationEventProcessor implements EventProcessor {
LoadImageIntegrationEventProcessor(this._options);
class LoadDartDebugImagesIntegrationEventProcessor implements EventProcessor {
LoadDartDebugImagesIntegrationEventProcessor(this._options);

final SentryOptions _options;

Expand Down
8 changes: 8 additions & 0 deletions dart/lib/src/runtime_checker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ class RuntimeChecker {
return const bool.fromEnvironment('dart.vm.profile', defaultValue: false);
}

/// Check if the Dart code is obfuscated.
bool isAppObfuscated() {
// In non-obfuscated builds, this will return "RuntimeChecker"
// In obfuscated builds, this will return something like "a" or other short identifier
final typeName = runtimeType.toString();
return !typeName.contains('RuntimeChecker');
}

final bool isRootZone;

String get compileMode {
Expand Down
52 changes: 49 additions & 3 deletions dart/test/load_dart_debug_images_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:sentry/src/platform/mock_platform.dart';
import 'package:sentry/src/sentry_stack_trace_factory.dart';
import 'package:test/test.dart';

import 'mocks/mock_runtime_checker.dart';
import 'test_utils.dart';

void main() {
Expand All @@ -26,6 +27,7 @@ void main() {
setUp(() {
fixture = Fixture();
fixture.options.platform = platform;
fixture.callIntegration();
});

test('adds itself to sdk.integrations', () {
Expand All @@ -40,7 +42,7 @@ void main() {
expect(fixture.options.eventProcessors.length, 1);
expect(
fixture.options.eventProcessors.first.runtimeType.toString(),
'LoadImageIntegrationEventProcessor',
'LoadDartDebugImagesIntegrationEventProcessor',
);
});

Expand Down Expand Up @@ -191,8 +193,51 @@ isolate_dso_base: 10000000
});
}

test('does not add itself to sdk.integrations if app is not obfuscated', () {
final fixture = Fixture()
..options.runtimeChecker = MockRuntimeChecker(isObfuscated: false);
fixture.callIntegration();
expect(
fixture.options.sdk.integrations
.contains(LoadDartDebugImagesIntegration.integrationName),
false,
);
});

test('does not add event processor to options if app is not obfuscated', () {
final fixture = Fixture()
..options.runtimeChecker = MockRuntimeChecker(isObfuscated: false);
fixture.callIntegration();
expect(fixture.options.eventProcessors.length, 0);
});

test(
'does add itself to sdk.integrations if app is not obfuscated but platform is web',
() {
final fixture = Fixture()
..options.runtimeChecker = MockRuntimeChecker(isObfuscated: false)
..options.platform = MockPlatform(isWeb: true);
fixture.callIntegration();
expect(
fixture.options.sdk.integrations
.contains(LoadDartDebugImagesIntegration.integrationName),
true,
);
});

test(
'does add event processor to options if app is not obfuscated but platform is web',
() {
final fixture = Fixture()
..options.runtimeChecker = MockRuntimeChecker(isObfuscated: false)
..options.platform = MockPlatform(isWeb: true);
fixture.callIntegration();
expect(fixture.options.eventProcessors.length, 1);
});

test('debug image is null on unsupported platforms', () async {
final fixture = Fixture()..options.platform = MockPlatform.linux();
fixture.callIntegration();
final event = fixture.newEvent(stackTrace: fixture.parse('''
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
build_id: 'b680cb890f9e3c12a24b172d050dec73'
Expand All @@ -205,10 +250,11 @@ isolate_dso_base: 40000000
}

class Fixture {
final options = defaultTestOptions();
final options = defaultTestOptions()
..runtimeChecker = MockRuntimeChecker(isObfuscated: true);
late final factory = SentryStackTraceFactory(options);

Fixture() {
void callIntegration() {
final integration = LoadDartDebugImagesIntegration();
integration.call(Hub(options), options);
}
Expand Down
5 changes: 5 additions & 0 deletions dart/test/mocks/mock_runtime_checker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ class MockRuntimeChecker extends RuntimeChecker with NoSuchMethodProvider {
this.isDebug = false,
this.isProfile = false,
this.isRelease = false,
this.isObfuscated = false,
bool isRootZone = true,
}) : super(isRootZone: isRootZone);

final bool isDebug;
final bool isProfile;
final bool isRelease;
final bool isObfuscated;

@override
bool isDebugMode() => isDebug;
Expand All @@ -22,4 +24,7 @@ class MockRuntimeChecker extends RuntimeChecker with NoSuchMethodProvider {

@override
bool isReleaseMode() => isRelease;

@override
bool isAppObfuscated() => isObfuscated;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,25 @@ class LoadNativeDebugImagesIntegration

@override
void call(Hub hub, SentryFlutterOptions options) {
options.addEventProcessor(
_LoadImageListIntegrationEventProcessor(options, _native),
);
options.sdk.addIntegration(integrationName);
// ignore: invalid_use_of_internal_member
if (options.runtimeChecker.isAppObfuscated()) {
options.addEventProcessor(
_LoadNativeDebugImagesIntegrationEventProcessor(options, _native),
);
options.sdk.addIntegration(integrationName);
}
}
}

class _LoadImageListIntegrationEventProcessor implements EventProcessor {
_LoadImageListIntegrationEventProcessor(this._options, this._native);
class _LoadNativeDebugImagesIntegrationEventProcessor
implements EventProcessor {
_LoadNativeDebugImagesIntegrationEventProcessor(this._options, this._native);

final SentryFlutterOptions _options;
final SentryNativeBinding _native;

late final _dartProcessor = LoadImageIntegrationEventProcessor(_options);
late final _dartProcessor =
LoadDartDebugImagesIntegrationEventProcessor(_options);

@override
Future<SentryEvent?> apply(SentryEvent event, Hint hint) async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:mockito/mockito.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_flutter/src/integrations/native_load_debug_images_integration.dart';

import '../mocks.dart';
import 'fixture.dart';

void main() {
Expand All @@ -26,6 +27,7 @@ void main() {

setUp(() async {
fixture = IntegrationTestFixture(LoadNativeDebugImagesIntegration.new);
fixture.options.runtimeChecker = MockRuntimeChecker(isObfuscated: true);
when(fixture.binding.loadDebugImages(any))
.thenAnswer((_) async => imageList.toList());
await fixture.registerIntegration();
Expand Down Expand Up @@ -73,8 +75,8 @@ void main() {

test('Event processor adds image list to the event', () async {
final ep = fixture.options.eventProcessors.first;
expect(
ep.runtimeType.toString(), "_LoadImageListIntegrationEventProcessor");
expect(ep.runtimeType.toString(),
"_LoadNativeDebugImagesIntegrationEventProcessor");
SentryEvent? event = _getEvent();
event = await ep.apply(event, Hint());

Expand Down Expand Up @@ -104,6 +106,28 @@ void main() {
verifyNever(fixture.binding.loadDebugImages(any));
});
});

test('does not add itself to sdk.integrations if app is not obfuscated',
() async {
final fixture =
IntegrationTestFixture(LoadNativeDebugImagesIntegration.new);
fixture.options.runtimeChecker = MockRuntimeChecker();
await fixture.registerIntegration();
expect(
fixture.options.sdk.integrations
.contains(LoadNativeDebugImagesIntegration.integrationName),
false,
);
});

test('does not add event processor to options if app is not obfuscated',
() async {
final fixture =
IntegrationTestFixture(LoadNativeDebugImagesIntegration.new);
fixture.options.runtimeChecker = MockRuntimeChecker();
await fixture.registerIntegration();
expect(fixture.options.eventProcessors.length, 0);
});
}

SentryEvent _getEvent() {
Expand Down
5 changes: 5 additions & 0 deletions flutter/test/mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ void main() {}
class MockRuntimeChecker with NoSuchMethodProvider implements RuntimeChecker {
MockRuntimeChecker({
this.buildMode = MockRuntimeCheckerBuildMode.debug,
this.isObfuscated = false,
this.isRoot = true,
});

final MockRuntimeCheckerBuildMode buildMode;
final bool isObfuscated;
final bool isRoot;

@override
Expand All @@ -88,6 +90,9 @@ class MockRuntimeChecker with NoSuchMethodProvider implements RuntimeChecker {
@override
bool isReleaseMode() => buildMode == MockRuntimeCheckerBuildMode.release;

@override
bool isAppObfuscated() => isObfuscated;

@override
bool get isRootZone => isRoot;
}
Expand Down
Loading