From 034d78cabdaf51f6fdb003c94dae7123bc0cc724 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 19:14:24 +0200 Subject: [PATCH 01/40] add symbolication --- dart/example/bin/example.dart | 27 +- dart/example/pubspec.yaml | 1 + dart/lib/sentry.dart | 2 + dart/lib/src/constants.dart | 21 + dart/lib/src/load_image_integration.dart | 37 + dart/lib/src/protocol/sentry_event.dart | 5 + dart/lib/src/protocol/sentry_transaction.dart | 3 + dart/lib/src/sentry.dart | 3 + dart/lib/src/sentry_client.dart | 2 + dart/lib/src/sentry_options.dart | 4 + dart/lib/src/symbolizer.dart | 126 ++ dart/test/dart_symbolizer_test.dart | 57 + .../ios/Runner.xcodeproj/project.pbxproj | 3 + flutter/example/ios/Runner/AppDelegate.swift | 2 +- flutter/example/lib/isar/user.dart | 24 +- flutter/example/lib/isar/user.g.dart | 1106 ++++++++--------- flutter/example/lib/main.dart | 81 +- .../macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../example/macos/Runner/AppDelegate.swift | 2 +- flutter/example/pubspec.yaml | 8 +- flutter/example/pubspec_overrides.yaml | 4 +- .../load_image_list_integration.dart | 5 +- flutter/lib/src/sentry_flutter.dart | 3 + 24 files changed, 901 insertions(+), 629 deletions(-) create mode 100644 dart/lib/src/constants.dart create mode 100644 dart/lib/src/load_image_integration.dart create mode 100644 dart/lib/src/symbolizer.dart create mode 100644 dart/test/dart_symbolizer_test.dart diff --git a/dart/example/bin/example.dart b/dart/example/bin/example.dart index d7530f5874..3aa5ff7fde 100644 --- a/dart/example/bin/example.dart +++ b/dart/example/bin/example.dart @@ -5,23 +5,28 @@ import 'dart:async'; import 'package:sentry/sentry.dart'; +import 'dart:typed_data'; + +import 'package:uuid/uuid.dart'; import 'event_example.dart'; /// Sends a test exception report to Sentry.io using this Dart client. Future main() async { // ATTENTION: Change the DSN below with your own to see the events in Sentry. Get one at sentry.io - const dsn = - 'https://e85b375ffb9f43cf8bdf9787768149e0@o447951.ingest.sentry.io/5428562'; - - await Sentry.init( - (options) => options - ..dsn = dsn - ..debug = true - ..sendDefaultPii = true - ..addEventProcessor(TagEventProcessor()), - appRunner: runApp, - ); + // const dsn = + // 'https://e85b375ffb9f43cf8bdf9787768149e0@o447951.ingest.sentry.io/5428562'; + // + // await Sentry.init( + // (options) => options + // ..dsn = dsn + // ..debug = true + // ..sendDefaultPii = true + // ..addEventProcessor(TagEventProcessor()), + // appRunner: runApp, + // ); + + print(convertCodeIdToDebugId('63956a4567aaf99a10d24405cf6a48f3')); } Future runApp() async { diff --git a/dart/example/pubspec.yaml b/dart/example/pubspec.yaml index e344bbe25a..a120d46484 100644 --- a/dart/example/pubspec.yaml +++ b/dart/example/pubspec.yaml @@ -10,6 +10,7 @@ environment: dependencies: sentry: path: ../../dart + uuid: ^3.0.0 dev_dependencies: lints: ^2.0.0 diff --git a/dart/lib/sentry.dart b/dart/lib/sentry.dart index e9cae9d666..81ab217d9e 100644 --- a/dart/lib/sentry.dart +++ b/dart/lib/sentry.dart @@ -58,3 +58,5 @@ export 'src/utils.dart'; export 'src/spotlight.dart'; // proxy export 'src/protocol/sentry_proxy.dart'; +export 'src/symbolizer.dart'; +export 'src/load_image_integration.dart'; diff --git a/dart/lib/src/constants.dart b/dart/lib/src/constants.dart new file mode 100644 index 0000000000..00dbef1403 --- /dev/null +++ b/dart/lib/src/constants.dart @@ -0,0 +1,21 @@ +// The section name in which the build ID is stored as a note. +const String buildIdSectionName = '.note.gnu.build-id'; +// The type of a build ID note. +const int buildIdNoteType = 3; +// The name of a build ID note. +const String buildIdNoteName = 'GNU'; + +// The dynamic symbol name for the VM instructions section. +const String vmSymbolName = '_kDartVmSnapshotInstructions'; + +// The dynamic symbol name for the VM data section. +const String vmDataSymbolName = '_kDartVmSnapshotData'; + +// The dynamic symbol name for the isolate instructions section. +const String isolateSymbolName = '_kDartIsolateSnapshotInstructions'; + +// The dynamic symbol name for the isolate data section. +const String isolateDataSymbolName = '_kDartIsolateSnapshotData'; + +// The ID for the root loading unit. +const int rootLoadingUnitId = 1; diff --git a/dart/lib/src/load_image_integration.dart b/dart/lib/src/load_image_integration.dart new file mode 100644 index 0000000000..ca0adb9a4e --- /dev/null +++ b/dart/lib/src/load_image_integration.dart @@ -0,0 +1,37 @@ +import 'package:sentry/sentry.dart'; + +abstract class ImageLoadingIntegration extends Integration {} + +class DartImageLoadingIntegration implements ImageLoadingIntegration { + @override + void call(Hub hub, SentryOptions options) { + options.addEventProcessor( + _LoadImageListIntegrationEventProcessor(DartSymbolizer(options))); + options.sdk.addIntegration('loadImageIntegration'); + } + + @override + void close() {} +} + +class _LoadImageListIntegrationEventProcessor implements EventProcessor { + _LoadImageListIntegrationEventProcessor(this._symbolizer); + + final DartSymbolizer _symbolizer; + + @override + Future apply(SentryEvent event, Hint hint) async { + if (event.stackTrace == null) { + return event; + } + print('stacktrace: ${event.stackTrace}'); + final image = _symbolizer.toImage(event.stackTrace!); + print('image: $image'); + if (image == null) { + return event; + } + final debugMeta = DebugMeta(images: [image]); + event = event.copyWith(debugMeta: debugMeta); + return event; + } +} diff --git a/dart/lib/src/protocol/sentry_event.dart b/dart/lib/src/protocol/sentry_event.dart index 1b2765c426..4200224126 100644 --- a/dart/lib/src/protocol/sentry_event.dart +++ b/dart/lib/src/protocol/sentry_event.dart @@ -39,6 +39,7 @@ class SentryEvent with SentryEventLike { this.debugMeta, this.type, this.unknown, + this.stackTrace, }) : eventId = eventId ?? SentryId.newId(), timestamp = timestamp ?? getUtcDateTime(), contexts = contexts ?? Contexts(), @@ -194,6 +195,8 @@ class SentryEvent with SentryEventLike { @internal final Map? unknown; + final StackTrace? stackTrace; + @override SentryEvent copyWith({ SentryId? eventId, @@ -224,6 +227,7 @@ class SentryEvent with SentryEventLike { List? exceptions, List? threads, String? type, + StackTrace? stackTrace, }) => SentryEvent( eventId: eventId ?? this.eventId, @@ -257,6 +261,7 @@ class SentryEvent with SentryEventLike { threads: (threads != null ? List.from(threads) : null) ?? this.threads, type: type ?? this.type, unknown: unknown, + stackTrace: stackTrace ?? this.stackTrace, ); /// Deserializes a [SentryEvent] from JSON [Map]. diff --git a/dart/lib/src/protocol/sentry_transaction.dart b/dart/lib/src/protocol/sentry_transaction.dart index eea319aa41..678cea4f7a 100644 --- a/dart/lib/src/protocol/sentry_transaction.dart +++ b/dart/lib/src/protocol/sentry_transaction.dart @@ -40,6 +40,7 @@ class SentryTransaction extends SentryEvent { Map? measurements, Map>? metricSummaries, SentryTransactionInfo? transactionInfo, + StackTrace? stackTrace, }) : super( timestamp: timestamp ?? tracer.endTimestamp, transaction: transaction ?? tracer.name, @@ -139,6 +140,7 @@ class SentryTransaction extends SentryEvent { Map? measurements, Map>? metricSummaries, SentryTransactionInfo? transactionInfo, + StackTrace? stackTrace, }) => SentryTransaction( tracer, @@ -167,5 +169,6 @@ class SentryTransaction extends SentryEvent { (metricSummaries != null ? Map.from(metricSummaries) : null) ?? this.metricSummaries, transactionInfo: transactionInfo ?? this.transactionInfo, + stackTrace: stackTrace ?? this.stackTrace, ); } diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index a3ac51e818..bbfc3dcd7b 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; import 'dart_exception_type_identifier.dart'; +import 'load_image_integration.dart'; import 'metrics/metrics_api.dart'; import 'run_zoned_guarded_integration.dart'; import 'event_processor/enricher/enricher_event_processor.dart'; @@ -83,6 +84,8 @@ class Sentry { options.addIntegrationByIndex(0, IsolateErrorIntegration()); } + options.addIntegration(DartImageLoadingIntegration()); + options.addEventProcessor(EnricherEventProcessor(options)); options.addEventProcessor(ExceptionEventProcessor(options)); options.addEventProcessor(DeduplicationEventProcessor(options)); diff --git a/dart/lib/src/sentry_client.dart b/dart/lib/src/sentry_client.dart index c4ebac3db5..1a1fcc0444 100644 --- a/dart/lib/src/sentry_client.dart +++ b/dart/lib/src/sentry_client.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:meta/meta.dart'; +import '../sentry.dart'; import 'client_reports/client_report_recorder.dart'; import 'client_reports/discard_reason.dart'; import 'event_processor.dart'; @@ -207,6 +208,7 @@ class SentryClient { release: event.release ?? _options.release, sdk: event.sdk ?? _options.sdk, platform: event.platform ?? sdkPlatform(_options.platformChecker.isWeb), + stackTrace: stackTrace, ); if (event is SentryTransaction) { diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 2b1771a2b5..5d05162fb5 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -358,6 +358,10 @@ class SentryOptions { _ignoredExceptionsForType.contains(exception.runtimeType); } + /// If enabled, the SDK will attempt to symbolicate pure Dart stack traces. + /// Default is `false` + bool symbolicateDartStacktrace = true; + @internal late ClientReportRecorder recorder = NoOpClientReportRecorder(); diff --git a/dart/lib/src/symbolizer.dart b/dart/lib/src/symbolizer.dart new file mode 100644 index 0000000000..9088ab91e5 --- /dev/null +++ b/dart/lib/src/symbolizer.dart @@ -0,0 +1,126 @@ +import 'dart:typed_data'; +import '../sentry.dart'; + +class DartSymbolizer { + final SentryOptions _options; + + // Header information + String? _arch; + String? _buildId; + String? _isolateDsoBase; + + // Regular expressions for parsing header lines + static const String _headerStartLine = + '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***'; + static final RegExp _osArchLineRegex = RegExp( + r'os(?:=|: )(\S+?),? arch(?:=|: )(\S+?),? comp(?:=|: )(yes|no),? sim(?:=|: )(yes|no)'); + static final RegExp _buildIdRegex = RegExp(r"build_id(?:=|: )'([\da-f]+)'"); + static final RegExp _isolateDsoBaseLineRegex = + RegExp(r'isolate_dso_base(?:=|: )([\da-f]+)'); + + DartSymbolizer(this._options); + + DebugImage? toImage(StackTrace stackTrace) { + _parseStackTrace(stackTrace); + return _createDebugImage(); + } + + void _parseStackTrace(StackTrace stackTrace) { + final lines = stackTrace.toString().split('\n'); + for (final line in lines) { + if (_tryParseHeaderLine(line)) continue; + } + } + + bool _tryParseHeaderLine(String line) { + if (line.contains(_headerStartLine)) { + _arch = _buildId = _isolateDsoBase = null; + return true; + } + + final parsers = [ + _parseOsArchLine, + _parseBuildIdLine, + _parseIsolateDsoBaseLine, + ]; + + return parsers.any((parser) => parser(line)); + } + + bool _parseOsArchLine(String line) { + final match = _osArchLineRegex.firstMatch(line); + if (match != null) { + _arch = match[2]; + return true; + } + return false; + } + + bool _parseBuildIdLine(String line) { + final match = _buildIdRegex.firstMatch(line); + if (match != null) { + _buildId = match[1]; + return true; + } + return false; + } + + bool _parseIsolateDsoBaseLine(String line) { + final match = _isolateDsoBaseLineRegex.firstMatch(line); + if (match != null) { + _isolateDsoBase = match[1]; + return true; + } + return false; + } + + DebugImage? _createDebugImage() { + if (_buildId == null || _isolateDsoBase == null) { + // TODO: log + return null; + } + + final type = _options.platformChecker.platform.isAndroid ? 'elf' : 'macho'; + return DebugImage( + type: type, + imageAddr: '0x$_isolateDsoBase', + debugId: _convertCodeIdToDebugId(_buildId!), + codeId: _buildId!, + arch: _arch, + ); + } + + // Debug identifier is the little-endian UUID representation of the first 16-bytes of + // the build ID. + String _convertCodeIdToDebugId(String codeId) { + codeId = codeId.replaceAll(' ', ''); + if (codeId.length < 32) { + // todo: don't throw + throw ArgumentError( + 'Code ID must be at least 32 hexadecimal characters long'); + } + + final first16Bytes = codeId.substring(0, 32); + final byteData = Uint8List.fromList(List.generate(16, + (i) => int.parse(first16Bytes.substring(i * 2, i * 2 + 2), radix: 16))); + + final buffer = byteData.buffer.asByteData(); + final timeLow = buffer.getUint32(0, Endian.little); + final timeMid = buffer.getUint16(4, Endian.little); + final timeHiAndVersion = buffer.getUint16(6, Endian.little); + final clockSeqHiAndReserved = buffer.getUint8(8); + final clockSeqLow = buffer.getUint8(9); + + return [ + timeLow.toRadixString(16).padLeft(8, '0'), + timeMid.toRadixString(16).padLeft(4, '0'), + timeHiAndVersion.toRadixString(16).padLeft(4, '0'), + clockSeqHiAndReserved.toRadixString(16).padLeft(2, '0') + + clockSeqLow.toRadixString(16).padLeft(2, '0'), + byteData + .sublist(10) + .map((b) => b.toRadixString(16).padLeft(2, '0')) + .join() + ].join('-'); + } +} diff --git a/dart/test/dart_symbolizer_test.dart b/dart/test/dart_symbolizer_test.dart new file mode 100644 index 0000000000..581744032f --- /dev/null +++ b/dart/test/dart_symbolizer_test.dart @@ -0,0 +1,57 @@ +import 'package:test/test.dart'; +import 'package:sentry/sentry.dart'; + +import 'mocks.dart'; +import 'mocks/mock_platform.dart'; +import 'mocks/mock_platform_checker.dart'; + +void main() { + late DartSymbolizer symbolizer; + late SentryOptions options; + late MockPlatformChecker mockPlatformChecker; + + setUp(() { + options = SentryOptions(dsn: fakeDsn); + symbolizer = DartSymbolizer(options); + }); + + test('Symbolizer correctly parses a valid stack trace header on Android', () { + mockPlatformChecker = MockPlatformChecker(platform: MockPlatform.android()); + options.platformChecker = mockPlatformChecker; + + final validStackTrace = StackTrace.fromString(''' +*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** +os: android, arch: arm64, comp: yes, sim: no +build_id: 'f1c3bcc0279865fe3058404b2831d9e64135386c' +isolate_dso_base: 0f00000000 +'''); + + final debugImage = symbolizer.toImage(validStackTrace)!; + + expect(debugImage.type, equals('elf')); + expect(debugImage.imageAddr, equals('0x0f00000000')); + expect( + debugImage.codeId, equals('f1c3bcc0279865fe3058404b2831d9e64135386c')); + expect(debugImage.debugId, equals('c0bcc3f1-9827-fe65-3058-404b2831d9e6')); + }); + + test('Symbolizer correctly parses a valid stack trace header on iOS', () { + mockPlatformChecker = MockPlatformChecker(platform: MockPlatform.iOS()); + options.platformChecker = mockPlatformChecker; + + final validStackTrace = StackTrace.fromString(''' +*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** +os: ios, arch: arm64, comp: yes, sim: no +build_id: 'f1c3bcc0279865fe3058404b2831d9e64135386c' +isolate_dso_base: 0f00000000 +'''); + + final debugImage = symbolizer.toImage(validStackTrace)!; + + expect(debugImage.type, equals('macho')); + expect(debugImage.imageAddr, equals('0x0f00000000')); + expect( + debugImage.codeId, equals('f1c3bcc0279865fe3058404b2831d9e64135386c')); + expect(debugImage.debugId, equals('c0bcc3f1-9827-fe65-3058-404b2831d9e6')); + }); +} diff --git a/flutter/example/ios/Runner.xcodeproj/project.pbxproj b/flutter/example/ios/Runner.xcodeproj/project.pbxproj index 11cb23fc9d..b02216e2f6 100644 --- a/flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -734,6 +734,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 97JCY7859U; ENABLE_BITCODE = NO; @@ -753,6 +755,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/flutter/example/ios/Runner/AppDelegate.swift b/flutter/example/ios/Runner/AppDelegate.swift index a231cc9c60..c24cacbbb2 100644 --- a/flutter/example/ios/Runner/AppDelegate.swift +++ b/flutter/example/ios/Runner/AppDelegate.swift @@ -2,7 +2,7 @@ import UIKit import Flutter import Sentry -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { private let _channel = "example.flutter.sentry.io" diff --git a/flutter/example/lib/isar/user.dart b/flutter/example/lib/isar/user.dart index f255d2389d..3ca768ae45 100644 --- a/flutter/example/lib/isar/user.dart +++ b/flutter/example/lib/isar/user.dart @@ -1,12 +1,12 @@ -import 'package:isar/isar.dart'; - -part 'user.g.dart'; - -@collection -class User { - Id id = Isar.autoIncrement; - - String? name; - - int? age; -} +// import 'package:isar/isar.dart'; +// +// part 'user.g.dart'; +// +// @collection +// class User { +// Id id = Isar.autoIncrement; +// +// String? name; +// +// int? age; +// } diff --git a/flutter/example/lib/isar/user.g.dart b/flutter/example/lib/isar/user.g.dart index c4d7ef985f..c90f98fa8f 100644 --- a/flutter/example/lib/isar/user.g.dart +++ b/flutter/example/lib/isar/user.g.dart @@ -1,553 +1,553 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'user.dart'; - -// ************************************************************************** -// IsarCollectionGenerator -// ************************************************************************** - -// coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types - -extension GetUserCollection on Isar { - IsarCollection get users => this.collection(); -} - -final UserSchema = CollectionSchema( - name: r'User', - id: BigInt.parse("-7838171048429979076").toInt(), - properties: { - r'age': PropertySchema( - id: BigInt.parse("0").toInt(), - name: r'age', - type: IsarType.long, - ), - r'name': PropertySchema( - id: BigInt.parse("1").toInt(), - name: r'name', - type: IsarType.string, - ) - }, - estimateSize: _userEstimateSize, - serialize: _userSerialize, - deserialize: _userDeserialize, - deserializeProp: _userDeserializeProp, - idName: r'id', - indexes: {}, - links: {}, - embeddedSchemas: {}, - getId: _userGetId, - getLinks: _userGetLinks, - attach: _userAttach, - version: '3.1.0', -); - -int _userEstimateSize( - User object, - List offsets, - Map> allOffsets, -) { - var bytesCount = offsets.last; - { - final value = object.name; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - return bytesCount; -} - -void _userSerialize( - User object, - IsarWriter writer, - List offsets, - Map> allOffsets, -) { - writer.writeLong(offsets[0], object.age); - writer.writeString(offsets[1], object.name); -} - -User _userDeserialize( - Id id, - IsarReader reader, - List offsets, - Map> allOffsets, -) { - final object = User(); - object.age = reader.readLongOrNull(offsets[0]); - object.id = id; - object.name = reader.readStringOrNull(offsets[1]); - return object; -} - -P _userDeserializeProp

( - IsarReader reader, - int propertyId, - int offset, - Map> allOffsets, -) { - switch (propertyId) { - case 0: - return (reader.readLongOrNull(offset)) as P; - case 1: - return (reader.readStringOrNull(offset)) as P; - default: - throw IsarError('Unknown property with id $propertyId'); - } -} - -Id _userGetId(User object) { - return object.id; -} - -List> _userGetLinks(User object) { - return []; -} - -void _userAttach(IsarCollection col, Id id, User object) { - object.id = id; -} - -extension UserQueryWhereSort on QueryBuilder { - QueryBuilder anyId() { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(const IdWhereClause.any()); - }); - } -} - -extension UserQueryWhere on QueryBuilder { - QueryBuilder idEqualTo(Id id) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); - }); - } - - QueryBuilder idNotEqualTo(Id id) { - return QueryBuilder.apply(this, (query) { - if (query.whereSort == Sort.asc) { - return query - .addWhereClause( - IdWhereClause.lessThan(upper: id, includeUpper: false), - ) - .addWhereClause( - IdWhereClause.greaterThan(lower: id, includeLower: false), - ); - } else { - return query - .addWhereClause( - IdWhereClause.greaterThan(lower: id, includeLower: false), - ) - .addWhereClause( - IdWhereClause.lessThan(upper: id, includeUpper: false), - ); - } - }); - } - - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause( - IdWhereClause.greaterThan(lower: id, includeLower: include), - ); - }); - } - - QueryBuilder idLessThan(Id id, - {bool include = false}) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause( - IdWhereClause.lessThan(upper: id, includeUpper: include), - ); - }); - } - - QueryBuilder idBetween( - Id lowerId, - Id upperId, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); - }); - } -} - -extension UserQueryFilter on QueryBuilder { - QueryBuilder ageIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'age', - )); - }); - } - - QueryBuilder ageIsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'age', - )); - }); - } - - QueryBuilder ageEqualTo(int? value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'age', - value: value, - )); - }); - } - - QueryBuilder ageGreaterThan( - int? value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'age', - value: value, - )); - }); - } - - QueryBuilder ageLessThan( - int? value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'age', - value: value, - )); - }); - } - - QueryBuilder ageBetween( - int? lower, - int? upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'age', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder idEqualTo(Id value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); - }); - } - - QueryBuilder idGreaterThan( - Id value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); - }); - } - - QueryBuilder idLessThan( - Id value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); - }); - } - - QueryBuilder idBetween( - Id lower, - Id upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder nameIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'name', - )); - }); - } - - QueryBuilder nameIsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'name', - )); - }); - } - - QueryBuilder nameEqualTo( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder nameGreaterThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder nameLessThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder nameBetween( - String? lower, - String? upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'name', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder nameStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder nameEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder nameContains(String value, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder nameMatches(String pattern, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'name', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder nameIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: '', - )); - }); - } - - QueryBuilder nameIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'name', - value: '', - )); - }); - } -} - -extension UserQueryObject on QueryBuilder {} - -extension UserQueryLinks on QueryBuilder {} - -extension UserQuerySortBy on QueryBuilder { - QueryBuilder sortByAge() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'age', Sort.asc); - }); - } - - QueryBuilder sortByAgeDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'age', Sort.desc); - }); - } - - QueryBuilder sortByName() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'name', Sort.asc); - }); - } - - QueryBuilder sortByNameDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'name', Sort.desc); - }); - } -} - -extension UserQuerySortThenBy on QueryBuilder { - QueryBuilder thenByAge() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'age', Sort.asc); - }); - } - - QueryBuilder thenByAgeDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'age', Sort.desc); - }); - } - - QueryBuilder thenById() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'id', Sort.asc); - }); - } - - QueryBuilder thenByIdDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'id', Sort.desc); - }); - } - - QueryBuilder thenByName() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'name', Sort.asc); - }); - } - - QueryBuilder thenByNameDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'name', Sort.desc); - }); - } -} - -extension UserQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByAge() { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'age'); - }); - } - - QueryBuilder distinctByName( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'name', caseSensitive: caseSensitive); - }); - } -} - -extension UserQueryProperty on QueryBuilder { - QueryBuilder idProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'id'); - }); - } - - QueryBuilder ageProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'age'); - }); - } - - QueryBuilder nameProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'name'); - }); - } -} +// // GENERATED CODE - DO NOT MODIFY BY HAND +// +// part of 'user.dart'; +// +// // ************************************************************************** +// // IsarCollectionGenerator +// // ************************************************************************** +// +// // coverage:ignore-file +// // ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types +// +// extension GetUserCollection on Isar { +// IsarCollection get users => this.collection(); +// } +// +// final UserSchema = CollectionSchema( +// name: r'User', +// id: BigInt.parse("-7838171048429979076").toInt(), +// properties: { +// r'age': PropertySchema( +// id: BigInt.parse("0").toInt(), +// name: r'age', +// type: IsarType.long, +// ), +// r'name': PropertySchema( +// id: BigInt.parse("1").toInt(), +// name: r'name', +// type: IsarType.string, +// ) +// }, +// estimateSize: _userEstimateSize, +// serialize: _userSerialize, +// deserialize: _userDeserialize, +// deserializeProp: _userDeserializeProp, +// idName: r'id', +// indexes: {}, +// links: {}, +// embeddedSchemas: {}, +// getId: _userGetId, +// getLinks: _userGetLinks, +// attach: _userAttach, +// version: '3.1.0', +// ); +// +// int _userEstimateSize( +// User object, +// List offsets, +// Map> allOffsets, +// ) { +// var bytesCount = offsets.last; +// { +// final value = object.name; +// if (value != null) { +// bytesCount += 3 + value.length * 3; +// } +// } +// return bytesCount; +// } +// +// void _userSerialize( +// User object, +// IsarWriter writer, +// List offsets, +// Map> allOffsets, +// ) { +// writer.writeLong(offsets[0], object.age); +// writer.writeString(offsets[1], object.name); +// } +// +// User _userDeserialize( +// Id id, +// IsarReader reader, +// List offsets, +// Map> allOffsets, +// ) { +// final object = User(); +// object.age = reader.readLongOrNull(offsets[0]); +// object.id = id; +// object.name = reader.readStringOrNull(offsets[1]); +// return object; +// } +// +// P _userDeserializeProp

( +// IsarReader reader, +// int propertyId, +// int offset, +// Map> allOffsets, +// ) { +// switch (propertyId) { +// case 0: +// return (reader.readLongOrNull(offset)) as P; +// case 1: +// return (reader.readStringOrNull(offset)) as P; +// default: +// throw IsarError('Unknown property with id $propertyId'); +// } +// } +// +// Id _userGetId(User object) { +// return object.id; +// } +// +// List> _userGetLinks(User object) { +// return []; +// } +// +// void _userAttach(IsarCollection col, Id id, User object) { +// object.id = id; +// } +// +// extension UserQueryWhereSort on QueryBuilder { +// QueryBuilder anyId() { +// return QueryBuilder.apply(this, (query) { +// return query.addWhereClause(const IdWhereClause.any()); +// }); +// } +// } +// +// extension UserQueryWhere on QueryBuilder { +// QueryBuilder idEqualTo(Id id) { +// return QueryBuilder.apply(this, (query) { +// return query.addWhereClause(IdWhereClause.between( +// lower: id, +// upper: id, +// )); +// }); +// } +// +// QueryBuilder idNotEqualTo(Id id) { +// return QueryBuilder.apply(this, (query) { +// if (query.whereSort == Sort.asc) { +// return query +// .addWhereClause( +// IdWhereClause.lessThan(upper: id, includeUpper: false), +// ) +// .addWhereClause( +// IdWhereClause.greaterThan(lower: id, includeLower: false), +// ); +// } else { +// return query +// .addWhereClause( +// IdWhereClause.greaterThan(lower: id, includeLower: false), +// ) +// .addWhereClause( +// IdWhereClause.lessThan(upper: id, includeUpper: false), +// ); +// } +// }); +// } +// +// QueryBuilder idGreaterThan(Id id, +// {bool include = false}) { +// return QueryBuilder.apply(this, (query) { +// return query.addWhereClause( +// IdWhereClause.greaterThan(lower: id, includeLower: include), +// ); +// }); +// } +// +// QueryBuilder idLessThan(Id id, +// {bool include = false}) { +// return QueryBuilder.apply(this, (query) { +// return query.addWhereClause( +// IdWhereClause.lessThan(upper: id, includeUpper: include), +// ); +// }); +// } +// +// QueryBuilder idBetween( +// Id lowerId, +// Id upperId, { +// bool includeLower = true, +// bool includeUpper = true, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addWhereClause(IdWhereClause.between( +// lower: lowerId, +// includeLower: includeLower, +// upper: upperId, +// includeUpper: includeUpper, +// )); +// }); +// } +// } +// +// extension UserQueryFilter on QueryBuilder { +// QueryBuilder ageIsNull() { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(const FilterCondition.isNull( +// property: r'age', +// )); +// }); +// } +// +// QueryBuilder ageIsNotNull() { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(const FilterCondition.isNotNull( +// property: r'age', +// )); +// }); +// } +// +// QueryBuilder ageEqualTo(int? value) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.equalTo( +// property: r'age', +// value: value, +// )); +// }); +// } +// +// QueryBuilder ageGreaterThan( +// int? value, { +// bool include = false, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.greaterThan( +// include: include, +// property: r'age', +// value: value, +// )); +// }); +// } +// +// QueryBuilder ageLessThan( +// int? value, { +// bool include = false, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.lessThan( +// include: include, +// property: r'age', +// value: value, +// )); +// }); +// } +// +// QueryBuilder ageBetween( +// int? lower, +// int? upper, { +// bool includeLower = true, +// bool includeUpper = true, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.between( +// property: r'age', +// lower: lower, +// includeLower: includeLower, +// upper: upper, +// includeUpper: includeUpper, +// )); +// }); +// } +// +// QueryBuilder idEqualTo(Id value) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.equalTo( +// property: r'id', +// value: value, +// )); +// }); +// } +// +// QueryBuilder idGreaterThan( +// Id value, { +// bool include = false, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.greaterThan( +// include: include, +// property: r'id', +// value: value, +// )); +// }); +// } +// +// QueryBuilder idLessThan( +// Id value, { +// bool include = false, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.lessThan( +// include: include, +// property: r'id', +// value: value, +// )); +// }); +// } +// +// QueryBuilder idBetween( +// Id lower, +// Id upper, { +// bool includeLower = true, +// bool includeUpper = true, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.between( +// property: r'id', +// lower: lower, +// includeLower: includeLower, +// upper: upper, +// includeUpper: includeUpper, +// )); +// }); +// } +// +// QueryBuilder nameIsNull() { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(const FilterCondition.isNull( +// property: r'name', +// )); +// }); +// } +// +// QueryBuilder nameIsNotNull() { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(const FilterCondition.isNotNull( +// property: r'name', +// )); +// }); +// } +// +// QueryBuilder nameEqualTo( +// String? value, { +// bool caseSensitive = true, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.equalTo( +// property: r'name', +// value: value, +// caseSensitive: caseSensitive, +// )); +// }); +// } +// +// QueryBuilder nameGreaterThan( +// String? value, { +// bool include = false, +// bool caseSensitive = true, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.greaterThan( +// include: include, +// property: r'name', +// value: value, +// caseSensitive: caseSensitive, +// )); +// }); +// } +// +// QueryBuilder nameLessThan( +// String? value, { +// bool include = false, +// bool caseSensitive = true, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.lessThan( +// include: include, +// property: r'name', +// value: value, +// caseSensitive: caseSensitive, +// )); +// }); +// } +// +// QueryBuilder nameBetween( +// String? lower, +// String? upper, { +// bool includeLower = true, +// bool includeUpper = true, +// bool caseSensitive = true, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.between( +// property: r'name', +// lower: lower, +// includeLower: includeLower, +// upper: upper, +// includeUpper: includeUpper, +// caseSensitive: caseSensitive, +// )); +// }); +// } +// +// QueryBuilder nameStartsWith( +// String value, { +// bool caseSensitive = true, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.startsWith( +// property: r'name', +// value: value, +// caseSensitive: caseSensitive, +// )); +// }); +// } +// +// QueryBuilder nameEndsWith( +// String value, { +// bool caseSensitive = true, +// }) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.endsWith( +// property: r'name', +// value: value, +// caseSensitive: caseSensitive, +// )); +// }); +// } +// +// QueryBuilder nameContains(String value, +// {bool caseSensitive = true}) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.contains( +// property: r'name', +// value: value, +// caseSensitive: caseSensitive, +// )); +// }); +// } +// +// QueryBuilder nameMatches(String pattern, +// {bool caseSensitive = true}) { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.matches( +// property: r'name', +// wildcard: pattern, +// caseSensitive: caseSensitive, +// )); +// }); +// } +// +// QueryBuilder nameIsEmpty() { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.equalTo( +// property: r'name', +// value: '', +// )); +// }); +// } +// +// QueryBuilder nameIsNotEmpty() { +// return QueryBuilder.apply(this, (query) { +// return query.addFilterCondition(FilterCondition.greaterThan( +// property: r'name', +// value: '', +// )); +// }); +// } +// } +// +// extension UserQueryObject on QueryBuilder {} +// +// extension UserQueryLinks on QueryBuilder {} +// +// extension UserQuerySortBy on QueryBuilder { +// QueryBuilder sortByAge() { +// return QueryBuilder.apply(this, (query) { +// return query.addSortBy(r'age', Sort.asc); +// }); +// } +// +// QueryBuilder sortByAgeDesc() { +// return QueryBuilder.apply(this, (query) { +// return query.addSortBy(r'age', Sort.desc); +// }); +// } +// +// QueryBuilder sortByName() { +// return QueryBuilder.apply(this, (query) { +// return query.addSortBy(r'name', Sort.asc); +// }); +// } +// +// QueryBuilder sortByNameDesc() { +// return QueryBuilder.apply(this, (query) { +// return query.addSortBy(r'name', Sort.desc); +// }); +// } +// } +// +// extension UserQuerySortThenBy on QueryBuilder { +// QueryBuilder thenByAge() { +// return QueryBuilder.apply(this, (query) { +// return query.addSortBy(r'age', Sort.asc); +// }); +// } +// +// QueryBuilder thenByAgeDesc() { +// return QueryBuilder.apply(this, (query) { +// return query.addSortBy(r'age', Sort.desc); +// }); +// } +// +// QueryBuilder thenById() { +// return QueryBuilder.apply(this, (query) { +// return query.addSortBy(r'id', Sort.asc); +// }); +// } +// +// QueryBuilder thenByIdDesc() { +// return QueryBuilder.apply(this, (query) { +// return query.addSortBy(r'id', Sort.desc); +// }); +// } +// +// QueryBuilder thenByName() { +// return QueryBuilder.apply(this, (query) { +// return query.addSortBy(r'name', Sort.asc); +// }); +// } +// +// QueryBuilder thenByNameDesc() { +// return QueryBuilder.apply(this, (query) { +// return query.addSortBy(r'name', Sort.desc); +// }); +// } +// } +// +// extension UserQueryWhereDistinct on QueryBuilder { +// QueryBuilder distinctByAge() { +// return QueryBuilder.apply(this, (query) { +// return query.addDistinctBy(r'age'); +// }); +// } +// +// QueryBuilder distinctByName( +// {bool caseSensitive = true}) { +// return QueryBuilder.apply(this, (query) { +// return query.addDistinctBy(r'name', caseSensitive: caseSensitive); +// }); +// } +// } +// +// extension UserQueryProperty on QueryBuilder { +// QueryBuilder idProperty() { +// return QueryBuilder.apply(this, (query) { +// return query.addPropertyName(r'id'); +// }); +// } +// +// QueryBuilder ageProperty() { +// return QueryBuilder.apply(this, (query) { +// return query.addPropertyName(r'age'); +// }); +// } +// +// QueryBuilder nameProperty() { +// return QueryBuilder.apply(this, (query) { +// return query.addPropertyName(r'name'); +// }); +// } +// } diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 7c11d17cca..d7686a9ef4 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -16,7 +16,7 @@ import 'package:sentry_dio/sentry_dio.dart'; import 'package:sentry_drift/sentry_drift.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_hive/sentry_hive.dart'; -import 'package:sentry_isar/sentry_isar.dart'; +// import 'package:sentry_isar/sentry_isar.dart'; import 'package:sentry_logging/sentry_logging.dart'; import 'package:sentry_sqflite/sentry_sqflite.dart'; import 'package:sqflite/sqflite.dart'; @@ -27,7 +27,7 @@ import 'package:universal_platform/universal_platform.dart'; import 'auto_close_screen.dart'; import 'drift/connection/connection.dart'; import 'drift/database.dart'; -import 'isar/user.dart'; +// import 'isar/user.dart'; import 'user_feedback_dialog.dart'; // ATTENTION: Change the DSN below with your own to see the events in Sentry. Get one at sentry.io @@ -62,33 +62,33 @@ Future setupSentry( bool isIntegrationTest = false, BeforeSendCallback? beforeSendCallback, }) async { - await SentryFlutter.init( + await Sentry.init( (options) { options.dsn = exampleDsn; options.tracesSampleRate = 1.0; - options.profilesSampleRate = 1.0; - options.reportPackages = false; + // options.profilesSampleRate = 1.0; + // options.reportPackages = false; options.addInAppInclude('sentry_flutter_example'); options.considerInAppFramesByDefault = false; options.attachThreads = true; - options.enableWindowMetricBreadcrumbs = true; + // options.enableWindowMetricBreadcrumbs = true; options.addIntegration(LoggingIntegration(minEventLevel: Level.INFO)); options.sendDefaultPii = true; - options.reportSilentFlutterErrors = true; - options.attachScreenshot = true; - options.screenshotQuality = SentryScreenshotQuality.low; - options.attachViewHierarchy = true; + // options.reportSilentFlutterErrors = true; + // options.attachScreenshot = true; + // options.screenshotQuality = SentryScreenshotQuality.low; + // options.attachViewHierarchy = true; // We can enable Sentry debug logging during development. This is likely // going to log too much for your app, but can be useful when figuring out // configuration issues, e.g. finding out why your events are not uploaded. options.debug = true; options.spotlight = Spotlight(enabled: true); - options.enableTimeToFullDisplayTracing = true; + // options.enableTimeToFullDisplayTracing = true; options.enableMetrics = true; options.maxRequestBodySize = MaxRequestBodySize.always; options.maxResponseBodySize = MaxResponseBodySize.always; - options.navigatorKey = navigatorKey; + // options.navigatorKey = navigatorKey; _isIntegrationTest = isIntegrationTest; if (_isIntegrationTest) { @@ -205,6 +205,7 @@ class MainScaffold extends StatelessWidget { 'Long press a button to see more information. (hover on web)'), ), ), + // Text(StackTrace.current.toString()), TooltipButton( onPressed: () => navigateToAutoCloseScreen(context), text: @@ -578,34 +579,34 @@ class MainScaffold extends StatelessWidget { } Future isarTest() async { - final tr = Sentry.startTransaction( - 'isarTest', - 'db', - bindToScope: true, - ); - - final dir = await getApplicationDocumentsDirectory(); - - final isar = await SentryIsar.open( - [UserSchema], - directory: dir.path, - ); - - final newUser = User() - ..name = 'Joe Dirt' - ..age = 36; - - await isar.writeTxn(() async { - await isar.users.put(newUser); // insert & update - }); - - final existingUser = await isar.users.get(newUser.id); // get - - await isar.writeTxn(() async { - await isar.users.delete(existingUser!.id); // delete - }); - - await tr.finish(status: const SpanStatus.ok()); + // final tr = Sentry.startTransaction( + // 'isarTest', + // 'db', + // bindToScope: true, + // ); + // + // final dir = await getApplicationDocumentsDirectory(); + // + // final isar = await SentryIsar.open( + // [UserSchema], + // directory: dir.path, + // ); + // + // final newUser = User() + // ..name = 'Joe Dirt' + // ..age = 36; + // + // await isar.writeTxn(() async { + // await isar.users.put(newUser); // insert & update + // }); + // + // final existingUser = await isar.users.get(newUser.id); // get + // + // await isar.writeTxn(() async { + // await isar.users.delete(existingUser!.id); // delete + // }); + // + // await tr.finish(status: const SpanStatus.ok()); } Future hiveTest() async { diff --git a/flutter/example/macos/Runner.xcodeproj/project.pbxproj b/flutter/example/macos/Runner.xcodeproj/project.pbxproj index ad9c506c71..103a73ecaa 100644 --- a/flutter/example/macos/Runner.xcodeproj/project.pbxproj +++ b/flutter/example/macos/Runner.xcodeproj/project.pbxproj @@ -209,7 +209,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1430; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/flutter/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 99a6840afc..9adb32c6e4 100644 --- a/flutter/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/flutter/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Bool { return true diff --git a/flutter/example/pubspec.yaml b/flutter/example/pubspec.yaml index dae5c41ece..f042f3f5b0 100644 --- a/flutter/example/pubspec.yaml +++ b/flutter/example/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: sentry_file: sentry_hive: sentry_drift: - sentry_isar: +# sentry_isar: universal_platform: ^1.0.0 feedback: ^2.0.0 provider: ^6.0.0 @@ -27,7 +27,7 @@ dependencies: sqflite: any # This gets constrained by `sentry_sqflite` logging: any # This gets constrained by `sentry_logging` drift: any # This gets constrained by `sentry_drift` - isar: any # This gets constrained by `sentry_isar` +# isar: any # This gets constrained by `sentry_isar` package_info_plus: ^4.0.0 path_provider: ^2.0.0 #sqflite_common_ffi: ^2.0.0 @@ -53,8 +53,8 @@ flutter: - assets/sentry-wordmark.png sentry: - upload_source_maps: true - upload_sources: true +# upload_source_maps: true +# upload_sources: true project: sentry-flutter org: sentry-sdks wait_for_processing: true diff --git a/flutter/example/pubspec_overrides.yaml b/flutter/example/pubspec_overrides.yaml index a392cc626d..d0e3a2713b 100644 --- a/flutter/example/pubspec_overrides.yaml +++ b/flutter/example/pubspec_overrides.yaml @@ -15,5 +15,5 @@ dependency_overrides: path: ../../hive sentry_drift: path: ../../drift - sentry_isar: - path: ../../isar +# sentry_isar: +# path: ../../isar diff --git a/flutter/lib/src/integrations/load_image_list_integration.dart b/flutter/lib/src/integrations/load_image_list_integration.dart index a3a1c9fc9d..b8c7e02605 100644 --- a/flutter/lib/src/integrations/load_image_list_integration.dart +++ b/flutter/lib/src/integrations/load_image_list_integration.dart @@ -2,16 +2,15 @@ import 'dart:async'; import 'package:sentry/sentry.dart'; import '../native/sentry_native_binding.dart'; -import '../sentry_flutter_options.dart'; /// Loads the native debug image list for stack trace symbolication. -class LoadImageListIntegration extends Integration { +class LoadImageListIntegration extends ImageLoadingIntegration { final SentryNativeBinding _native; LoadImageListIntegration(this._native); @override - void call(Hub hub, SentryFlutterOptions options) { + void call(Hub hub, SentryOptions options) { options.addEventProcessor( _LoadImageListIntegrationEventProcessor(_native), ); diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index 3ff835284c..086ed4467f 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -180,6 +180,9 @@ mixin SentryFlutter { if (native != null) { integrations.add(NativeSdkIntegration(native)); integrations.add(LoadContextsIntegration(native)); + + // Remove the dart only integration + integrations.removeWhere((element) => element is ImageLoadingIntegration); integrations.add(LoadImageListIntegration(native)); } From db74ff35facbeb1cfda458dc671a6365d3f90bd2 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 20:07:50 +0200 Subject: [PATCH 02/40] update implementation --- dart/lib/sentry.dart | 4 +-- ...rt => dart_image_loading_integration.dart} | 15 +++----- ...olizer.dart => debug_image_extractor.dart} | 34 ++++++++++++++++--- dart/lib/src/sentry.dart | 6 ++-- dart/lib/src/sentry_options.dart | 4 +-- ...t.dart => debug_image_extractor_test.dart} | 13 +++---- flutter/example/lib/main.dart | 3 +- .../load_image_list_integration.dart | 2 +- flutter/lib/src/sentry_flutter.dart | 4 +-- 9 files changed, 51 insertions(+), 34 deletions(-) rename dart/lib/src/{load_image_integration.dart => dart_image_loading_integration.dart} (55%) rename dart/lib/src/{symbolizer.dart => debug_image_extractor.dart} (78%) rename dart/test/{dart_symbolizer_test.dart => debug_image_extractor_test.dart} (85%) diff --git a/dart/lib/sentry.dart b/dart/lib/sentry.dart index 81ab217d9e..2d2807b2a0 100644 --- a/dart/lib/sentry.dart +++ b/dart/lib/sentry.dart @@ -58,5 +58,5 @@ export 'src/utils.dart'; export 'src/spotlight.dart'; // proxy export 'src/protocol/sentry_proxy.dart'; -export 'src/symbolizer.dart'; -export 'src/load_image_integration.dart'; +export 'src/debug_image_extractor.dart'; +export 'src/dart_image_loading_integration.dart'; diff --git a/dart/lib/src/load_image_integration.dart b/dart/lib/src/dart_image_loading_integration.dart similarity index 55% rename from dart/lib/src/load_image_integration.dart rename to dart/lib/src/dart_image_loading_integration.dart index ca0adb9a4e..1803405319 100644 --- a/dart/lib/src/load_image_integration.dart +++ b/dart/lib/src/dart_image_loading_integration.dart @@ -1,12 +1,10 @@ -import 'package:sentry/sentry.dart'; +import '../sentry.dart'; -abstract class ImageLoadingIntegration extends Integration {} - -class DartImageLoadingIntegration implements ImageLoadingIntegration { +class DartImageLoadingIntegration extends Integration { @override void call(Hub hub, SentryOptions options) { options.addEventProcessor( - _LoadImageListIntegrationEventProcessor(DartSymbolizer(options))); + _LoadImageListIntegrationEventProcessor(DebugImageExtractor(options))); options.sdk.addIntegration('loadImageIntegration'); } @@ -17,21 +15,18 @@ class DartImageLoadingIntegration implements ImageLoadingIntegration { class _LoadImageListIntegrationEventProcessor implements EventProcessor { _LoadImageListIntegrationEventProcessor(this._symbolizer); - final DartSymbolizer _symbolizer; + final DebugImageExtractor _symbolizer; @override Future apply(SentryEvent event, Hint hint) async { if (event.stackTrace == null) { return event; } - print('stacktrace: ${event.stackTrace}'); final image = _symbolizer.toImage(event.stackTrace!); - print('image: $image'); if (image == null) { return event; } - final debugMeta = DebugMeta(images: [image]); - event = event.copyWith(debugMeta: debugMeta); + event = event.copyWith(debugMeta: DebugMeta(images: [image])); return event; } } diff --git a/dart/lib/src/symbolizer.dart b/dart/lib/src/debug_image_extractor.dart similarity index 78% rename from dart/lib/src/symbolizer.dart rename to dart/lib/src/debug_image_extractor.dart index 9088ab91e5..4a672a3e5f 100644 --- a/dart/lib/src/symbolizer.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -1,7 +1,13 @@ import 'dart:typed_data'; +import 'package:meta/meta.dart'; + import '../sentry.dart'; -class DartSymbolizer { +/// Processes a stack trace and extracts debug image information from it and +/// creates a synthetic representation of the debug image. +/// Currently working for iOS, macOS and Android. +@internal +class DebugImageExtractor { final SentryOptions _options; // Header information @@ -18,7 +24,7 @@ class DartSymbolizer { static final RegExp _isolateDsoBaseLineRegex = RegExp(r'isolate_dso_base(?:=|: )([\da-f]+)'); - DartSymbolizer(this._options); + DebugImageExtractor(this._options); DebugImage? toImage(StackTrace stackTrace) { _parseStackTrace(stackTrace); @@ -81,17 +87,22 @@ class DartSymbolizer { } final type = _options.platformChecker.platform.isAndroid ? 'elf' : 'macho'; + final debugId = _options.platformChecker.platform.isAndroid + ? _convertCodeIdToDebugId(_buildId!) + : _hexToUuid(_buildId!); + final codeId = + _options.platformChecker.platform.isAndroid ? _buildId! : null; return DebugImage( type: type, imageAddr: '0x$_isolateDsoBase', - debugId: _convertCodeIdToDebugId(_buildId!), - codeId: _buildId!, + debugId: debugId, + codeId: codeId, arch: _arch, ); } // Debug identifier is the little-endian UUID representation of the first 16-bytes of - // the build ID. + // the build ID on Android String _convertCodeIdToDebugId(String codeId) { codeId = codeId.replaceAll(' ', ''); if (codeId.length < 32) { @@ -123,4 +134,17 @@ class DartSymbolizer { .join() ].join('-'); } + + String _hexToUuid(String hex) { + if (hex.length != 32) { + // todo: don't throw + throw FormatException('Input must be a 32-character hexadecimal string'); + } + + return '${hex.substring(0, 8)}-' + '${hex.substring(8, 12)}-' + '${hex.substring(12, 16)}-' + '${hex.substring(16, 20)}-' + '${hex.substring(20)}'; + } } diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index bbfc3dcd7b..1a3fcffdea 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; import 'dart_exception_type_identifier.dart'; -import 'load_image_integration.dart'; +import 'dart_image_loading_integration.dart'; import 'metrics/metrics_api.dart'; import 'run_zoned_guarded_integration.dart'; import 'event_processor/enricher/enricher_event_processor.dart'; @@ -84,7 +84,9 @@ class Sentry { options.addIntegrationByIndex(0, IsolateErrorIntegration()); } - options.addIntegration(DartImageLoadingIntegration()); + if (options.enablePureDartSymbolication) { + options.addIntegration(DartImageLoadingIntegration()); + } options.addEventProcessor(EnricherEventProcessor(options)); options.addEventProcessor(ExceptionEventProcessor(options)); diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 5d05162fb5..0479861d28 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -359,8 +359,8 @@ class SentryOptions { } /// If enabled, the SDK will attempt to symbolicate pure Dart stack traces. - /// Default is `false` - bool symbolicateDartStacktrace = true; + /// Automatically set to false in `SentryFlutter.init`. + bool enablePureDartSymbolication = true; @internal late ClientReportRecorder recorder = NoOpClientReportRecorder(); diff --git a/dart/test/dart_symbolizer_test.dart b/dart/test/debug_image_extractor_test.dart similarity index 85% rename from dart/test/dart_symbolizer_test.dart rename to dart/test/debug_image_extractor_test.dart index 581744032f..740c0ccca9 100644 --- a/dart/test/dart_symbolizer_test.dart +++ b/dart/test/debug_image_extractor_test.dart @@ -6,13 +6,13 @@ import 'mocks/mock_platform.dart'; import 'mocks/mock_platform_checker.dart'; void main() { - late DartSymbolizer symbolizer; + late DebugImageExtractor symbolizer; late SentryOptions options; late MockPlatformChecker mockPlatformChecker; setUp(() { options = SentryOptions(dsn: fakeDsn); - symbolizer = DartSymbolizer(options); + symbolizer = DebugImageExtractor(options); }); test('Symbolizer correctly parses a valid stack trace header on Android', () { @@ -42,16 +42,13 @@ isolate_dso_base: 0f00000000 final validStackTrace = StackTrace.fromString(''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** os: ios, arch: arm64, comp: yes, sim: no -build_id: 'f1c3bcc0279865fe3058404b2831d9e64135386c' +build_id: 'b680cb890f9e3c12a24b172d050dec73' isolate_dso_base: 0f00000000 '''); - - final debugImage = symbolizer.toImage(validStackTrace)!; - expect(debugImage.type, equals('macho')); expect(debugImage.imageAddr, equals('0x0f00000000')); - expect( - debugImage.codeId, equals('f1c3bcc0279865fe3058404b2831d9e64135386c')); + // expect( + // debugImage.codeId, equals('f1c3bcc0279865fe3058404b2831d9e64135386c')); expect(debugImage.debugId, equals('c0bcc3f1-9827-fe65-3058-404b2831d9e6')); }); } diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index d7686a9ef4..5a5ebc868f 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -62,7 +62,7 @@ Future setupSentry( bool isIntegrationTest = false, BeforeSendCallback? beforeSendCallback, }) async { - await Sentry.init( + await SentryFlutter.init( (options) { options.dsn = exampleDsn; options.tracesSampleRate = 1.0; @@ -560,6 +560,7 @@ class MainScaffold extends StatelessWidget { 'Demonstrates the metrics. It creates several metrics and send them to Sentry.', buttonTitle: 'Metrics', ), + SelectableText(StackTrace.current.toString()), if (UniversalPlatform.isIOS || UniversalPlatform.isMacOS) const CocoaExample(), if (UniversalPlatform.isAndroid) const AndroidExample(), diff --git a/flutter/lib/src/integrations/load_image_list_integration.dart b/flutter/lib/src/integrations/load_image_list_integration.dart index b8c7e02605..a9b0f9b2ed 100644 --- a/flutter/lib/src/integrations/load_image_list_integration.dart +++ b/flutter/lib/src/integrations/load_image_list_integration.dart @@ -4,7 +4,7 @@ import 'package:sentry/sentry.dart'; import '../native/sentry_native_binding.dart'; /// Loads the native debug image list for stack trace symbolication. -class LoadImageListIntegration extends ImageLoadingIntegration { +class LoadImageListIntegration extends Integration { final SentryNativeBinding _native; LoadImageListIntegration(this._native); diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index 086ed4467f..f37d7277be 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -180,10 +180,8 @@ mixin SentryFlutter { if (native != null) { integrations.add(NativeSdkIntegration(native)); integrations.add(LoadContextsIntegration(native)); - - // Remove the dart only integration - integrations.removeWhere((element) => element is ImageLoadingIntegration); integrations.add(LoadImageListIntegration(native)); + options.enablePureDartSymbolication = false; } final renderer = options.rendererWrapper.getRenderer(); From e6c16500f081a229a57df5ce014c5b6745730511 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 20:10:44 +0200 Subject: [PATCH 03/40] update --- dart/example/bin/example.dart | 27 +++++++++++---------------- dart/example/pubspec.yaml | 1 - 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/dart/example/bin/example.dart b/dart/example/bin/example.dart index 3aa5ff7fde..d7530f5874 100644 --- a/dart/example/bin/example.dart +++ b/dart/example/bin/example.dart @@ -5,28 +5,23 @@ import 'dart:async'; import 'package:sentry/sentry.dart'; -import 'dart:typed_data'; - -import 'package:uuid/uuid.dart'; import 'event_example.dart'; /// Sends a test exception report to Sentry.io using this Dart client. Future main() async { // ATTENTION: Change the DSN below with your own to see the events in Sentry. Get one at sentry.io - // const dsn = - // 'https://e85b375ffb9f43cf8bdf9787768149e0@o447951.ingest.sentry.io/5428562'; - // - // await Sentry.init( - // (options) => options - // ..dsn = dsn - // ..debug = true - // ..sendDefaultPii = true - // ..addEventProcessor(TagEventProcessor()), - // appRunner: runApp, - // ); - - print(convertCodeIdToDebugId('63956a4567aaf99a10d24405cf6a48f3')); + const dsn = + 'https://e85b375ffb9f43cf8bdf9787768149e0@o447951.ingest.sentry.io/5428562'; + + await Sentry.init( + (options) => options + ..dsn = dsn + ..debug = true + ..sendDefaultPii = true + ..addEventProcessor(TagEventProcessor()), + appRunner: runApp, + ); } Future runApp() async { diff --git a/dart/example/pubspec.yaml b/dart/example/pubspec.yaml index a120d46484..e344bbe25a 100644 --- a/dart/example/pubspec.yaml +++ b/dart/example/pubspec.yaml @@ -10,7 +10,6 @@ environment: dependencies: sentry: path: ../../dart - uuid: ^3.0.0 dev_dependencies: lints: ^2.0.0 From 05ce21ea997ca50720ef7f32782c5546ff7b310e Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 20:19:32 +0200 Subject: [PATCH 04/40] update --- dart/lib/sentry.dart | 2 - .../src/dart_image_loading_integration.dart | 16 +- dart/lib/src/debug_image_extractor.dart | 154 ++++++++---------- 3 files changed, 79 insertions(+), 93 deletions(-) diff --git a/dart/lib/sentry.dart b/dart/lib/sentry.dart index 2d2807b2a0..e9cae9d666 100644 --- a/dart/lib/sentry.dart +++ b/dart/lib/sentry.dart @@ -58,5 +58,3 @@ export 'src/utils.dart'; export 'src/spotlight.dart'; // proxy export 'src/protocol/sentry_proxy.dart'; -export 'src/debug_image_extractor.dart'; -export 'src/dart_image_loading_integration.dart'; diff --git a/dart/lib/src/dart_image_loading_integration.dart b/dart/lib/src/dart_image_loading_integration.dart index 1803405319..1d15e8bf06 100644 --- a/dart/lib/src/dart_image_loading_integration.dart +++ b/dart/lib/src/dart_image_loading_integration.dart @@ -1,10 +1,11 @@ import '../sentry.dart'; +import 'debug_image_extractor.dart'; class DartImageLoadingIntegration extends Integration { @override void call(Hub hub, SentryOptions options) { options.addEventProcessor( - _LoadImageListIntegrationEventProcessor(DebugImageExtractor(options))); + _LoadImageIntegrationEventProcessor(DebugImageExtractor(options))); options.sdk.addIntegration('loadImageIntegration'); } @@ -12,21 +13,22 @@ class DartImageLoadingIntegration extends Integration { void close() {} } -class _LoadImageListIntegrationEventProcessor implements EventProcessor { - _LoadImageListIntegrationEventProcessor(this._symbolizer); +class _LoadImageIntegrationEventProcessor implements EventProcessor { + _LoadImageIntegrationEventProcessor(this._debugImageExtractor); - final DebugImageExtractor _symbolizer; + final DebugImageExtractor _debugImageExtractor; @override Future apply(SentryEvent event, Hint hint) async { if (event.stackTrace == null) { return event; } - final image = _symbolizer.toImage(event.stackTrace!); - if (image == null) { + final syntheticImage = + _debugImageExtractor.extractFrom(event.stackTrace!).toDebugImage(); + if (syntheticImage == null) { return event; } - event = event.copyWith(debugMeta: DebugMeta(images: [image])); + event = event.copyWith(debugMeta: DebugMeta(images: [syntheticImage])); return event; } } diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_image_extractor.dart index 4a672a3e5f..660a12dacd 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -3,101 +3,43 @@ import 'package:meta/meta.dart'; import '../sentry.dart'; -/// Processes a stack trace and extracts debug image information from it and -/// creates a synthetic representation of the debug image. -/// Currently working for iOS, macOS and Android. +// Regular expressions for parsing header lines +const String _headerStartLine = + '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***'; +final RegExp _osArchLineRegex = RegExp( + r'os(?:=|: )(\S+?),? arch(?:=|: )(\S+?),? comp(?:=|: )(yes|no),? sim(?:=|: )(yes|no)'); +final RegExp _buildIdRegex = RegExp(r"build_id(?:=|: )'([\da-f]+)'"); +final RegExp _isolateDsoBaseLineRegex = + RegExp(r'isolate_dso_base(?:=|: )([\da-f]+)'); + +@immutable @internal -class DebugImageExtractor { - final SentryOptions _options; - - // Header information - String? _arch; - String? _buildId; - String? _isolateDsoBase; - - // Regular expressions for parsing header lines - static const String _headerStartLine = - '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***'; - static final RegExp _osArchLineRegex = RegExp( - r'os(?:=|: )(\S+?),? arch(?:=|: )(\S+?),? comp(?:=|: )(yes|no),? sim(?:=|: )(yes|no)'); - static final RegExp _buildIdRegex = RegExp(r"build_id(?:=|: )'([\da-f]+)'"); - static final RegExp _isolateDsoBaseLineRegex = - RegExp(r'isolate_dso_base(?:=|: )([\da-f]+)'); - - DebugImageExtractor(this._options); - - DebugImage? toImage(StackTrace stackTrace) { - _parseStackTrace(stackTrace); - return _createDebugImage(); - } - - void _parseStackTrace(StackTrace stackTrace) { - final lines = stackTrace.toString().split('\n'); - for (final line in lines) { - if (_tryParseHeaderLine(line)) continue; - } - } +class DebugInfo { + final String? arch; + final String? buildId; + final String? isolateDsoBase; + final SentryOptions options; - bool _tryParseHeaderLine(String line) { - if (line.contains(_headerStartLine)) { - _arch = _buildId = _isolateDsoBase = null; - return true; - } - - final parsers = [ - _parseOsArchLine, - _parseBuildIdLine, - _parseIsolateDsoBaseLine, - ]; + DebugInfo(this.arch, this.buildId, this.isolateDsoBase, this.options); - return parsers.any((parser) => parser(line)); - } - - bool _parseOsArchLine(String line) { - final match = _osArchLineRegex.firstMatch(line); - if (match != null) { - _arch = match[2]; - return true; - } - return false; - } - - bool _parseBuildIdLine(String line) { - final match = _buildIdRegex.firstMatch(line); - if (match != null) { - _buildId = match[1]; - return true; - } - return false; - } - - bool _parseIsolateDsoBaseLine(String line) { - final match = _isolateDsoBaseLineRegex.firstMatch(line); - if (match != null) { - _isolateDsoBase = match[1]; - return true; - } - return false; - } - - DebugImage? _createDebugImage() { - if (_buildId == null || _isolateDsoBase == null) { + DebugImage? toDebugImage() { + if (buildId == null || isolateDsoBase == null) { // TODO: log return null; } - final type = _options.platformChecker.platform.isAndroid ? 'elf' : 'macho'; - final debugId = _options.platformChecker.platform.isAndroid - ? _convertCodeIdToDebugId(_buildId!) - : _hexToUuid(_buildId!); - final codeId = - _options.platformChecker.platform.isAndroid ? _buildId! : null; + final type = options.platformChecker.platform.isAndroid ? 'elf' : 'macho'; + final debugId = options.platformChecker.platform.isAndroid + ? _convertCodeIdToDebugId(buildId!) + : _hexToUuid(buildId!); + final codeId = options.platformChecker.platform.isAndroid ? buildId! : null; + return DebugImage( type: type, - imageAddr: '0x$_isolateDsoBase', + imageAddr: '0x$isolateDsoBase', debugId: debugId, codeId: codeId, - arch: _arch, + arch: arch, ); } @@ -148,3 +90,47 @@ class DebugImageExtractor { '${hex.substring(20)}'; } } + +/// Processes a stack trace by extracting debug image information from it and +/// creating a synthetic representation of a debug image. +/// Currently working for iOS, macOS and Android. +@internal +class DebugImageExtractor { + DebugImageExtractor(this._options); + + final SentryOptions _options; + + DebugInfo extractFrom(StackTrace stackTrace) { + String? arch; + String? buildId; + String? isolateDsoBase; + + final lines = stackTrace.toString().split('\n'); + for (final line in lines) { + if (line.contains(_headerStartLine)) { + arch = buildId = isolateDsoBase = null; + continue; + } + + final archMatch = _osArchLineRegex.firstMatch(line); + if (archMatch != null) { + arch = archMatch[2]; + continue; + } + + final buildIdMatch = _buildIdRegex.firstMatch(line); + if (buildIdMatch != null) { + buildId = buildIdMatch[1]; + continue; + } + + final isolateMatch = _isolateDsoBaseLineRegex.firstMatch(line); + if (isolateMatch != null) { + isolateDsoBase = isolateMatch[1]; + continue; + } + } + + return DebugInfo(arch, buildId, isolateDsoBase, _options); + } +} From 1e3a44a52a1be09736bb1d5e9cccc45e131c15ef Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 20:25:47 +0200 Subject: [PATCH 05/40] update --- dart/lib/src/constants.dart | 21 ------------ .../src/dart_image_loading_integration.dart | 32 +++++++++++++++++++ dart/lib/src/protocol/sentry_event.dart | 3 ++ .../load_image_list_integration.dart | 26 --------------- 4 files changed, 35 insertions(+), 47 deletions(-) delete mode 100644 dart/lib/src/constants.dart diff --git a/dart/lib/src/constants.dart b/dart/lib/src/constants.dart deleted file mode 100644 index 00dbef1403..0000000000 --- a/dart/lib/src/constants.dart +++ /dev/null @@ -1,21 +0,0 @@ -// The section name in which the build ID is stored as a note. -const String buildIdSectionName = '.note.gnu.build-id'; -// The type of a build ID note. -const int buildIdNoteType = 3; -// The name of a build ID note. -const String buildIdNoteName = 'GNU'; - -// The dynamic symbol name for the VM instructions section. -const String vmSymbolName = '_kDartVmSnapshotInstructions'; - -// The dynamic symbol name for the VM data section. -const String vmDataSymbolName = '_kDartVmSnapshotData'; - -// The dynamic symbol name for the isolate instructions section. -const String isolateSymbolName = '_kDartIsolateSnapshotInstructions'; - -// The dynamic symbol name for the isolate data section. -const String isolateDataSymbolName = '_kDartIsolateSnapshotData'; - -// The ID for the root loading unit. -const int rootLoadingUnitId = 1; diff --git a/dart/lib/src/dart_image_loading_integration.dart b/dart/lib/src/dart_image_loading_integration.dart index 1d15e8bf06..142d2e2a8b 100644 --- a/dart/lib/src/dart_image_loading_integration.dart +++ b/dart/lib/src/dart_image_loading_integration.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../sentry.dart'; import 'debug_image_extractor.dart'; @@ -20,6 +22,9 @@ class _LoadImageIntegrationEventProcessor implements EventProcessor { @override Future apply(SentryEvent event, Hint hint) async { + if (!event.needsSymbolication()) { + return event; + } if (event.stackTrace == null) { return event; } @@ -32,3 +37,30 @@ class _LoadImageIntegrationEventProcessor implements EventProcessor { return event; } } + +@internal +extension NeedsSymbolication on SentryEvent { + bool needsSymbolication() { + if (this is SentryTransaction) { + return false; + } + final frames = _getStacktraceFrames(); + if (frames == null) { + return false; + } + return frames.any((frame) => 'native' == frame?.platform); + } + + Iterable? _getStacktraceFrames() { + if (exceptions?.isNotEmpty == true) { + return exceptions?.first.stackTrace?.frames; + } + if (threads?.isNotEmpty == true) { + var stacktraces = threads?.map((e) => e.stacktrace); + return stacktraces + ?.where((element) => element != null) + .expand((element) => element!.frames); + } + return null; + } +} diff --git a/dart/lib/src/protocol/sentry_event.dart b/dart/lib/src/protocol/sentry_event.dart index 4200224126..99e2044d5e 100644 --- a/dart/lib/src/protocol/sentry_event.dart +++ b/dart/lib/src/protocol/sentry_event.dart @@ -195,6 +195,9 @@ class SentryEvent with SentryEventLike { @internal final Map? unknown; + /// The stacktrace is only used internally for processing such as in [DartImageLoadingIntegration]. + /// TODO: should be a better way than saving a reference to the stacktrace + @internal final StackTrace? stackTrace; @override diff --git a/flutter/lib/src/integrations/load_image_list_integration.dart b/flutter/lib/src/integrations/load_image_list_integration.dart index a9b0f9b2ed..9564217160 100644 --- a/flutter/lib/src/integrations/load_image_list_integration.dart +++ b/flutter/lib/src/integrations/load_image_list_integration.dart @@ -19,32 +19,6 @@ class LoadImageListIntegration extends Integration { } } -extension _NeedsSymbolication on SentryEvent { - bool needsSymbolication() { - if (this is SentryTransaction) { - return false; - } - final frames = _getStacktraceFrames(); - if (frames == null) { - return false; - } - return frames.any((frame) => 'native' == frame?.platform); - } - - Iterable? _getStacktraceFrames() { - if (exceptions?.isNotEmpty == true) { - return exceptions?.first.stackTrace?.frames; - } - if (threads?.isNotEmpty == true) { - var stacktraces = threads?.map((e) => e.stacktrace); - return stacktraces - ?.where((element) => element != null) - .expand((element) => element!.frames); - } - return null; - } -} - class _LoadImageListIntegrationEventProcessor implements EventProcessor { _LoadImageListIntegrationEventProcessor(this._native); From a1e8aa188c5651fc70a405c6ebf012dedef4e9ce Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 20:27:14 +0200 Subject: [PATCH 06/40] update --- .../src/dart_image_loading_integration.dart | 27 ------------------- dart/lib/src/protocol/sentry_event.dart | 24 +++++++++++++++++ 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/dart/lib/src/dart_image_loading_integration.dart b/dart/lib/src/dart_image_loading_integration.dart index 142d2e2a8b..c9e0eca33d 100644 --- a/dart/lib/src/dart_image_loading_integration.dart +++ b/dart/lib/src/dart_image_loading_integration.dart @@ -37,30 +37,3 @@ class _LoadImageIntegrationEventProcessor implements EventProcessor { return event; } } - -@internal -extension NeedsSymbolication on SentryEvent { - bool needsSymbolication() { - if (this is SentryTransaction) { - return false; - } - final frames = _getStacktraceFrames(); - if (frames == null) { - return false; - } - return frames.any((frame) => 'native' == frame?.platform); - } - - Iterable? _getStacktraceFrames() { - if (exceptions?.isNotEmpty == true) { - return exceptions?.first.stackTrace?.frames; - } - if (threads?.isNotEmpty == true) { - var stacktraces = threads?.map((e) => e.stacktrace); - return stacktraces - ?.where((element) => element != null) - .expand((element) => element!.frames); - } - return null; - } -} diff --git a/dart/lib/src/protocol/sentry_event.dart b/dart/lib/src/protocol/sentry_event.dart index 99e2044d5e..5994475e38 100644 --- a/dart/lib/src/protocol/sentry_event.dart +++ b/dart/lib/src/protocol/sentry_event.dart @@ -419,4 +419,28 @@ class SentryEvent with SentryEventLike { if (threadJson?.isNotEmpty ?? false) 'threads': {'values': threadJson}, }; } + + bool needsSymbolication() { + if (this is SentryTransaction) { + return false; + } + final frames = _getStacktraceFrames(); + if (frames == null) { + return false; + } + return frames.any((frame) => 'native' == frame?.platform); + } + + Iterable? _getStacktraceFrames() { + if (exceptions?.isNotEmpty == true) { + return exceptions?.first.stackTrace?.frames; + } + if (threads?.isNotEmpty == true) { + var stacktraces = threads?.map((e) => e.stacktrace); + return stacktraces + ?.where((element) => element != null) + .expand((element) => element!.frames); + } + return null; + } } From dc023622620a27b021d42e1dd460f4d290804946 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 20:27:41 +0200 Subject: [PATCH 07/40] update --- flutter/lib/src/integrations/load_image_list_integration.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/lib/src/integrations/load_image_list_integration.dart b/flutter/lib/src/integrations/load_image_list_integration.dart index 9564217160..dd28ec394a 100644 --- a/flutter/lib/src/integrations/load_image_list_integration.dart +++ b/flutter/lib/src/integrations/load_image_list_integration.dart @@ -4,13 +4,13 @@ import 'package:sentry/sentry.dart'; import '../native/sentry_native_binding.dart'; /// Loads the native debug image list for stack trace symbolication. -class LoadImageListIntegration extends Integration { +class LoadImageListIntegration extends Integration { final SentryNativeBinding _native; LoadImageListIntegration(this._native); @override - void call(Hub hub, SentryOptions options) { + void call(Hub hub, SentryFlutterOptions options) { options.addEventProcessor( _LoadImageListIntegrationEventProcessor(_native), ); From 345d81bc0631728be5eede5af4dc6abe0e2685d1 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 20:33:15 +0200 Subject: [PATCH 08/40] update comment --- dart/lib/src/debug_image_extractor.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_image_extractor.dart index 660a12dacd..b08ecde577 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -91,9 +91,7 @@ class DebugInfo { } } -/// Processes a stack trace by extracting debug image information from it and -/// creating a synthetic representation of a debug image. -/// Currently working for iOS, macOS and Android. +/// Processes a stack trace by extracting debug image information from its header. @internal class DebugImageExtractor { DebugImageExtractor(this._options); From a260686113d27835c94c87e55c916871959904b3 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 20:38:43 +0200 Subject: [PATCH 09/40] update --- .../src/dart_image_loading_integration.dart | 6 +- ...tractor.dart => debug_info_extractor.dart} | 72 +++++++++++-------- 2 files changed, 47 insertions(+), 31 deletions(-) rename dart/lib/src/{debug_image_extractor.dart => debug_info_extractor.dart} (73%) diff --git a/dart/lib/src/dart_image_loading_integration.dart b/dart/lib/src/dart_image_loading_integration.dart index c9e0eca33d..4bca0f3c54 100644 --- a/dart/lib/src/dart_image_loading_integration.dart +++ b/dart/lib/src/dart_image_loading_integration.dart @@ -1,13 +1,13 @@ import 'package:meta/meta.dart'; import '../sentry.dart'; -import 'debug_image_extractor.dart'; +import 'debug_info_extractor.dart'; class DartImageLoadingIntegration extends Integration { @override void call(Hub hub, SentryOptions options) { options.addEventProcessor( - _LoadImageIntegrationEventProcessor(DebugImageExtractor(options))); + _LoadImageIntegrationEventProcessor(DebugInfoExtractor(options))); options.sdk.addIntegration('loadImageIntegration'); } @@ -18,7 +18,7 @@ class DartImageLoadingIntegration extends Integration { class _LoadImageIntegrationEventProcessor implements EventProcessor { _LoadImageIntegrationEventProcessor(this._debugImageExtractor); - final DebugImageExtractor _debugImageExtractor; + final DebugInfoExtractor _debugImageExtractor; @override Future apply(SentryEvent event, Hint hint) async { diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_info_extractor.dart similarity index 73% rename from dart/lib/src/debug_image_extractor.dart rename to dart/lib/src/debug_info_extractor.dart index b08ecde577..bb6e584d00 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_info_extractor.dart @@ -3,15 +3,6 @@ import 'package:meta/meta.dart'; import '../sentry.dart'; -// Regular expressions for parsing header lines -const String _headerStartLine = - '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***'; -final RegExp _osArchLineRegex = RegExp( - r'os(?:=|: )(\S+?),? arch(?:=|: )(\S+?),? comp(?:=|: )(yes|no),? sim(?:=|: )(yes|no)'); -final RegExp _buildIdRegex = RegExp(r"build_id(?:=|: )'([\da-f]+)'"); -final RegExp _isolateDsoBaseLineRegex = - RegExp(r'isolate_dso_base(?:=|: )([\da-f]+)'); - @immutable @internal class DebugInfo { @@ -91,10 +82,20 @@ class DebugInfo { } } -/// Processes a stack trace by extracting debug image information from its header. +// Regular expressions for parsing header lines +const String _headerStartLine = + '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***'; +final RegExp _osArchLineRegex = RegExp( + r'os(?:=|: )(\S+?),? arch(?:=|: )(\S+?),? comp(?:=|: )(yes|no),? sim(?:=|: )(yes|no)'); +final RegExp _buildIdRegex = RegExp(r"build_id(?:=|: )'([\da-f]+)'"); +final RegExp _isolateDsoBaseLineRegex = + RegExp(r'isolate_dso_base(?:=|: )([\da-f]+)'); + +/// Processes a stack trace by extracting debug information from its header. +@internal @internal -class DebugImageExtractor { - DebugImageExtractor(this._options); +class DebugInfoExtractor { + DebugInfoExtractor(this._options); final SentryOptions _options; @@ -104,31 +105,46 @@ class DebugImageExtractor { String? isolateDsoBase; final lines = stackTrace.toString().split('\n'); + for (final line in lines) { - if (line.contains(_headerStartLine)) { - arch = buildId = isolateDsoBase = null; + if (_isHeaderStartLine(line)) { continue; } - final archMatch = _osArchLineRegex.firstMatch(line); - if (archMatch != null) { - arch = archMatch[2]; - continue; - } + arch ??= _extractArch(line); + buildId ??= _extractBuildId(line); + isolateDsoBase ??= _extractIsolateDsoBase(line); - final buildIdMatch = _buildIdRegex.firstMatch(line); - if (buildIdMatch != null) { - buildId = buildIdMatch[1]; - continue; + // Early return if all needed information is found + if (arch != null && buildId != null && isolateDsoBase != null) { + return DebugInfo(arch, buildId, isolateDsoBase, _options); } + } - final isolateMatch = _isolateDsoBaseLineRegex.firstMatch(line); - if (isolateMatch != null) { - isolateDsoBase = isolateMatch[1]; - continue; - } + if (arch == null || buildId == null || isolateDsoBase == null) { + _options.logger(SentryLevel.warning, + 'Incomplete debug info extracted from stack trace.'); } return DebugInfo(arch, buildId, isolateDsoBase, _options); } + + bool _isHeaderStartLine(String line) { + return line.contains(_headerStartLine); + } + + String? _extractArch(String line) { + final archMatch = _osArchLineRegex.firstMatch(line); + return archMatch?.group(2); + } + + String? _extractBuildId(String line) { + final buildIdMatch = _buildIdRegex.firstMatch(line); + return buildIdMatch?.group(1); + } + + String? _extractIsolateDsoBase(String line) { + final isolateMatch = _isolateDsoBaseLineRegex.firstMatch(line); + return isolateMatch?.group(1); + } } From 4caf3ad3bcf3be98c583c81385a5edaf2d2351de Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 20:39:23 +0200 Subject: [PATCH 10/40] update --- dart/lib/src/debug_info_extractor.dart | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/dart/lib/src/debug_info_extractor.dart b/dart/lib/src/debug_info_extractor.dart index bb6e584d00..8b21154d87 100644 --- a/dart/lib/src/debug_info_extractor.dart +++ b/dart/lib/src/debug_info_extractor.dart @@ -6,12 +6,11 @@ import '../sentry.dart'; @immutable @internal class DebugInfo { - final String? arch; final String? buildId; final String? isolateDsoBase; final SentryOptions options; - DebugInfo(this.arch, this.buildId, this.isolateDsoBase, this.options); + DebugInfo(this.buildId, this.isolateDsoBase, this.options); DebugImage? toDebugImage() { if (buildId == null || isolateDsoBase == null) { @@ -30,7 +29,6 @@ class DebugInfo { imageAddr: '0x$isolateDsoBase', debugId: debugId, codeId: codeId, - arch: arch, ); } @@ -85,8 +83,6 @@ class DebugInfo { // Regular expressions for parsing header lines const String _headerStartLine = '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***'; -final RegExp _osArchLineRegex = RegExp( - r'os(?:=|: )(\S+?),? arch(?:=|: )(\S+?),? comp(?:=|: )(yes|no),? sim(?:=|: )(yes|no)'); final RegExp _buildIdRegex = RegExp(r"build_id(?:=|: )'([\da-f]+)'"); final RegExp _isolateDsoBaseLineRegex = RegExp(r'isolate_dso_base(?:=|: )([\da-f]+)'); @@ -111,7 +107,6 @@ class DebugInfoExtractor { continue; } - arch ??= _extractArch(line); buildId ??= _extractBuildId(line); isolateDsoBase ??= _extractIsolateDsoBase(line); @@ -133,11 +128,6 @@ class DebugInfoExtractor { return line.contains(_headerStartLine); } - String? _extractArch(String line) { - final archMatch = _osArchLineRegex.firstMatch(line); - return archMatch?.group(2); - } - String? _extractBuildId(String line) { final buildIdMatch = _buildIdRegex.firstMatch(line); return buildIdMatch?.group(1); From 966def4a036f9fc0051d503c00b670cd23dd8f23 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 20:40:29 +0200 Subject: [PATCH 11/40] update --- dart/lib/src/debug_info_extractor.dart | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/dart/lib/src/debug_info_extractor.dart b/dart/lib/src/debug_info_extractor.dart index 8b21154d87..5e5c8a6e48 100644 --- a/dart/lib/src/debug_info_extractor.dart +++ b/dart/lib/src/debug_info_extractor.dart @@ -8,21 +8,23 @@ import '../sentry.dart'; class DebugInfo { final String? buildId; final String? isolateDsoBase; - final SentryOptions options; + final SentryOptions _options; - DebugInfo(this.buildId, this.isolateDsoBase, this.options); + DebugInfo(this.buildId, this.isolateDsoBase, this._options); DebugImage? toDebugImage() { if (buildId == null || isolateDsoBase == null) { - // TODO: log + _options.logger(SentryLevel.warning, + 'Cannot create DebugImage without buildId and isolateDsoBase.'); return null; } - final type = options.platformChecker.platform.isAndroid ? 'elf' : 'macho'; - final debugId = options.platformChecker.platform.isAndroid + final type = _options.platformChecker.platform.isAndroid ? 'elf' : 'macho'; + final debugId = _options.platformChecker.platform.isAndroid ? _convertCodeIdToDebugId(buildId!) : _hexToUuid(buildId!); - final codeId = options.platformChecker.platform.isAndroid ? buildId! : null; + final codeId = + _options.platformChecker.platform.isAndroid ? buildId! : null; return DebugImage( type: type, @@ -96,7 +98,6 @@ class DebugInfoExtractor { final SentryOptions _options; DebugInfo extractFrom(StackTrace stackTrace) { - String? arch; String? buildId; String? isolateDsoBase; @@ -111,17 +112,12 @@ class DebugInfoExtractor { isolateDsoBase ??= _extractIsolateDsoBase(line); // Early return if all needed information is found - if (arch != null && buildId != null && isolateDsoBase != null) { - return DebugInfo(arch, buildId, isolateDsoBase, _options); + if (buildId != null && isolateDsoBase != null) { + return DebugInfo(buildId, isolateDsoBase, _options); } } - if (arch == null || buildId == null || isolateDsoBase == null) { - _options.logger(SentryLevel.warning, - 'Incomplete debug info extracted from stack trace.'); - } - - return DebugInfo(arch, buildId, isolateDsoBase, _options); + return DebugInfo(buildId, isolateDsoBase, _options); } bool _isHeaderStartLine(String line) { From a6ffe9786d55c8eda590c7e8b1655f2c01a96c73 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 28 Aug 2024 21:24:21 +0200 Subject: [PATCH 12/40] fix --- flutter/lib/src/integrations/load_image_list_integration.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/src/integrations/load_image_list_integration.dart b/flutter/lib/src/integrations/load_image_list_integration.dart index dd28ec394a..b04b3860ba 100644 --- a/flutter/lib/src/integrations/load_image_list_integration.dart +++ b/flutter/lib/src/integrations/load_image_list_integration.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:sentry/sentry.dart'; +import '../../sentry_flutter.dart'; import '../native/sentry_native_binding.dart'; /// Loads the native debug image list for stack trace symbolication. From 188b0eef3bd60eeb55b196a6b9bab9a8729ff0d6 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 01:35:02 +0200 Subject: [PATCH 13/40] update --- .../src/dart_image_loading_integration.dart | 39 ------ ...ractor.dart => debug_image_extractor.dart} | 113 +++++++++--------- dart/lib/src/load_dart_image_integration.dart | 36 ++++++ dart/lib/src/sentry.dart | 4 +- dart/lib/src/sentry_client.dart | 3 +- dart/test/debug_image_extractor_test.dart | 102 +++++++++++----- .../load_dart_image_integration_test.dart | 111 +++++++++++++++++ dart/test/sentry_options_test.dart | 6 + .../integrations/load_image_list_test.dart | 2 + flutter/test/sentry_flutter_test.dart | 1 + 10 files changed, 285 insertions(+), 132 deletions(-) delete mode 100644 dart/lib/src/dart_image_loading_integration.dart rename dart/lib/src/{debug_info_extractor.dart => debug_image_extractor.dart} (89%) create mode 100644 dart/lib/src/load_dart_image_integration.dart create mode 100644 dart/test/load_dart_image_integration_test.dart diff --git a/dart/lib/src/dart_image_loading_integration.dart b/dart/lib/src/dart_image_loading_integration.dart deleted file mode 100644 index 4bca0f3c54..0000000000 --- a/dart/lib/src/dart_image_loading_integration.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:meta/meta.dart'; - -import '../sentry.dart'; -import 'debug_info_extractor.dart'; - -class DartImageLoadingIntegration extends Integration { - @override - void call(Hub hub, SentryOptions options) { - options.addEventProcessor( - _LoadImageIntegrationEventProcessor(DebugInfoExtractor(options))); - options.sdk.addIntegration('loadImageIntegration'); - } - - @override - void close() {} -} - -class _LoadImageIntegrationEventProcessor implements EventProcessor { - _LoadImageIntegrationEventProcessor(this._debugImageExtractor); - - final DebugInfoExtractor _debugImageExtractor; - - @override - Future apply(SentryEvent event, Hint hint) async { - if (!event.needsSymbolication()) { - return event; - } - if (event.stackTrace == null) { - return event; - } - final syntheticImage = - _debugImageExtractor.extractFrom(event.stackTrace!).toDebugImage(); - if (syntheticImage == null) { - return event; - } - event = event.copyWith(debugMeta: DebugMeta(images: [syntheticImage])); - return event; - } -} diff --git a/dart/lib/src/debug_info_extractor.dart b/dart/lib/src/debug_image_extractor.dart similarity index 89% rename from dart/lib/src/debug_info_extractor.dart rename to dart/lib/src/debug_image_extractor.dart index 5e5c8a6e48..ca2c2ae60b 100644 --- a/dart/lib/src/debug_info_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -3,14 +3,68 @@ import 'package:meta/meta.dart'; import '../sentry.dart'; -@immutable +// Regular expressions for parsing header lines +const String _headerStartLine = + '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***'; +final RegExp _buildIdRegex = RegExp(r"build_id(?:=|: )'([\da-f]+)'"); +final RegExp _isolateDsoBaseLineRegex = + RegExp(r'isolate_dso_base(?:=|: )([\da-f]+)'); + +/// Processes a stack trace by extracting debug information from its header. @internal -class DebugInfo { +class DebugImageExtractor { + DebugImageExtractor(this._options); + + final SentryOptions _options; + + DebugImage? extractDebugImageFrom(StackTrace stackTrace) { + return _extractDebugInfoFrom(stackTrace).toDebugImage(); + } + + _DebugInfo _extractDebugInfoFrom(StackTrace stackTrace) { + String? buildId; + String? isolateDsoBase; + + final lines = stackTrace.toString().split('\n'); + + for (final line in lines) { + if (_isHeaderStartLine(line)) { + continue; + } + + buildId ??= _extractBuildId(line); + isolateDsoBase ??= _extractIsolateDsoBase(line); + + // Early return if all needed information is found + if (buildId != null && isolateDsoBase != null) { + return _DebugInfo(buildId, isolateDsoBase, _options); + } + } + + return _DebugInfo(buildId, isolateDsoBase, _options); + } + + bool _isHeaderStartLine(String line) { + return line.contains(_headerStartLine); + } + + String? _extractBuildId(String line) { + final buildIdMatch = _buildIdRegex.firstMatch(line); + return buildIdMatch?.group(1); + } + + String? _extractIsolateDsoBase(String line) { + final isolateMatch = _isolateDsoBaseLineRegex.firstMatch(line); + return isolateMatch?.group(1); + } +} + +class _DebugInfo { final String? buildId; final String? isolateDsoBase; final SentryOptions _options; - DebugInfo(this.buildId, this.isolateDsoBase, this._options); + _DebugInfo(this.buildId, this.isolateDsoBase, this._options); DebugImage? toDebugImage() { if (buildId == null || isolateDsoBase == null) { @@ -81,56 +135,3 @@ class DebugInfo { '${hex.substring(20)}'; } } - -// Regular expressions for parsing header lines -const String _headerStartLine = - '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***'; -final RegExp _buildIdRegex = RegExp(r"build_id(?:=|: )'([\da-f]+)'"); -final RegExp _isolateDsoBaseLineRegex = - RegExp(r'isolate_dso_base(?:=|: )([\da-f]+)'); - -/// Processes a stack trace by extracting debug information from its header. -@internal -@internal -class DebugInfoExtractor { - DebugInfoExtractor(this._options); - - final SentryOptions _options; - - DebugInfo extractFrom(StackTrace stackTrace) { - String? buildId; - String? isolateDsoBase; - - final lines = stackTrace.toString().split('\n'); - - for (final line in lines) { - if (_isHeaderStartLine(line)) { - continue; - } - - buildId ??= _extractBuildId(line); - isolateDsoBase ??= _extractIsolateDsoBase(line); - - // Early return if all needed information is found - if (buildId != null && isolateDsoBase != null) { - return DebugInfo(buildId, isolateDsoBase, _options); - } - } - - return DebugInfo(buildId, isolateDsoBase, _options); - } - - bool _isHeaderStartLine(String line) { - return line.contains(_headerStartLine); - } - - String? _extractBuildId(String line) { - final buildIdMatch = _buildIdRegex.firstMatch(line); - return buildIdMatch?.group(1); - } - - String? _extractIsolateDsoBase(String line) { - final isolateMatch = _isolateDsoBaseLineRegex.firstMatch(line); - return isolateMatch?.group(1); - } -} diff --git a/dart/lib/src/load_dart_image_integration.dart b/dart/lib/src/load_dart_image_integration.dart new file mode 100644 index 0000000000..9ded65e403 --- /dev/null +++ b/dart/lib/src/load_dart_image_integration.dart @@ -0,0 +1,36 @@ +import '../sentry.dart'; +import 'debug_image_extractor.dart'; + +class LoadDartImageIntegration extends Integration { + @override + void call(Hub hub, SentryOptions options) { + options.addEventProcessor( + _LoadImageIntegrationEventProcessor(DebugImageExtractor(options))); + options.sdk.addIntegration('loadDartImageIntegration'); + } +} + +class _LoadImageIntegrationEventProcessor implements EventProcessor { + _LoadImageIntegrationEventProcessor(this._debugImageExtractor); + + final DebugImageExtractor _debugImageExtractor; + + @override + Future apply(SentryEvent event, Hint hint) async { + if (!event.needsSymbolication() || event.stackTrace == null) { + return event; + } + + final syntheticImage = + _debugImageExtractor.extractDebugImageFrom(event.stackTrace!); + if (syntheticImage == null) { + return event; + } + + DebugMeta debugMeta = event.debugMeta ?? DebugMeta(); + final images = debugMeta.images; + debugMeta = debugMeta.copyWith(images: [...images, syntheticImage]); + + return event.copyWith(debugMeta: debugMeta); + } +} diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index 1a3fcffdea..0104866e61 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; import 'dart_exception_type_identifier.dart'; -import 'dart_image_loading_integration.dart'; +import 'load_dart_image_integration.dart'; import 'metrics/metrics_api.dart'; import 'run_zoned_guarded_integration.dart'; import 'event_processor/enricher/enricher_event_processor.dart'; @@ -85,7 +85,7 @@ class Sentry { } if (options.enablePureDartSymbolication) { - options.addIntegration(DartImageLoadingIntegration()); + options.addIntegration(LoadDartImageIntegration()); } options.addEventProcessor(EnricherEventProcessor(options)); diff --git a/dart/lib/src/sentry_client.dart b/dart/lib/src/sentry_client.dart index 1a1fcc0444..8f0ecab164 100644 --- a/dart/lib/src/sentry_client.dart +++ b/dart/lib/src/sentry_client.dart @@ -3,7 +3,6 @@ import 'dart:math'; import 'package:meta/meta.dart'; -import '../sentry.dart'; import 'client_reports/client_report_recorder.dart'; import 'client_reports/discard_reason.dart'; import 'event_processor.dart'; @@ -208,7 +207,7 @@ class SentryClient { release: event.release ?? _options.release, sdk: event.sdk ?? _options.sdk, platform: event.platform ?? sdkPlatform(_options.platformChecker.isWeb), - stackTrace: stackTrace, + stackTrace: StackTrace.fromString(stackTrace.toString()), ); if (event is SentryTransaction) { diff --git a/dart/test/debug_image_extractor_test.dart b/dart/test/debug_image_extractor_test.dart index 740c0ccca9..98478ad3d0 100644 --- a/dart/test/debug_image_extractor_test.dart +++ b/dart/test/debug_image_extractor_test.dart @@ -1,54 +1,90 @@ import 'package:test/test.dart'; import 'package:sentry/sentry.dart'; +import 'package:sentry/src/debug_image_extractor.dart'; -import 'mocks.dart'; import 'mocks/mock_platform.dart'; import 'mocks/mock_platform_checker.dart'; void main() { - late DebugImageExtractor symbolizer; - late SentryOptions options; - late MockPlatformChecker mockPlatformChecker; + group('DebugImageExtractor', () { + late Fixture fixture; - setUp(() { - options = SentryOptions(dsn: fakeDsn); - symbolizer = DebugImageExtractor(options); - }); + setUp(() { + fixture = Fixture(); + }); + + test('extracts debug image from valid stack trace', () { + final stackTrace = StackTrace.fromString(''' +*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** +build_id: 'b680cb890f9e3c12a24b172d050dec73' +isolate_dso_base: 10000000 +'''); + final extractor = fixture.getSut(platform: MockPlatform.android()); + final debugImage = extractor.extractDebugImageFrom(stackTrace); + + expect(debugImage, isNotNull); + expect(debugImage?.debugId, isNotEmpty); + expect(debugImage?.imageAddr, equals('0x10000000')); + }); - test('Symbolizer correctly parses a valid stack trace header on Android', () { - mockPlatformChecker = MockPlatformChecker(platform: MockPlatform.android()); - options.platformChecker = mockPlatformChecker; + test('returns null for invalid stack trace', () { + final stackTrace = StackTrace.fromString('Invalid stack trace'); + final extractor = fixture.getSut(platform: MockPlatform.android()); + final debugImage = extractor.extractDebugImageFrom(stackTrace); - final validStackTrace = StackTrace.fromString(''' + expect(debugImage, isNull); + }); + + test('extracts correct debug ID for Android', () { + final stackTrace = StackTrace.fromString(''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** -os: android, arch: arm64, comp: yes, sim: no -build_id: 'f1c3bcc0279865fe3058404b2831d9e64135386c' -isolate_dso_base: 0f00000000 +build_id: 'b680cb890f9e3c12a24b172d050dec73' +isolate_dso_base: 20000000 '''); + final extractor = fixture.getSut(platform: MockPlatform.android()); + final debugImage = extractor.extractDebugImageFrom(stackTrace); - final debugImage = symbolizer.toImage(validStackTrace)!; + expect( + debugImage?.debugId, equals('89cb80b6-9e0f-123c-a24b-172d050dec73')); + }); - expect(debugImage.type, equals('elf')); - expect(debugImage.imageAddr, equals('0x0f00000000')); - expect( - debugImage.codeId, equals('f1c3bcc0279865fe3058404b2831d9e64135386c')); - expect(debugImage.debugId, equals('c0bcc3f1-9827-fe65-3058-404b2831d9e6')); - }); + test('extracts correct debug ID for iOS', () { + final stackTrace = StackTrace.fromString(''' +*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** +build_id: 'b680cb890f9e3c12a24b172d050dec73' +isolate_dso_base: 30000000 +'''); + final extractor = fixture.getSut(platform: MockPlatform.iOS()); + final debugImage = extractor.extractDebugImageFrom(stackTrace); - test('Symbolizer correctly parses a valid stack trace header on iOS', () { - mockPlatformChecker = MockPlatformChecker(platform: MockPlatform.iOS()); - options.platformChecker = mockPlatformChecker; + expect( + debugImage?.debugId, equals('b680cb89-0f9e-3c12-a24b-172d050dec73')); + expect(debugImage?.codeId, isNull); + }); - final validStackTrace = StackTrace.fromString(''' + test('sets correct type based on platform', () { + final stackTrace = StackTrace.fromString(''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** -os: ios, arch: arm64, comp: yes, sim: no build_id: 'b680cb890f9e3c12a24b172d050dec73' -isolate_dso_base: 0f00000000 +isolate_dso_base: 40000000 '''); - expect(debugImage.type, equals('macho')); - expect(debugImage.imageAddr, equals('0x0f00000000')); - // expect( - // debugImage.codeId, equals('f1c3bcc0279865fe3058404b2831d9e64135386c')); - expect(debugImage.debugId, equals('c0bcc3f1-9827-fe65-3058-404b2831d9e6')); + final androidExtractor = fixture.getSut(platform: MockPlatform.android()); + final iosExtractor = fixture.getSut(platform: MockPlatform.iOS()); + + final androidDebugImage = + androidExtractor.extractDebugImageFrom(stackTrace); + final iosDebugImage = iosExtractor.extractDebugImageFrom(stackTrace); + + expect(androidDebugImage?.type, equals('elf')); + expect(iosDebugImage?.type, equals('macho')); + }); }); } + +class Fixture { + DebugImageExtractor getSut({required MockPlatform platform}) { + final options = SentryOptions(dsn: 'https://public@sentry.example.com/1') + ..platformChecker = MockPlatformChecker(platform: platform); + return DebugImageExtractor(options); + } +} diff --git a/dart/test/load_dart_image_integration_test.dart b/dart/test/load_dart_image_integration_test.dart new file mode 100644 index 0000000000..bea3d00ae3 --- /dev/null +++ b/dart/test/load_dart_image_integration_test.dart @@ -0,0 +1,111 @@ +@TestOn('vm') +library dart_test; + +import 'package:sentry/sentry.dart'; +import 'package:sentry/src/load_dart_image_integration.dart'; +import 'package:test/test.dart'; + +void main() { + group(LoadDartImageIntegration(), () { + late Fixture fixture; + + setUp(() { + fixture = Fixture(); + }); + + test('adds itself to sdk.integrations', () { + expect( + fixture.options.sdk.integrations.contains('loadDartImageIntegration'), + true, + ); + }); + + test('Event processor is added to options', () { + expect(fixture.options.eventProcessors.length, 1); + expect( + fixture.options.eventProcessors.first.runtimeType.toString(), + '_LoadImageIntegrationEventProcessor', + ); + }); + + test('Event processor does not modify event if symbolication is not needed', + () async { + final event = _getEvent(needsSymbolication: false); + final processor = fixture.options.eventProcessors.first; + final resultEvent = await processor.apply(event, Hint()); + + expect(resultEvent, equals(event)); + }); + + test('Event processor does not modify event if stackTrace is null', + () async { + final event = _getEvent(); + final processor = fixture.options.eventProcessors.first; + final resultEvent = await processor.apply(event, Hint()); + + expect(resultEvent, equals(event)); + }); + + test('Event processor adds debug image when symbolication is needed', + () async { + final stackTrace = StackTrace.fromString(''' +*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** +build_id: 'b680cb890f9e3c12a24b172d050dec73' +isolate_dso_base: 10000000 +'''); + SentryEvent event = _getEvent(); + event = event.copyWith(stackTrace: stackTrace); + + final processor = fixture.options.eventProcessors.first; + final resultEvent = await processor.apply(event, Hint()); + + expect(resultEvent?.debugMeta?.images.length, 1); + final debugImage = resultEvent?.debugMeta?.images.first; + expect(debugImage?.debugId, isNotEmpty); + expect(debugImage?.imageAddr, equals('0x10000000')); + }); + + test('Event processor adds debug image to existing debugMeta', () async { + final stackTrace = StackTrace.fromString(''' +*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** +build_id: 'b680cb890f9e3c12a24b172d050dec73' +isolate_dso_base: 10000000 +'''); + final existingDebugImage = DebugImage( + type: 'macho', + debugId: 'existing-debug-id', + imageAddr: '0x2000', + ); + SentryEvent event = _getEvent(); + event = event.copyWith( + stackTrace: stackTrace, + debugMeta: DebugMeta(images: [existingDebugImage]), + ); + + final processor = fixture.options.eventProcessors.first; + final resultEvent = await processor.apply(event, Hint()); + + expect(resultEvent?.debugMeta?.images.length, 2); + expect(resultEvent?.debugMeta?.images, contains(existingDebugImage)); + expect( + resultEvent?.debugMeta?.images.last.imageAddr, equals('0x10000000')); + }); + }); +} + +class Fixture { + final options = SentryOptions(dsn: 'https://public@sentry.example.com/1'); + + Fixture() { + final integration = LoadDartImageIntegration(); + integration.call(Hub(options), options); + } +} + +SentryEvent _getEvent({bool needsSymbolication = true}) { + final frame = + SentryStackFrame(platform: needsSymbolication ? 'native' : 'dart'); + final st = SentryStackTrace(frames: [frame]); + return SentryEvent( + threads: [SentryThread(stacktrace: st)], debugMeta: DebugMeta()); +} diff --git a/dart/test/sentry_options_test.dart b/dart/test/sentry_options_test.dart index ccd06d5bf0..ac0d633694 100644 --- a/dart/test/sentry_options_test.dart +++ b/dart/test/sentry_options_test.dart @@ -192,4 +192,10 @@ void main() { expect(options.enableSpanLocalMetricAggregation, true); }); + + test('enablePureDartSymbolication is enabled by default', () { + final options = SentryOptions(dsn: fakeDsn); + + expect(options.enablePureDartSymbolication, true); + }); } diff --git a/flutter/test/integrations/load_image_list_test.dart b/flutter/test/integrations/load_image_list_test.dart index 35e59b7599..e9d4905ab3 100644 --- a/flutter/test/integrations/load_image_list_test.dart +++ b/flutter/test/integrations/load_image_list_test.dart @@ -102,6 +102,8 @@ void main() { await fixture.hub.captureMessage('error'); verifyNever(fixture.binding.loadDebugImages()); }); + + test('') }); } diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index 1d78b947d1..d5b6e96fc9 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -616,6 +616,7 @@ void main() { expect(sdkVersion, options.sdk.version); expect('pub:sentry_flutter', options.sdk.packages.last.name); expect(sdkVersion, options.sdk.packages.last.version); + expect(false, options.enablePureDartSymbolication); }, appRunner: appRunner, platformChecker: getPlatformChecker( From ef8ab0a4607bd376e92866fe41b8a872a44c8a25 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 01:49:34 +0200 Subject: [PATCH 14/40] fix tests --- .../integrations/load_image_list_test.dart | 2 - flutter/test/mocks.mocks.dart | 52 ++++++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/flutter/test/integrations/load_image_list_test.dart b/flutter/test/integrations/load_image_list_test.dart index e9d4905ab3..35e59b7599 100644 --- a/flutter/test/integrations/load_image_list_test.dart +++ b/flutter/test/integrations/load_image_list_test.dart @@ -102,8 +102,6 @@ void main() { await fixture.hub.captureMessage('error'); verifyNever(fixture.binding.loadDebugImages()); }); - - test('') }); } diff --git a/flutter/test/mocks.mocks.dart b/flutter/test/mocks.mocks.dart index 01d2127efe..9e6e4a64d1 100644 --- a/flutter/test/mocks.mocks.dart +++ b/flutter/test/mocks.mocks.dart @@ -263,6 +263,12 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { returnValueForMissingStub: null, ); + @override + Map get measurements => (super.noSuchMethod( + Invocation.getter(#measurements), + returnValue: {}, + ) as Map); + @override _i2.SentrySpanContext get context => (super.noSuchMethod( Invocation.getter(#context), @@ -332,12 +338,6 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { returnValue: {}, ) as Map); - @override - Map get measurements => (super.noSuchMethod( - Invocation.getter(#measurements), - returnValue: {}, - ) as Map); - @override _i8.Future finish({ _i3.SpanStatus? status, @@ -502,6 +502,24 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { returnValueForMissingStub: null, ); + @override + void setMeasurementFromChild( + String? name, + num? value, { + _i2.SentryMeasurementUnit? unit, + }) => + super.noSuchMethod( + Invocation.method( + #setMeasurementFromChild, + [ + name, + value, + ], + {#unit: unit}, + ), + returnValueForMissingStub: null, + ); + @override void scheduleFinish() => super.noSuchMethod( Invocation.method( @@ -669,6 +687,7 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { Map? measurements, Map>? metricSummaries, _i3.SentryTransactionInfo? transactionInfo, + StackTrace? stackTrace, }) => (super.noSuchMethod( Invocation.method( @@ -704,6 +723,7 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { #measurements: measurements, #metricSummaries: metricSummaries, #transactionInfo: transactionInfo, + #stackTrace: stackTrace, }, ), returnValue: _FakeSentryTransaction_7( @@ -741,10 +761,20 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { #measurements: measurements, #metricSummaries: metricSummaries, #transactionInfo: transactionInfo, + #stackTrace: stackTrace, }, ), ), ) as _i3.SentryTransaction); + + @override + bool needsSymbolication() => (super.noSuchMethod( + Invocation.method( + #needsSymbolication, + [], + ), + returnValue: false, + ) as bool); } /// A class which mocks [SentrySpan]. @@ -1346,6 +1376,16 @@ class MockSentryNativeBinding extends _i1.Mock returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + + @override + _i8.Future nativeCrash() => (super.noSuchMethod( + Invocation.method( + #nativeCrash, + [], + ), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), + ) as _i8.Future); } /// A class which mocks [Hub]. From 558a9dcb5a459740fbb2b55376cfcfcd331cab45 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 01:56:08 +0200 Subject: [PATCH 15/40] fix initial value test --- flutter/test/sentry_flutter_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index d5b6e96fc9..7728c50baa 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -621,7 +621,7 @@ void main() { appRunner: appRunner, platformChecker: getPlatformChecker( platform: MockPlatform.android(), - isWeb: true, + isWeb: false, ), ); From 666b65ffd20e9a0576689eea9664bf7185f1c7b0 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 13:07:54 +0200 Subject: [PATCH 16/40] Update comment and test --- dart/lib/src/sentry_options.dart | 6 ++++-- flutter/test/sentry_flutter_test.dart | 24 ++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 0479861d28..13b36f79ad 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -358,8 +358,10 @@ class SentryOptions { _ignoredExceptionsForType.contains(exception.runtimeType); } - /// If enabled, the SDK will attempt to symbolicate pure Dart stack traces. - /// Automatically set to false in `SentryFlutter.init`. + /// If enabled, the SDK will attempt to symbolicate pure Dart stack traces when + /// used in Flutter. This feature works on Flutter iOS, macOS and Android. + /// Due to automatic debug image loading from the native SDKs + /// this flag is automatically set to false in `SentryFlutter.init`. bool enablePureDartSymbolication = true; @internal diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index 7728c50baa..760d20b3d4 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -616,12 +616,32 @@ void main() { expect(sdkVersion, options.sdk.version); expect('pub:sentry_flutter', options.sdk.packages.last.name); expect(sdkVersion, options.sdk.packages.last.version); - expect(false, options.enablePureDartSymbolication); }, appRunner: appRunner, platformChecker: getPlatformChecker( platform: MockPlatform.android(), - isWeb: false, + isWeb: true, + ), + ); + + await Sentry.close(); + }); + + test( + 'enablePureDartSymbolication is set to false during SentryFlutter init', + () async { + SentryFlutter.native = MockSentryNativeBinding(); + await SentryFlutter.init( + (options) { + options.dsn = fakeDsn; + options.automatedTestMode = true; + + expect(options.enablePureDartSymbolication, false); + }, + appRunner: appRunner, + platformChecker: getPlatformChecker( + platform: MockPlatform.android(), + isWeb: true, ), ); From 621a095c490ed5ec59eb7eac1dea626ff1a987f9 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 13:15:52 +0200 Subject: [PATCH 17/40] update --- dart/lib/src/protocol/sentry_event.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dart/lib/src/protocol/sentry_event.dart b/dart/lib/src/protocol/sentry_event.dart index 5994475e38..426ed6678d 100644 --- a/dart/lib/src/protocol/sentry_event.dart +++ b/dart/lib/src/protocol/sentry_event.dart @@ -1,5 +1,6 @@ import 'package:meta/meta.dart'; +import '../load_dart_image_integration.dart'; import '../protocol.dart'; import '../throwable_mechanism.dart'; import '../utils.dart'; @@ -195,7 +196,7 @@ class SentryEvent with SentryEventLike { @internal final Map? unknown; - /// The stacktrace is only used internally for processing such as in [DartImageLoadingIntegration]. + /// The stacktrace is only used internally for processing such as in [LoadDartImageIntegration]. /// TODO: should be a better way than saving a reference to the stacktrace @internal final StackTrace? stackTrace; From c3ec5e8bfba22e5007401cff8e2d2aaa97cd169b Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 13:43:17 +0200 Subject: [PATCH 18/40] Update NeedsSymbolication --- dart/lib/src/load_dart_image_integration.dart | 28 +++++++++++++++++++ dart/lib/src/protocol/sentry_event.dart | 24 ---------------- .../load_image_list_integration.dart | 4 +++ 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/dart/lib/src/load_dart_image_integration.dart b/dart/lib/src/load_dart_image_integration.dart index 9ded65e403..6c5f33c42d 100644 --- a/dart/lib/src/load_dart_image_integration.dart +++ b/dart/lib/src/load_dart_image_integration.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../sentry.dart'; import 'debug_image_extractor.dart'; @@ -34,3 +36,29 @@ class _LoadImageIntegrationEventProcessor implements EventProcessor { return event.copyWith(debugMeta: debugMeta); } } + +extension NeedsSymbolication on SentryEvent { + bool needsSymbolication() { + if (this is SentryTransaction) { + return false; + } + final frames = _getStacktraceFrames(); + if (frames == null) { + return false; + } + return frames.any((frame) => 'native' == frame?.platform); + } + + Iterable? _getStacktraceFrames() { + if (exceptions?.isNotEmpty == true) { + return exceptions?.first.stackTrace?.frames; + } + if (threads?.isNotEmpty == true) { + var stacktraces = threads?.map((e) => e.stacktrace); + return stacktraces + ?.where((element) => element != null) + .expand((element) => element!.frames); + } + return null; + } +} diff --git a/dart/lib/src/protocol/sentry_event.dart b/dart/lib/src/protocol/sentry_event.dart index 426ed6678d..bab5a7ce04 100644 --- a/dart/lib/src/protocol/sentry_event.dart +++ b/dart/lib/src/protocol/sentry_event.dart @@ -420,28 +420,4 @@ class SentryEvent with SentryEventLike { if (threadJson?.isNotEmpty ?? false) 'threads': {'values': threadJson}, }; } - - bool needsSymbolication() { - if (this is SentryTransaction) { - return false; - } - final frames = _getStacktraceFrames(); - if (frames == null) { - return false; - } - return frames.any((frame) => 'native' == frame?.platform); - } - - Iterable? _getStacktraceFrames() { - if (exceptions?.isNotEmpty == true) { - return exceptions?.first.stackTrace?.frames; - } - if (threads?.isNotEmpty == true) { - var stacktraces = threads?.map((e) => e.stacktrace); - return stacktraces - ?.where((element) => element != null) - .expand((element) => element!.frames); - } - return null; - } } diff --git a/flutter/lib/src/integrations/load_image_list_integration.dart b/flutter/lib/src/integrations/load_image_list_integration.dart index b04b3860ba..eb641afaa2 100644 --- a/flutter/lib/src/integrations/load_image_list_integration.dart +++ b/flutter/lib/src/integrations/load_image_list_integration.dart @@ -3,6 +3,10 @@ import 'dart:async'; import '../../sentry_flutter.dart'; import '../native/sentry_native_binding.dart'; +// ignore: implementation_imports +import 'package:sentry/src/load_dart_image_integration.dart' + show NeedsSymbolication; + /// Loads the native debug image list for stack trace symbolication. class LoadImageListIntegration extends Integration { final SentryNativeBinding _native; From df5d983a03c185db7271f52e8ad4a7ba7d29c241 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 13:47:13 +0200 Subject: [PATCH 19/40] revert sample --- flutter/example/lib/isar/user.dart | 24 +- flutter/example/lib/isar/user.g.dart | 1106 ++++++++++++------------ flutter/example/lib/main.dart | 58 +- flutter/example/pubspec.yaml | 8 +- flutter/example/pubspec_overrides.yaml | 4 +- 5 files changed, 600 insertions(+), 600 deletions(-) diff --git a/flutter/example/lib/isar/user.dart b/flutter/example/lib/isar/user.dart index 3ca768ae45..f255d2389d 100644 --- a/flutter/example/lib/isar/user.dart +++ b/flutter/example/lib/isar/user.dart @@ -1,12 +1,12 @@ -// import 'package:isar/isar.dart'; -// -// part 'user.g.dart'; -// -// @collection -// class User { -// Id id = Isar.autoIncrement; -// -// String? name; -// -// int? age; -// } +import 'package:isar/isar.dart'; + +part 'user.g.dart'; + +@collection +class User { + Id id = Isar.autoIncrement; + + String? name; + + int? age; +} diff --git a/flutter/example/lib/isar/user.g.dart b/flutter/example/lib/isar/user.g.dart index c90f98fa8f..c4d7ef985f 100644 --- a/flutter/example/lib/isar/user.g.dart +++ b/flutter/example/lib/isar/user.g.dart @@ -1,553 +1,553 @@ -// // GENERATED CODE - DO NOT MODIFY BY HAND -// -// part of 'user.dart'; -// -// // ************************************************************************** -// // IsarCollectionGenerator -// // ************************************************************************** -// -// // coverage:ignore-file -// // ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types -// -// extension GetUserCollection on Isar { -// IsarCollection get users => this.collection(); -// } -// -// final UserSchema = CollectionSchema( -// name: r'User', -// id: BigInt.parse("-7838171048429979076").toInt(), -// properties: { -// r'age': PropertySchema( -// id: BigInt.parse("0").toInt(), -// name: r'age', -// type: IsarType.long, -// ), -// r'name': PropertySchema( -// id: BigInt.parse("1").toInt(), -// name: r'name', -// type: IsarType.string, -// ) -// }, -// estimateSize: _userEstimateSize, -// serialize: _userSerialize, -// deserialize: _userDeserialize, -// deserializeProp: _userDeserializeProp, -// idName: r'id', -// indexes: {}, -// links: {}, -// embeddedSchemas: {}, -// getId: _userGetId, -// getLinks: _userGetLinks, -// attach: _userAttach, -// version: '3.1.0', -// ); -// -// int _userEstimateSize( -// User object, -// List offsets, -// Map> allOffsets, -// ) { -// var bytesCount = offsets.last; -// { -// final value = object.name; -// if (value != null) { -// bytesCount += 3 + value.length * 3; -// } -// } -// return bytesCount; -// } -// -// void _userSerialize( -// User object, -// IsarWriter writer, -// List offsets, -// Map> allOffsets, -// ) { -// writer.writeLong(offsets[0], object.age); -// writer.writeString(offsets[1], object.name); -// } -// -// User _userDeserialize( -// Id id, -// IsarReader reader, -// List offsets, -// Map> allOffsets, -// ) { -// final object = User(); -// object.age = reader.readLongOrNull(offsets[0]); -// object.id = id; -// object.name = reader.readStringOrNull(offsets[1]); -// return object; -// } -// -// P _userDeserializeProp

( -// IsarReader reader, -// int propertyId, -// int offset, -// Map> allOffsets, -// ) { -// switch (propertyId) { -// case 0: -// return (reader.readLongOrNull(offset)) as P; -// case 1: -// return (reader.readStringOrNull(offset)) as P; -// default: -// throw IsarError('Unknown property with id $propertyId'); -// } -// } -// -// Id _userGetId(User object) { -// return object.id; -// } -// -// List> _userGetLinks(User object) { -// return []; -// } -// -// void _userAttach(IsarCollection col, Id id, User object) { -// object.id = id; -// } -// -// extension UserQueryWhereSort on QueryBuilder { -// QueryBuilder anyId() { -// return QueryBuilder.apply(this, (query) { -// return query.addWhereClause(const IdWhereClause.any()); -// }); -// } -// } -// -// extension UserQueryWhere on QueryBuilder { -// QueryBuilder idEqualTo(Id id) { -// return QueryBuilder.apply(this, (query) { -// return query.addWhereClause(IdWhereClause.between( -// lower: id, -// upper: id, -// )); -// }); -// } -// -// QueryBuilder idNotEqualTo(Id id) { -// return QueryBuilder.apply(this, (query) { -// if (query.whereSort == Sort.asc) { -// return query -// .addWhereClause( -// IdWhereClause.lessThan(upper: id, includeUpper: false), -// ) -// .addWhereClause( -// IdWhereClause.greaterThan(lower: id, includeLower: false), -// ); -// } else { -// return query -// .addWhereClause( -// IdWhereClause.greaterThan(lower: id, includeLower: false), -// ) -// .addWhereClause( -// IdWhereClause.lessThan(upper: id, includeUpper: false), -// ); -// } -// }); -// } -// -// QueryBuilder idGreaterThan(Id id, -// {bool include = false}) { -// return QueryBuilder.apply(this, (query) { -// return query.addWhereClause( -// IdWhereClause.greaterThan(lower: id, includeLower: include), -// ); -// }); -// } -// -// QueryBuilder idLessThan(Id id, -// {bool include = false}) { -// return QueryBuilder.apply(this, (query) { -// return query.addWhereClause( -// IdWhereClause.lessThan(upper: id, includeUpper: include), -// ); -// }); -// } -// -// QueryBuilder idBetween( -// Id lowerId, -// Id upperId, { -// bool includeLower = true, -// bool includeUpper = true, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addWhereClause(IdWhereClause.between( -// lower: lowerId, -// includeLower: includeLower, -// upper: upperId, -// includeUpper: includeUpper, -// )); -// }); -// } -// } -// -// extension UserQueryFilter on QueryBuilder { -// QueryBuilder ageIsNull() { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(const FilterCondition.isNull( -// property: r'age', -// )); -// }); -// } -// -// QueryBuilder ageIsNotNull() { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(const FilterCondition.isNotNull( -// property: r'age', -// )); -// }); -// } -// -// QueryBuilder ageEqualTo(int? value) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.equalTo( -// property: r'age', -// value: value, -// )); -// }); -// } -// -// QueryBuilder ageGreaterThan( -// int? value, { -// bool include = false, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.greaterThan( -// include: include, -// property: r'age', -// value: value, -// )); -// }); -// } -// -// QueryBuilder ageLessThan( -// int? value, { -// bool include = false, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.lessThan( -// include: include, -// property: r'age', -// value: value, -// )); -// }); -// } -// -// QueryBuilder ageBetween( -// int? lower, -// int? upper, { -// bool includeLower = true, -// bool includeUpper = true, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.between( -// property: r'age', -// lower: lower, -// includeLower: includeLower, -// upper: upper, -// includeUpper: includeUpper, -// )); -// }); -// } -// -// QueryBuilder idEqualTo(Id value) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.equalTo( -// property: r'id', -// value: value, -// )); -// }); -// } -// -// QueryBuilder idGreaterThan( -// Id value, { -// bool include = false, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.greaterThan( -// include: include, -// property: r'id', -// value: value, -// )); -// }); -// } -// -// QueryBuilder idLessThan( -// Id value, { -// bool include = false, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.lessThan( -// include: include, -// property: r'id', -// value: value, -// )); -// }); -// } -// -// QueryBuilder idBetween( -// Id lower, -// Id upper, { -// bool includeLower = true, -// bool includeUpper = true, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.between( -// property: r'id', -// lower: lower, -// includeLower: includeLower, -// upper: upper, -// includeUpper: includeUpper, -// )); -// }); -// } -// -// QueryBuilder nameIsNull() { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(const FilterCondition.isNull( -// property: r'name', -// )); -// }); -// } -// -// QueryBuilder nameIsNotNull() { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(const FilterCondition.isNotNull( -// property: r'name', -// )); -// }); -// } -// -// QueryBuilder nameEqualTo( -// String? value, { -// bool caseSensitive = true, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.equalTo( -// property: r'name', -// value: value, -// caseSensitive: caseSensitive, -// )); -// }); -// } -// -// QueryBuilder nameGreaterThan( -// String? value, { -// bool include = false, -// bool caseSensitive = true, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.greaterThan( -// include: include, -// property: r'name', -// value: value, -// caseSensitive: caseSensitive, -// )); -// }); -// } -// -// QueryBuilder nameLessThan( -// String? value, { -// bool include = false, -// bool caseSensitive = true, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.lessThan( -// include: include, -// property: r'name', -// value: value, -// caseSensitive: caseSensitive, -// )); -// }); -// } -// -// QueryBuilder nameBetween( -// String? lower, -// String? upper, { -// bool includeLower = true, -// bool includeUpper = true, -// bool caseSensitive = true, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.between( -// property: r'name', -// lower: lower, -// includeLower: includeLower, -// upper: upper, -// includeUpper: includeUpper, -// caseSensitive: caseSensitive, -// )); -// }); -// } -// -// QueryBuilder nameStartsWith( -// String value, { -// bool caseSensitive = true, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.startsWith( -// property: r'name', -// value: value, -// caseSensitive: caseSensitive, -// )); -// }); -// } -// -// QueryBuilder nameEndsWith( -// String value, { -// bool caseSensitive = true, -// }) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.endsWith( -// property: r'name', -// value: value, -// caseSensitive: caseSensitive, -// )); -// }); -// } -// -// QueryBuilder nameContains(String value, -// {bool caseSensitive = true}) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.contains( -// property: r'name', -// value: value, -// caseSensitive: caseSensitive, -// )); -// }); -// } -// -// QueryBuilder nameMatches(String pattern, -// {bool caseSensitive = true}) { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.matches( -// property: r'name', -// wildcard: pattern, -// caseSensitive: caseSensitive, -// )); -// }); -// } -// -// QueryBuilder nameIsEmpty() { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.equalTo( -// property: r'name', -// value: '', -// )); -// }); -// } -// -// QueryBuilder nameIsNotEmpty() { -// return QueryBuilder.apply(this, (query) { -// return query.addFilterCondition(FilterCondition.greaterThan( -// property: r'name', -// value: '', -// )); -// }); -// } -// } -// -// extension UserQueryObject on QueryBuilder {} -// -// extension UserQueryLinks on QueryBuilder {} -// -// extension UserQuerySortBy on QueryBuilder { -// QueryBuilder sortByAge() { -// return QueryBuilder.apply(this, (query) { -// return query.addSortBy(r'age', Sort.asc); -// }); -// } -// -// QueryBuilder sortByAgeDesc() { -// return QueryBuilder.apply(this, (query) { -// return query.addSortBy(r'age', Sort.desc); -// }); -// } -// -// QueryBuilder sortByName() { -// return QueryBuilder.apply(this, (query) { -// return query.addSortBy(r'name', Sort.asc); -// }); -// } -// -// QueryBuilder sortByNameDesc() { -// return QueryBuilder.apply(this, (query) { -// return query.addSortBy(r'name', Sort.desc); -// }); -// } -// } -// -// extension UserQuerySortThenBy on QueryBuilder { -// QueryBuilder thenByAge() { -// return QueryBuilder.apply(this, (query) { -// return query.addSortBy(r'age', Sort.asc); -// }); -// } -// -// QueryBuilder thenByAgeDesc() { -// return QueryBuilder.apply(this, (query) { -// return query.addSortBy(r'age', Sort.desc); -// }); -// } -// -// QueryBuilder thenById() { -// return QueryBuilder.apply(this, (query) { -// return query.addSortBy(r'id', Sort.asc); -// }); -// } -// -// QueryBuilder thenByIdDesc() { -// return QueryBuilder.apply(this, (query) { -// return query.addSortBy(r'id', Sort.desc); -// }); -// } -// -// QueryBuilder thenByName() { -// return QueryBuilder.apply(this, (query) { -// return query.addSortBy(r'name', Sort.asc); -// }); -// } -// -// QueryBuilder thenByNameDesc() { -// return QueryBuilder.apply(this, (query) { -// return query.addSortBy(r'name', Sort.desc); -// }); -// } -// } -// -// extension UserQueryWhereDistinct on QueryBuilder { -// QueryBuilder distinctByAge() { -// return QueryBuilder.apply(this, (query) { -// return query.addDistinctBy(r'age'); -// }); -// } -// -// QueryBuilder distinctByName( -// {bool caseSensitive = true}) { -// return QueryBuilder.apply(this, (query) { -// return query.addDistinctBy(r'name', caseSensitive: caseSensitive); -// }); -// } -// } -// -// extension UserQueryProperty on QueryBuilder { -// QueryBuilder idProperty() { -// return QueryBuilder.apply(this, (query) { -// return query.addPropertyName(r'id'); -// }); -// } -// -// QueryBuilder ageProperty() { -// return QueryBuilder.apply(this, (query) { -// return query.addPropertyName(r'age'); -// }); -// } -// -// QueryBuilder nameProperty() { -// return QueryBuilder.apply(this, (query) { -// return query.addPropertyName(r'name'); -// }); -// } -// } +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types + +extension GetUserCollection on Isar { + IsarCollection get users => this.collection(); +} + +final UserSchema = CollectionSchema( + name: r'User', + id: BigInt.parse("-7838171048429979076").toInt(), + properties: { + r'age': PropertySchema( + id: BigInt.parse("0").toInt(), + name: r'age', + type: IsarType.long, + ), + r'name': PropertySchema( + id: BigInt.parse("1").toInt(), + name: r'name', + type: IsarType.string, + ) + }, + estimateSize: _userEstimateSize, + serialize: _userSerialize, + deserialize: _userDeserialize, + deserializeProp: _userDeserializeProp, + idName: r'id', + indexes: {}, + links: {}, + embeddedSchemas: {}, + getId: _userGetId, + getLinks: _userGetLinks, + attach: _userAttach, + version: '3.1.0', +); + +int _userEstimateSize( + User object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + { + final value = object.name; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + return bytesCount; +} + +void _userSerialize( + User object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeLong(offsets[0], object.age); + writer.writeString(offsets[1], object.name); +} + +User _userDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = User(); + object.age = reader.readLongOrNull(offsets[0]); + object.id = id; + object.name = reader.readStringOrNull(offsets[1]); + return object; +} + +P _userDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readLongOrNull(offset)) as P; + case 1: + return (reader.readStringOrNull(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _userGetId(User object) { + return object.id; +} + +List> _userGetLinks(User object) { + return []; +} + +void _userAttach(IsarCollection col, Id id, User object) { + object.id = id; +} + +extension UserQueryWhereSort on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension UserQueryWhere on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder idNotEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder idGreaterThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } +} + +extension UserQueryFilter on QueryBuilder { + QueryBuilder ageIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'age', + )); + }); + } + + QueryBuilder ageIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'age', + )); + }); + } + + QueryBuilder ageEqualTo(int? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'age', + value: value, + )); + }); + } + + QueryBuilder ageGreaterThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'age', + value: value, + )); + }); + } + + QueryBuilder ageLessThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'age', + value: value, + )); + }); + } + + QueryBuilder ageBetween( + int? lower, + int? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'age', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder idEqualTo(Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder nameIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'name', + )); + }); + } + + QueryBuilder nameIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'name', + )); + }); + } + + QueryBuilder nameEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameContains(String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameMatches(String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'name', + value: '', + )); + }); + } + + QueryBuilder nameIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'name', + value: '', + )); + }); + } +} + +extension UserQueryObject on QueryBuilder {} + +extension UserQueryLinks on QueryBuilder {} + +extension UserQuerySortBy on QueryBuilder { + QueryBuilder sortByAge() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'age', Sort.asc); + }); + } + + QueryBuilder sortByAgeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'age', Sort.desc); + }); + } + + QueryBuilder sortByName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.asc); + }); + } + + QueryBuilder sortByNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.desc); + }); + } +} + +extension UserQuerySortThenBy on QueryBuilder { + QueryBuilder thenByAge() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'age', Sort.asc); + }); + } + + QueryBuilder thenByAgeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'age', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.asc); + }); + } + + QueryBuilder thenByNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.desc); + }); + } +} + +extension UserQueryWhereDistinct on QueryBuilder { + QueryBuilder distinctByAge() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'age'); + }); + } + + QueryBuilder distinctByName( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'name', caseSensitive: caseSensitive); + }); + } +} + +extension UserQueryProperty on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder ageProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'age'); + }); + } + + QueryBuilder nameProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'name'); + }); + } +} diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 5a5ebc868f..a94f1743c0 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -16,7 +16,7 @@ import 'package:sentry_dio/sentry_dio.dart'; import 'package:sentry_drift/sentry_drift.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_hive/sentry_hive.dart'; -// import 'package:sentry_isar/sentry_isar.dart'; +import 'package:sentry_isar/sentry_isar.dart'; import 'package:sentry_logging/sentry_logging.dart'; import 'package:sentry_sqflite/sentry_sqflite.dart'; import 'package:sqflite/sqflite.dart'; @@ -580,34 +580,34 @@ class MainScaffold extends StatelessWidget { } Future isarTest() async { - // final tr = Sentry.startTransaction( - // 'isarTest', - // 'db', - // bindToScope: true, - // ); - // - // final dir = await getApplicationDocumentsDirectory(); - // - // final isar = await SentryIsar.open( - // [UserSchema], - // directory: dir.path, - // ); - // - // final newUser = User() - // ..name = 'Joe Dirt' - // ..age = 36; - // - // await isar.writeTxn(() async { - // await isar.users.put(newUser); // insert & update - // }); - // - // final existingUser = await isar.users.get(newUser.id); // get - // - // await isar.writeTxn(() async { - // await isar.users.delete(existingUser!.id); // delete - // }); - // - // await tr.finish(status: const SpanStatus.ok()); + final tr = Sentry.startTransaction( + 'isarTest', + 'db', + bindToScope: true, + ); + + final dir = await getApplicationDocumentsDirectory(); + + final isar = await SentryIsar.open( + [UserSchema], + directory: dir.path, + ); + + final newUser = User() + ..name = 'Joe Dirt' + ..age = 36; + + await isar.writeTxn(() async { + await isar.users.put(newUser); // insert & update + }); + + final existingUser = await isar.users.get(newUser.id); // get + + await isar.writeTxn(() async { + await isar.users.delete(existingUser!.id); // delete + }); + + await tr.finish(status: const SpanStatus.ok()); } Future hiveTest() async { diff --git a/flutter/example/pubspec.yaml b/flutter/example/pubspec.yaml index f042f3f5b0..dae5c41ece 100644 --- a/flutter/example/pubspec.yaml +++ b/flutter/example/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: sentry_file: sentry_hive: sentry_drift: -# sentry_isar: + sentry_isar: universal_platform: ^1.0.0 feedback: ^2.0.0 provider: ^6.0.0 @@ -27,7 +27,7 @@ dependencies: sqflite: any # This gets constrained by `sentry_sqflite` logging: any # This gets constrained by `sentry_logging` drift: any # This gets constrained by `sentry_drift` -# isar: any # This gets constrained by `sentry_isar` + isar: any # This gets constrained by `sentry_isar` package_info_plus: ^4.0.0 path_provider: ^2.0.0 #sqflite_common_ffi: ^2.0.0 @@ -53,8 +53,8 @@ flutter: - assets/sentry-wordmark.png sentry: -# upload_source_maps: true -# upload_sources: true + upload_source_maps: true + upload_sources: true project: sentry-flutter org: sentry-sdks wait_for_processing: true diff --git a/flutter/example/pubspec_overrides.yaml b/flutter/example/pubspec_overrides.yaml index d0e3a2713b..a392cc626d 100644 --- a/flutter/example/pubspec_overrides.yaml +++ b/flutter/example/pubspec_overrides.yaml @@ -15,5 +15,5 @@ dependency_overrides: path: ../../hive sentry_drift: path: ../../drift -# sentry_isar: -# path: ../../isar + sentry_isar: + path: ../../isar From 859ac244de3c955f4fee00f97f2fbb8d74e52a5b Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 13:48:27 +0200 Subject: [PATCH 20/40] revert --- .../ios/Runner.xcodeproj/project.pbxproj | 3 --- flutter/example/ios/Runner/AppDelegate.swift | 2 +- flutter/example/lib/main.dart | 22 +++++++++---------- .../macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../example/macos/Runner/AppDelegate.swift | 2 +- 6 files changed, 14 insertions(+), 19 deletions(-) diff --git a/flutter/example/ios/Runner.xcodeproj/project.pbxproj b/flutter/example/ios/Runner.xcodeproj/project.pbxproj index b02216e2f6..11cb23fc9d 100644 --- a/flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -734,8 +734,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 97JCY7859U; ENABLE_BITCODE = NO; @@ -755,7 +753,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/flutter/example/ios/Runner/AppDelegate.swift b/flutter/example/ios/Runner/AppDelegate.swift index c24cacbbb2..a231cc9c60 100644 --- a/flutter/example/ios/Runner/AppDelegate.swift +++ b/flutter/example/ios/Runner/AppDelegate.swift @@ -2,7 +2,7 @@ import UIKit import Flutter import Sentry -@main +@UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { private let _channel = "example.flutter.sentry.io" diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index a94f1743c0..7c11d17cca 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -27,7 +27,7 @@ import 'package:universal_platform/universal_platform.dart'; import 'auto_close_screen.dart'; import 'drift/connection/connection.dart'; import 'drift/database.dart'; -// import 'isar/user.dart'; +import 'isar/user.dart'; import 'user_feedback_dialog.dart'; // ATTENTION: Change the DSN below with your own to see the events in Sentry. Get one at sentry.io @@ -66,29 +66,29 @@ Future setupSentry( (options) { options.dsn = exampleDsn; options.tracesSampleRate = 1.0; - // options.profilesSampleRate = 1.0; - // options.reportPackages = false; + options.profilesSampleRate = 1.0; + options.reportPackages = false; options.addInAppInclude('sentry_flutter_example'); options.considerInAppFramesByDefault = false; options.attachThreads = true; - // options.enableWindowMetricBreadcrumbs = true; + options.enableWindowMetricBreadcrumbs = true; options.addIntegration(LoggingIntegration(minEventLevel: Level.INFO)); options.sendDefaultPii = true; - // options.reportSilentFlutterErrors = true; - // options.attachScreenshot = true; - // options.screenshotQuality = SentryScreenshotQuality.low; - // options.attachViewHierarchy = true; + options.reportSilentFlutterErrors = true; + options.attachScreenshot = true; + options.screenshotQuality = SentryScreenshotQuality.low; + options.attachViewHierarchy = true; // We can enable Sentry debug logging during development. This is likely // going to log too much for your app, but can be useful when figuring out // configuration issues, e.g. finding out why your events are not uploaded. options.debug = true; options.spotlight = Spotlight(enabled: true); - // options.enableTimeToFullDisplayTracing = true; + options.enableTimeToFullDisplayTracing = true; options.enableMetrics = true; options.maxRequestBodySize = MaxRequestBodySize.always; options.maxResponseBodySize = MaxResponseBodySize.always; - // options.navigatorKey = navigatorKey; + options.navigatorKey = navigatorKey; _isIntegrationTest = isIntegrationTest; if (_isIntegrationTest) { @@ -205,7 +205,6 @@ class MainScaffold extends StatelessWidget { 'Long press a button to see more information. (hover on web)'), ), ), - // Text(StackTrace.current.toString()), TooltipButton( onPressed: () => navigateToAutoCloseScreen(context), text: @@ -560,7 +559,6 @@ class MainScaffold extends StatelessWidget { 'Demonstrates the metrics. It creates several metrics and send them to Sentry.', buttonTitle: 'Metrics', ), - SelectableText(StackTrace.current.toString()), if (UniversalPlatform.isIOS || UniversalPlatform.isMacOS) const CocoaExample(), if (UniversalPlatform.isAndroid) const AndroidExample(), diff --git a/flutter/example/macos/Runner.xcodeproj/project.pbxproj b/flutter/example/macos/Runner.xcodeproj/project.pbxproj index 103a73ecaa..ad9c506c71 100644 --- a/flutter/example/macos/Runner.xcodeproj/project.pbxproj +++ b/flutter/example/macos/Runner.xcodeproj/project.pbxproj @@ -209,7 +209,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1430; - LastUpgradeCheck = 1510; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/flutter/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 9adb32c6e4..99a6840afc 100644 --- a/flutter/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/flutter/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Bool { return true From cd8b486913f407b077bd0c240eae994eec509842 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 13:58:00 +0200 Subject: [PATCH 21/40] update --- dart/lib/src/load_dart_image_integration.dart | 2 -- flutter/lib/src/integrations/load_image_list_integration.dart | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dart/lib/src/load_dart_image_integration.dart b/dart/lib/src/load_dart_image_integration.dart index 6c5f33c42d..c6dc5e67bf 100644 --- a/dart/lib/src/load_dart_image_integration.dart +++ b/dart/lib/src/load_dart_image_integration.dart @@ -1,5 +1,3 @@ -import 'package:meta/meta.dart'; - import '../sentry.dart'; import 'debug_image_extractor.dart'; diff --git a/flutter/lib/src/integrations/load_image_list_integration.dart b/flutter/lib/src/integrations/load_image_list_integration.dart index eb641afaa2..51d7ca8b75 100644 --- a/flutter/lib/src/integrations/load_image_list_integration.dart +++ b/flutter/lib/src/integrations/load_image_list_integration.dart @@ -1,7 +1,8 @@ import 'dart:async'; -import '../../sentry_flutter.dart'; +import 'package:sentry/sentry.dart'; import '../native/sentry_native_binding.dart'; +import '../sentry_flutter_options.dart'; // ignore: implementation_imports import 'package:sentry/src/load_dart_image_integration.dart' From 6369c35602d849b2dd4bac41f662ceef88714838 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 14:13:12 +0200 Subject: [PATCH 22/40] update naming --- ...> load_dart_debug_images_integration.dart} | 8 ++--- dart/lib/src/protocol/sentry_event.dart | 4 +-- dart/lib/src/sentry.dart | 4 +-- ...d_dart_debug_images_integration_test.dart} | 32 ++----------------- .../lib/src/integrations/integrations.dart | 2 +- ...load_native_debug_images_integration.dart} | 7 ++-- flutter/lib/src/sentry_flutter.dart | 2 +- ...art => load_native_debug_images_test.dart} | 11 ++++--- flutter/test/sentry_flutter_test.dart | 4 +-- 9 files changed, 23 insertions(+), 51 deletions(-) rename dart/lib/src/{load_dart_image_integration.dart => load_dart_debug_images_integration.dart} (84%) rename dart/test/{load_dart_image_integration_test.dart => load_dart_debug_images_integration_test.dart} (69%) rename flutter/lib/src/integrations/{load_image_list_integration.dart => load_native_debug_images_integration.dart} (83%) rename flutter/test/integrations/{load_image_list_test.dart => load_native_debug_images_test.dart} (90%) diff --git a/dart/lib/src/load_dart_image_integration.dart b/dart/lib/src/load_dart_debug_images_integration.dart similarity index 84% rename from dart/lib/src/load_dart_image_integration.dart rename to dart/lib/src/load_dart_debug_images_integration.dart index c6dc5e67bf..793724fe42 100644 --- a/dart/lib/src/load_dart_image_integration.dart +++ b/dart/lib/src/load_dart_debug_images_integration.dart @@ -1,7 +1,7 @@ import '../sentry.dart'; import 'debug_image_extractor.dart'; -class LoadDartImageIntegration extends Integration { +class LoadDartDebugImagesIntegration extends Integration { @override void call(Hub hub, SentryOptions options) { options.addEventProcessor( @@ -27,11 +27,7 @@ class _LoadImageIntegrationEventProcessor implements EventProcessor { return event; } - DebugMeta debugMeta = event.debugMeta ?? DebugMeta(); - final images = debugMeta.images; - debugMeta = debugMeta.copyWith(images: [...images, syntheticImage]); - - return event.copyWith(debugMeta: debugMeta); + return event.copyWith(debugMeta: DebugMeta(images: [syntheticImage])); } } diff --git a/dart/lib/src/protocol/sentry_event.dart b/dart/lib/src/protocol/sentry_event.dart index bab5a7ce04..1f4e725c09 100644 --- a/dart/lib/src/protocol/sentry_event.dart +++ b/dart/lib/src/protocol/sentry_event.dart @@ -1,6 +1,6 @@ import 'package:meta/meta.dart'; -import '../load_dart_image_integration.dart'; +import '../load_dart_debug_images_integration.dart'; import '../protocol.dart'; import '../throwable_mechanism.dart'; import '../utils.dart'; @@ -196,7 +196,7 @@ class SentryEvent with SentryEventLike { @internal final Map? unknown; - /// The stacktrace is only used internally for processing such as in [LoadDartImageIntegration]. + /// The stacktrace is only used internally for processing such as in [LoadDartDebugImagesIntegration]. /// TODO: should be a better way than saving a reference to the stacktrace @internal final StackTrace? stackTrace; diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index 0104866e61..0ce339d590 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; import 'dart_exception_type_identifier.dart'; -import 'load_dart_image_integration.dart'; +import 'load_dart_debug_images_integration.dart'; import 'metrics/metrics_api.dart'; import 'run_zoned_guarded_integration.dart'; import 'event_processor/enricher/enricher_event_processor.dart'; @@ -85,7 +85,7 @@ class Sentry { } if (options.enablePureDartSymbolication) { - options.addIntegration(LoadDartImageIntegration()); + options.addIntegration(LoadDartDebugImagesIntegration()); } options.addEventProcessor(EnricherEventProcessor(options)); diff --git a/dart/test/load_dart_image_integration_test.dart b/dart/test/load_dart_debug_images_integration_test.dart similarity index 69% rename from dart/test/load_dart_image_integration_test.dart rename to dart/test/load_dart_debug_images_integration_test.dart index bea3d00ae3..ab3a0d35e7 100644 --- a/dart/test/load_dart_image_integration_test.dart +++ b/dart/test/load_dart_debug_images_integration_test.dart @@ -2,11 +2,11 @@ library dart_test; import 'package:sentry/sentry.dart'; -import 'package:sentry/src/load_dart_image_integration.dart'; +import 'package:sentry/src/load_dart_debug_images_integration.dart'; import 'package:test/test.dart'; void main() { - group(LoadDartImageIntegration(), () { + group(LoadDartDebugImagesIntegration(), () { late Fixture fixture; setUp(() { @@ -64,32 +64,6 @@ isolate_dso_base: 10000000 expect(debugImage?.debugId, isNotEmpty); expect(debugImage?.imageAddr, equals('0x10000000')); }); - - test('Event processor adds debug image to existing debugMeta', () async { - final stackTrace = StackTrace.fromString(''' -*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** -build_id: 'b680cb890f9e3c12a24b172d050dec73' -isolate_dso_base: 10000000 -'''); - final existingDebugImage = DebugImage( - type: 'macho', - debugId: 'existing-debug-id', - imageAddr: '0x2000', - ); - SentryEvent event = _getEvent(); - event = event.copyWith( - stackTrace: stackTrace, - debugMeta: DebugMeta(images: [existingDebugImage]), - ); - - final processor = fixture.options.eventProcessors.first; - final resultEvent = await processor.apply(event, Hint()); - - expect(resultEvent?.debugMeta?.images.length, 2); - expect(resultEvent?.debugMeta?.images, contains(existingDebugImage)); - expect( - resultEvent?.debugMeta?.images.last.imageAddr, equals('0x10000000')); - }); }); } @@ -97,7 +71,7 @@ class Fixture { final options = SentryOptions(dsn: 'https://public@sentry.example.com/1'); Fixture() { - final integration = LoadDartImageIntegration(); + final integration = LoadDartDebugImagesIntegration(); integration.call(Hub(options), options); } } diff --git a/flutter/lib/src/integrations/integrations.dart b/flutter/lib/src/integrations/integrations.dart index ac16732f58..df4fe0a025 100644 --- a/flutter/lib/src/integrations/integrations.dart +++ b/flutter/lib/src/integrations/integrations.dart @@ -1,7 +1,7 @@ export 'debug_print_integration.dart'; export 'flutter_error_integration.dart'; export 'load_contexts_integration.dart'; -export 'load_image_list_integration.dart'; +export 'load_native_debug_images_integration.dart'; export 'load_release_integration.dart'; export 'native_app_start_integration.dart'; export 'native_sdk_integration.dart'; diff --git a/flutter/lib/src/integrations/load_image_list_integration.dart b/flutter/lib/src/integrations/load_native_debug_images_integration.dart similarity index 83% rename from flutter/lib/src/integrations/load_image_list_integration.dart rename to flutter/lib/src/integrations/load_native_debug_images_integration.dart index 51d7ca8b75..daeeede597 100644 --- a/flutter/lib/src/integrations/load_image_list_integration.dart +++ b/flutter/lib/src/integrations/load_native_debug_images_integration.dart @@ -5,14 +5,15 @@ import '../native/sentry_native_binding.dart'; import '../sentry_flutter_options.dart'; // ignore: implementation_imports -import 'package:sentry/src/load_dart_image_integration.dart' +import 'package:sentry/src/load_dart_debug_images_integration.dart' show NeedsSymbolication; /// Loads the native debug image list for stack trace symbolication. -class LoadImageListIntegration extends Integration { +class LoadNativeDebugImagesIntegration + extends Integration { final SentryNativeBinding _native; - LoadImageListIntegration(this._native); + LoadNativeDebugImagesIntegration(this._native); @override void call(Hub hub, SentryFlutterOptions options) { diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index f37d7277be..ef15816952 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -180,7 +180,7 @@ mixin SentryFlutter { if (native != null) { integrations.add(NativeSdkIntegration(native)); integrations.add(LoadContextsIntegration(native)); - integrations.add(LoadImageListIntegration(native)); + integrations.add(LoadNativeDebugImagesIntegration(native)); options.enablePureDartSymbolication = false; } diff --git a/flutter/test/integrations/load_image_list_test.dart b/flutter/test/integrations/load_native_debug_images_test.dart similarity index 90% rename from flutter/test/integrations/load_image_list_test.dart rename to flutter/test/integrations/load_native_debug_images_test.dart index 35e59b7599..743a1c4fc3 100644 --- a/flutter/test/integrations/load_image_list_test.dart +++ b/flutter/test/integrations/load_native_debug_images_test.dart @@ -4,12 +4,12 @@ library flutter_test; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:sentry_flutter/src/integrations/load_image_list_integration.dart'; +import 'package:sentry_flutter/src/integrations/load_native_debug_images_integration.dart'; import 'fixture.dart'; void main() { - group(LoadImageListIntegration, () { + group(LoadNativeDebugImagesIntegration, () { final imageList = [ DebugImage.fromJson({ 'code_file': '/apex/com.android.art/javalib/arm64/boot.oat', @@ -22,16 +22,17 @@ void main() { }) ]; - late IntegrationTestFixture fixture; + late IntegrationTestFixture fixture; setUp(() async { - fixture = IntegrationTestFixture(LoadImageListIntegration.new); + fixture = IntegrationTestFixture(LoadNativeDebugImagesIntegration.new); when(fixture.binding.loadDebugImages()) .thenAnswer((_) async => imageList); await fixture.registerIntegration(); }); - test('$LoadImageListIntegration adds itself to sdk.integrations', () async { + test('$LoadNativeDebugImagesIntegration adds itself to sdk.integrations', + () async { expect( fixture.options.sdk.integrations.contains('loadImageListIntegration'), true, diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index 760d20b3d4..b35c6b6311 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -38,13 +38,13 @@ final nonWebIntegrations = [ // These should be added to Android final androidIntegrations = [ - LoadImageListIntegration, + LoadNativeDebugImagesIntegration, LoadContextsIntegration, ]; // These should be added to iOS and macOS final iOsAndMacOsIntegrations = [ - LoadImageListIntegration, + LoadNativeDebugImagesIntegration, LoadContextsIntegration, ]; From f7d358e9f9e298cc6ce2856af6c5ab83cda2fe69 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 14:21:38 +0200 Subject: [PATCH 23/40] update naming and comments of flag --- dart/lib/src/sentry.dart | 2 +- dart/lib/src/sentry_options.dart | 14 +++++++++----- dart/test/sentry_options_test.dart | 2 +- flutter/lib/src/sentry_flutter.dart | 2 +- flutter/test/sentry_flutter_test.dart | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index 0ce339d590..29217abe40 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -84,7 +84,7 @@ class Sentry { options.addIntegrationByIndex(0, IsolateErrorIntegration()); } - if (options.enablePureDartSymbolication) { + if (options.enableDartSymbolication) { options.addIntegration(LoadDartDebugImagesIntegration()); } diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 13b36f79ad..4818642947 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -358,11 +358,15 @@ class SentryOptions { _ignoredExceptionsForType.contains(exception.runtimeType); } - /// If enabled, the SDK will attempt to symbolicate pure Dart stack traces when - /// used in Flutter. This feature works on Flutter iOS, macOS and Android. - /// Due to automatic debug image loading from the native SDKs - /// this flag is automatically set to false in `SentryFlutter.init`. - bool enablePureDartSymbolication = true; + /// Enables Dart symbolication for stack traces in Flutter. + /// + /// If true, the SDK will attempt to symbolicate Dart stack traces when + /// [Sentry.init] is used instead of `SentryFlutter.init`. This is useful + /// when native debug images are not available. + /// + /// Automatically set to `false` when using `SentryFlutter.init`, as it uses + /// native SDKs for setting up symbolication on iOS, macOS, and Android. + bool enableDartSymbolication = true; @internal late ClientReportRecorder recorder = NoOpClientReportRecorder(); diff --git a/dart/test/sentry_options_test.dart b/dart/test/sentry_options_test.dart index ac0d633694..bb2db9db24 100644 --- a/dart/test/sentry_options_test.dart +++ b/dart/test/sentry_options_test.dart @@ -196,6 +196,6 @@ void main() { test('enablePureDartSymbolication is enabled by default', () { final options = SentryOptions(dsn: fakeDsn); - expect(options.enablePureDartSymbolication, true); + expect(options.enableDartSymbolication, true); }); } diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index ef15816952..5705473734 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -181,7 +181,7 @@ mixin SentryFlutter { integrations.add(NativeSdkIntegration(native)); integrations.add(LoadContextsIntegration(native)); integrations.add(LoadNativeDebugImagesIntegration(native)); - options.enablePureDartSymbolication = false; + options.enableDartSymbolication = false; } final renderer = options.rendererWrapper.getRenderer(); diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index b35c6b6311..cdfdd3a16b 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -636,7 +636,7 @@ void main() { options.dsn = fakeDsn; options.automatedTestMode = true; - expect(options.enablePureDartSymbolication, false); + expect(options.enableDartSymbolication, false); }, appRunner: appRunner, platformChecker: getPlatformChecker( From db3bf92ad73c2a2336dbf1c28a6c39d252298994 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 14:47:47 +0200 Subject: [PATCH 24/40] set stacktrace in hint --- dart/lib/src/debug_image_extractor.dart | 21 ++++---- .../load_dart_debug_images_integration.dart | 9 +++- dart/lib/src/protocol/sentry_event.dart | 9 ---- dart/lib/src/protocol/sentry_transaction.dart | 3 -- dart/lib/src/sentry_client.dart | 3 +- dart/test/debug_image_extractor_test.dart | 18 +++---- ...ad_dart_debug_images_integration_test.dart | 8 +-- ...native_debug_images_integration_test.dart} | 0 flutter/test/mocks.mocks.dart | 52 +++---------------- 9 files changed, 39 insertions(+), 84 deletions(-) rename flutter/test/integrations/{load_native_debug_images_test.dart => load_native_debug_images_integration_test.dart} (100%) diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_image_extractor.dart index ca2c2ae60b..4981b0f596 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -17,15 +17,15 @@ class DebugImageExtractor { final SentryOptions _options; - DebugImage? extractDebugImageFrom(StackTrace stackTrace) { - return _extractDebugInfoFrom(stackTrace).toDebugImage(); + DebugImage? extractDebugImageFrom(String stackTraceString) { + return _extractDebugInfoFrom(stackTraceString).toDebugImage(); } - _DebugInfo _extractDebugInfoFrom(StackTrace stackTrace) { + _DebugInfo _extractDebugInfoFrom(String stackTraceString) { String? buildId; String? isolateDsoBase; - final lines = stackTrace.toString().split('\n'); + final lines = stackTraceString.split('\n'); for (final line in lines) { if (_isHeaderStartLine(line)) { @@ -90,12 +90,12 @@ class _DebugInfo { // Debug identifier is the little-endian UUID representation of the first 16-bytes of // the build ID on Android - String _convertCodeIdToDebugId(String codeId) { + String? _convertCodeIdToDebugId(String codeId) { codeId = codeId.replaceAll(' ', ''); if (codeId.length < 32) { - // todo: don't throw - throw ArgumentError( + _options.logger(SentryLevel.warning, 'Code ID must be at least 32 hexadecimal characters long'); + return null; } final first16Bytes = codeId.substring(0, 32); @@ -122,10 +122,11 @@ class _DebugInfo { ].join('-'); } - String _hexToUuid(String hex) { + String? _hexToUuid(String hex) { if (hex.length != 32) { - // todo: don't throw - throw FormatException('Input must be a 32-character hexadecimal string'); + _options.logger(SentryLevel.warning, + 'Hex input must be a 32-character hexadecimal string'); + return null; } return '${hex.substring(0, 8)}-' diff --git a/dart/lib/src/load_dart_debug_images_integration.dart b/dart/lib/src/load_dart_debug_images_integration.dart index 793724fe42..fcb0261380 100644 --- a/dart/lib/src/load_dart_debug_images_integration.dart +++ b/dart/lib/src/load_dart_debug_images_integration.dart @@ -10,6 +10,8 @@ class LoadDartDebugImagesIntegration extends Integration { } } +const hintRawStackTraceKey = 'raw_stacktrace'; + class _LoadImageIntegrationEventProcessor implements EventProcessor { _LoadImageIntegrationEventProcessor(this._debugImageExtractor); @@ -17,12 +19,15 @@ class _LoadImageIntegrationEventProcessor implements EventProcessor { @override Future apply(SentryEvent event, Hint hint) async { - if (!event.needsSymbolication() || event.stackTrace == null) { + final stackTrace = hint.get(hintRawStackTraceKey) as String?; + if (!event.needsSymbolication() || stackTrace == null) { return event; } final syntheticImage = - _debugImageExtractor.extractDebugImageFrom(event.stackTrace!); + _debugImageExtractor.extractDebugImageFrom(stackTrace); + + print(syntheticImage); if (syntheticImage == null) { return event; } diff --git a/dart/lib/src/protocol/sentry_event.dart b/dart/lib/src/protocol/sentry_event.dart index 1f4e725c09..1b2765c426 100644 --- a/dart/lib/src/protocol/sentry_event.dart +++ b/dart/lib/src/protocol/sentry_event.dart @@ -1,6 +1,5 @@ import 'package:meta/meta.dart'; -import '../load_dart_debug_images_integration.dart'; import '../protocol.dart'; import '../throwable_mechanism.dart'; import '../utils.dart'; @@ -40,7 +39,6 @@ class SentryEvent with SentryEventLike { this.debugMeta, this.type, this.unknown, - this.stackTrace, }) : eventId = eventId ?? SentryId.newId(), timestamp = timestamp ?? getUtcDateTime(), contexts = contexts ?? Contexts(), @@ -196,11 +194,6 @@ class SentryEvent with SentryEventLike { @internal final Map? unknown; - /// The stacktrace is only used internally for processing such as in [LoadDartDebugImagesIntegration]. - /// TODO: should be a better way than saving a reference to the stacktrace - @internal - final StackTrace? stackTrace; - @override SentryEvent copyWith({ SentryId? eventId, @@ -231,7 +224,6 @@ class SentryEvent with SentryEventLike { List? exceptions, List? threads, String? type, - StackTrace? stackTrace, }) => SentryEvent( eventId: eventId ?? this.eventId, @@ -265,7 +257,6 @@ class SentryEvent with SentryEventLike { threads: (threads != null ? List.from(threads) : null) ?? this.threads, type: type ?? this.type, unknown: unknown, - stackTrace: stackTrace ?? this.stackTrace, ); /// Deserializes a [SentryEvent] from JSON [Map]. diff --git a/dart/lib/src/protocol/sentry_transaction.dart b/dart/lib/src/protocol/sentry_transaction.dart index 678cea4f7a..eea319aa41 100644 --- a/dart/lib/src/protocol/sentry_transaction.dart +++ b/dart/lib/src/protocol/sentry_transaction.dart @@ -40,7 +40,6 @@ class SentryTransaction extends SentryEvent { Map? measurements, Map>? metricSummaries, SentryTransactionInfo? transactionInfo, - StackTrace? stackTrace, }) : super( timestamp: timestamp ?? tracer.endTimestamp, transaction: transaction ?? tracer.name, @@ -140,7 +139,6 @@ class SentryTransaction extends SentryEvent { Map? measurements, Map>? metricSummaries, SentryTransactionInfo? transactionInfo, - StackTrace? stackTrace, }) => SentryTransaction( tracer, @@ -169,6 +167,5 @@ class SentryTransaction extends SentryEvent { (metricSummaries != null ? Map.from(metricSummaries) : null) ?? this.metricSummaries, transactionInfo: transactionInfo ?? this.transactionInfo, - stackTrace: stackTrace ?? this.stackTrace, ); } diff --git a/dart/lib/src/sentry_client.dart b/dart/lib/src/sentry_client.dart index 8f0ecab164..23eeed6ba0 100644 --- a/dart/lib/src/sentry_client.dart +++ b/dart/lib/src/sentry_client.dart @@ -7,6 +7,7 @@ import 'client_reports/client_report_recorder.dart'; import 'client_reports/discard_reason.dart'; import 'event_processor.dart'; import 'hint.dart'; +import 'load_dart_debug_images_integration.dart'; import 'metrics/metric.dart'; import 'metrics/metrics_aggregator.dart'; import 'protocol.dart'; @@ -117,6 +118,7 @@ class SentryClient { SentryEvent? preparedEvent = _prepareEvent(event, stackTrace: stackTrace); hint ??= Hint(); + hint.set(hintRawStackTraceKey, stackTrace.toString()); if (scope != null) { preparedEvent = await scope.applyToEvent(preparedEvent, hint); @@ -207,7 +209,6 @@ class SentryClient { release: event.release ?? _options.release, sdk: event.sdk ?? _options.sdk, platform: event.platform ?? sdkPlatform(_options.platformChecker.isWeb), - stackTrace: StackTrace.fromString(stackTrace.toString()), ); if (event is SentryTransaction) { diff --git a/dart/test/debug_image_extractor_test.dart b/dart/test/debug_image_extractor_test.dart index 98478ad3d0..b6edc73374 100644 --- a/dart/test/debug_image_extractor_test.dart +++ b/dart/test/debug_image_extractor_test.dart @@ -14,11 +14,11 @@ void main() { }); test('extracts debug image from valid stack trace', () { - final stackTrace = StackTrace.fromString(''' + final stackTrace = ''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** build_id: 'b680cb890f9e3c12a24b172d050dec73' isolate_dso_base: 10000000 -'''); +'''; final extractor = fixture.getSut(platform: MockPlatform.android()); final debugImage = extractor.extractDebugImageFrom(stackTrace); @@ -28,7 +28,7 @@ isolate_dso_base: 10000000 }); test('returns null for invalid stack trace', () { - final stackTrace = StackTrace.fromString('Invalid stack trace'); + final stackTrace = 'Invalid stack trace'; final extractor = fixture.getSut(platform: MockPlatform.android()); final debugImage = extractor.extractDebugImageFrom(stackTrace); @@ -36,11 +36,11 @@ isolate_dso_base: 10000000 }); test('extracts correct debug ID for Android', () { - final stackTrace = StackTrace.fromString(''' + final stackTrace = ''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** build_id: 'b680cb890f9e3c12a24b172d050dec73' isolate_dso_base: 20000000 -'''); +'''; final extractor = fixture.getSut(platform: MockPlatform.android()); final debugImage = extractor.extractDebugImageFrom(stackTrace); @@ -49,11 +49,11 @@ isolate_dso_base: 20000000 }); test('extracts correct debug ID for iOS', () { - final stackTrace = StackTrace.fromString(''' + final stackTrace = ''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** build_id: 'b680cb890f9e3c12a24b172d050dec73' isolate_dso_base: 30000000 -'''); +'''; final extractor = fixture.getSut(platform: MockPlatform.iOS()); final debugImage = extractor.extractDebugImageFrom(stackTrace); @@ -63,11 +63,11 @@ isolate_dso_base: 30000000 }); test('sets correct type based on platform', () { - final stackTrace = StackTrace.fromString(''' + final stackTrace = ''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** build_id: 'b680cb890f9e3c12a24b172d050dec73' isolate_dso_base: 40000000 -'''); +'''; final androidExtractor = fixture.getSut(platform: MockPlatform.android()); final iosExtractor = fixture.getSut(platform: MockPlatform.iOS()); diff --git a/dart/test/load_dart_debug_images_integration_test.dart b/dart/test/load_dart_debug_images_integration_test.dart index ab3a0d35e7..947c57fdf1 100644 --- a/dart/test/load_dart_debug_images_integration_test.dart +++ b/dart/test/load_dart_debug_images_integration_test.dart @@ -48,16 +48,16 @@ void main() { test('Event processor adds debug image when symbolication is needed', () async { - final stackTrace = StackTrace.fromString(''' + final stackTrace = ''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** build_id: 'b680cb890f9e3c12a24b172d050dec73' isolate_dso_base: 10000000 -'''); +'''; SentryEvent event = _getEvent(); - event = event.copyWith(stackTrace: stackTrace); final processor = fixture.options.eventProcessors.first; - final resultEvent = await processor.apply(event, Hint()); + final resultEvent = await processor.apply( + event, Hint()..set(hintRawStackTraceKey, stackTrace)); expect(resultEvent?.debugMeta?.images.length, 1); final debugImage = resultEvent?.debugMeta?.images.first; diff --git a/flutter/test/integrations/load_native_debug_images_test.dart b/flutter/test/integrations/load_native_debug_images_integration_test.dart similarity index 100% rename from flutter/test/integrations/load_native_debug_images_test.dart rename to flutter/test/integrations/load_native_debug_images_integration_test.dart diff --git a/flutter/test/mocks.mocks.dart b/flutter/test/mocks.mocks.dart index 9e6e4a64d1..01d2127efe 100644 --- a/flutter/test/mocks.mocks.dart +++ b/flutter/test/mocks.mocks.dart @@ -263,12 +263,6 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { returnValueForMissingStub: null, ); - @override - Map get measurements => (super.noSuchMethod( - Invocation.getter(#measurements), - returnValue: {}, - ) as Map); - @override _i2.SentrySpanContext get context => (super.noSuchMethod( Invocation.getter(#context), @@ -338,6 +332,12 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { returnValue: {}, ) as Map); + @override + Map get measurements => (super.noSuchMethod( + Invocation.getter(#measurements), + returnValue: {}, + ) as Map); + @override _i8.Future finish({ _i3.SpanStatus? status, @@ -502,24 +502,6 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { returnValueForMissingStub: null, ); - @override - void setMeasurementFromChild( - String? name, - num? value, { - _i2.SentryMeasurementUnit? unit, - }) => - super.noSuchMethod( - Invocation.method( - #setMeasurementFromChild, - [ - name, - value, - ], - {#unit: unit}, - ), - returnValueForMissingStub: null, - ); - @override void scheduleFinish() => super.noSuchMethod( Invocation.method( @@ -687,7 +669,6 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { Map? measurements, Map>? metricSummaries, _i3.SentryTransactionInfo? transactionInfo, - StackTrace? stackTrace, }) => (super.noSuchMethod( Invocation.method( @@ -723,7 +704,6 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { #measurements: measurements, #metricSummaries: metricSummaries, #transactionInfo: transactionInfo, - #stackTrace: stackTrace, }, ), returnValue: _FakeSentryTransaction_7( @@ -761,20 +741,10 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { #measurements: measurements, #metricSummaries: metricSummaries, #transactionInfo: transactionInfo, - #stackTrace: stackTrace, }, ), ), ) as _i3.SentryTransaction); - - @override - bool needsSymbolication() => (super.noSuchMethod( - Invocation.method( - #needsSymbolication, - [], - ), - returnValue: false, - ) as bool); } /// A class which mocks [SentrySpan]. @@ -1376,16 +1346,6 @@ class MockSentryNativeBinding extends _i1.Mock returnValue: _i8.Future.value(), returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); - - @override - _i8.Future nativeCrash() => (super.noSuchMethod( - Invocation.method( - #nativeCrash, - [], - ), - returnValue: _i8.Future.value(), - returnValueForMissingStub: _i8.Future.value(), - ) as _i8.Future); } /// A class which mocks [Hub]. From 07f0b6b7207f2887901ddb0a83b7f777ed5ad45e Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 14:54:35 +0200 Subject: [PATCH 25/40] update --- .../load_dart_debug_images_integration.dart | 33 +++++++++++++------ ...ad_dart_debug_images_integration_test.dart | 16 +++++++-- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/dart/lib/src/load_dart_debug_images_integration.dart b/dart/lib/src/load_dart_debug_images_integration.dart index fcb0261380..b4fbc79bd3 100644 --- a/dart/lib/src/load_dart_debug_images_integration.dart +++ b/dart/lib/src/load_dart_debug_images_integration.dart @@ -4,8 +4,8 @@ import 'debug_image_extractor.dart'; class LoadDartDebugImagesIntegration extends Integration { @override void call(Hub hub, SentryOptions options) { - options.addEventProcessor( - _LoadImageIntegrationEventProcessor(DebugImageExtractor(options))); + options.addEventProcessor(_LoadImageIntegrationEventProcessor( + DebugImageExtractor(options), options)); options.sdk.addIntegration('loadDartImageIntegration'); } } @@ -13,26 +13,39 @@ class LoadDartDebugImagesIntegration extends Integration { const hintRawStackTraceKey = 'raw_stacktrace'; class _LoadImageIntegrationEventProcessor implements EventProcessor { - _LoadImageIntegrationEventProcessor(this._debugImageExtractor); + _LoadImageIntegrationEventProcessor(this._debugImageExtractor, this._options); + + final SentryOptions _options; final DebugImageExtractor _debugImageExtractor; @override Future apply(SentryEvent event, Hint hint) async { final stackTrace = hint.get(hintRawStackTraceKey) as String?; - if (!event.needsSymbolication() || stackTrace == null) { + if (!_options.enableDartSymbolication || + !event.needsSymbolication() || + stackTrace == null) { return event; } - final syntheticImage = - _debugImageExtractor.extractDebugImageFrom(stackTrace); + try { + final syntheticImage = + _debugImageExtractor.extractDebugImageFrom(stackTrace); + if (syntheticImage == null) { + return event; + } - print(syntheticImage); - if (syntheticImage == null) { + return event.copyWith(debugMeta: DebugMeta(images: [syntheticImage])); + } catch (e, stackTrace) { + _options.logger( + SentryLevel.info, + "Couldn't add Dart debug image to event. " + 'The event will still be reported.', + exception: e, + stackTrace: stackTrace, + ); return event; } - - return event.copyWith(debugMeta: DebugMeta(images: [syntheticImage])); } } diff --git a/dart/test/load_dart_debug_images_integration_test.dart b/dart/test/load_dart_debug_images_integration_test.dart index 947c57fdf1..870c692747 100644 --- a/dart/test/load_dart_debug_images_integration_test.dart +++ b/dart/test/load_dart_debug_images_integration_test.dart @@ -28,7 +28,8 @@ void main() { ); }); - test('Event processor does not modify event if symbolication is not needed', + test( + 'Event processor does not add debug image if symbolication is not needed', () async { final event = _getEvent(needsSymbolication: false); final processor = fixture.options.eventProcessors.first; @@ -37,7 +38,7 @@ void main() { expect(resultEvent, equals(event)); }); - test('Event processor does not modify event if stackTrace is null', + test('Event processor does not add debug image if stackTrace is null', () async { final event = _getEvent(); final processor = fixture.options.eventProcessors.first; @@ -46,6 +47,17 @@ void main() { expect(resultEvent, equals(event)); }); + test( + 'Event processor does not add debug image if enableDartSymbolication is false', + () async { + fixture.options.enableDartSymbolication = false; + final event = _getEvent(); + final processor = fixture.options.eventProcessors.first; + final resultEvent = await processor.apply(event, Hint()); + + expect(resultEvent, equals(event)); + }); + test('Event processor adds debug image when symbolication is needed', () async { final stackTrace = ''' From 775279d48b49a6374479aa7de5da39377fce52fc Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 14:57:14 +0200 Subject: [PATCH 26/40] add changelog --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c78f149f0..a304504261 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## Unreleased + +### Features + + - Add `enableDartSymbolication` option to Sentry.init() for **Flutter iOS, macOS and Android** ([#2256](https://github.com/getsentry/sentry-dart/pull/2256)) + - This flag enables symbolication of Dart stack traces when native debug images are not available. + - Useful when using Sentry.init() instead of SentryFlutter.init() in Flutter projects for example due to size limitations. + - `true` by default but automatically set to `false` when using SentryFlutter.init() because the SentryFlutter fetches debug images from the native SDK integrations. + ## 8.8.0 ### Features From 88cfa1fb10d37b92216ed784f40e2c504c42ab3a Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 16:38:05 +0200 Subject: [PATCH 27/40] update --- dart/lib/src/debug_image_extractor.dart | 90 ++++++++++++------- .../load_dart_debug_images_integration.dart | 1 - dart/test/debug_image_extractor_test.dart | 42 +++++---- dart/test/mocks/mock_platform.dart | 4 + 4 files changed, 89 insertions(+), 48 deletions(-) diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_image_extractor.dart index 4981b0f596..b8433a7385 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -1,5 +1,7 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; +import 'package:uuid/parsing.dart'; +import 'package:uuid/uuid.dart'; import '../sentry.dart'; @@ -10,7 +12,8 @@ final RegExp _buildIdRegex = RegExp(r"build_id(?:=|: )'([\da-f]+)'"); final RegExp _isolateDsoBaseLineRegex = RegExp(r'isolate_dso_base(?:=|: )([\da-f]+)'); -/// Processes a stack trace by extracting debug information from its header. +/// Extracts debug information from stack trace header. +/// Needed for symbolication of Dart stack traces without native debug images. @internal class DebugImageExtractor { DebugImageExtractor(this._options); @@ -73,23 +76,41 @@ class _DebugInfo { return null; } - final type = _options.platformChecker.platform.isAndroid ? 'elf' : 'macho'; - final debugId = _options.platformChecker.platform.isAndroid - ? _convertCodeIdToDebugId(buildId!) - : _hexToUuid(buildId!); - final codeId = - _options.platformChecker.platform.isAndroid ? buildId! : null; - + String type; + String? imageAddr; + String? debugId; + String? codeId; + + final platform = _options.platformChecker.platform; + + // Default values for all platforms + imageAddr = '0x$isolateDsoBase'; + + if (platform.isAndroid) { + type = 'elf'; + debugId = _convertCodeIdToDebugId(buildId!); + codeId = buildId; + } else if (platform.isIOS || platform.isMacOS) { + type = 'macho'; + debugId = _formatHexToUuid(buildId!); + // `codeId` is not needed for iOS/MacOS. + } else { + _options.logger( + SentryLevel.warning, + 'Unsupported platform for creating Dart debug images.', + ); + return null; + } return DebugImage( type: type, - imageAddr: '0x$isolateDsoBase', + imageAddr: imageAddr, debugId: debugId, codeId: codeId, ); } // Debug identifier is the little-endian UUID representation of the first 16-bytes of - // the build ID on Android + // the build ID on ELF images. String? _convertCodeIdToDebugId(String codeId) { codeId = codeId.replaceAll(' ', ''); if (codeId.length < 32) { @@ -99,30 +120,35 @@ class _DebugInfo { } final first16Bytes = codeId.substring(0, 32); - final byteData = Uint8List.fromList(List.generate(16, - (i) => int.parse(first16Bytes.substring(i * 2, i * 2 + 2), radix: 16))); - - final buffer = byteData.buffer.asByteData(); - final timeLow = buffer.getUint32(0, Endian.little); - final timeMid = buffer.getUint16(4, Endian.little); - final timeHiAndVersion = buffer.getUint16(6, Endian.little); - final clockSeqHiAndReserved = buffer.getUint8(8); - final clockSeqLow = buffer.getUint8(9); - - return [ - timeLow.toRadixString(16).padLeft(8, '0'), - timeMid.toRadixString(16).padLeft(4, '0'), - timeHiAndVersion.toRadixString(16).padLeft(4, '0'), - clockSeqHiAndReserved.toRadixString(16).padLeft(2, '0') + - clockSeqLow.toRadixString(16).padLeft(2, '0'), - byteData - .sublist(10) - .map((b) => b.toRadixString(16).padLeft(2, '0')) - .join() - ].join('-'); + final byteData = UuidParsing.parseHexToBytes(first16Bytes); + + if (byteData.isEmpty) { + _options.logger( + SentryLevel.warning, 'Failed to convert code ID to debug ID'); + return null; + } + + return bigToLittleEndianUuid(UuidValue.fromByteList(byteData).uuid); + } + + String bigToLittleEndianUuid(String bigEndianUuid) { + // Remove hyphens and convert to a byte array + final byteArray = + Uuid.parse(bigEndianUuid, validationMode: ValidationMode.nonStrict); + + // Reverse the necessary sections according to the UUID fields + final reversedByteArray = Uint8List.fromList([ + ...byteArray.sublist(0, 4).reversed, + ...byteArray.sublist(4, 6).reversed, + ...byteArray.sublist(6, 8).reversed, + ...byteArray.sublist(8, 10), + ...byteArray.sublist(10), + ]); + + return Uuid.unparse(reversedByteArray); } - String? _hexToUuid(String hex) { + String? _formatHexToUuid(String hex) { if (hex.length != 32) { _options.logger(SentryLevel.warning, 'Hex input must be a 32-character hexadecimal string'); diff --git a/dart/lib/src/load_dart_debug_images_integration.dart b/dart/lib/src/load_dart_debug_images_integration.dart index b4fbc79bd3..29e3f0eea8 100644 --- a/dart/lib/src/load_dart_debug_images_integration.dart +++ b/dart/lib/src/load_dart_debug_images_integration.dart @@ -16,7 +16,6 @@ class _LoadImageIntegrationEventProcessor implements EventProcessor { _LoadImageIntegrationEventProcessor(this._debugImageExtractor, this._options); final SentryOptions _options; - final DebugImageExtractor _debugImageExtractor; @override diff --git a/dart/test/debug_image_extractor_test.dart b/dart/test/debug_image_extractor_test.dart index b6edc73374..bd748d26a5 100644 --- a/dart/test/debug_image_extractor_test.dart +++ b/dart/test/debug_image_extractor_test.dart @@ -13,20 +13,6 @@ void main() { fixture = Fixture(); }); - test('extracts debug image from valid stack trace', () { - final stackTrace = ''' -*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** -build_id: 'b680cb890f9e3c12a24b172d050dec73' -isolate_dso_base: 10000000 -'''; - final extractor = fixture.getSut(platform: MockPlatform.android()); - final debugImage = extractor.extractDebugImageFrom(stackTrace); - - expect(debugImage, isNotNull); - expect(debugImage?.debugId, isNotEmpty); - expect(debugImage?.imageAddr, equals('0x10000000')); - }); - test('returns null for invalid stack trace', () { final stackTrace = 'Invalid stack trace'; final extractor = fixture.getSut(platform: MockPlatform.android()); @@ -35,7 +21,7 @@ isolate_dso_base: 10000000 expect(debugImage, isNull); }); - test('extracts correct debug ID for Android', () { + test('extracts correct debug ID for Android with short debugId', () { final stackTrace = ''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** build_id: 'b680cb890f9e3c12a24b172d050dec73' @@ -48,6 +34,19 @@ isolate_dso_base: 20000000 debugImage?.debugId, equals('89cb80b6-9e0f-123c-a24b-172d050dec73')); }); + test('extracts correct debug ID for Android with long debugId', () { + final stackTrace = ''' +*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** +build_id: 'f1c3bcc0279865fe3058404b2831d9e64135386c' +isolate_dso_base: 30000000 +'''; + final extractor = fixture.getSut(platform: MockPlatform.android()); + final debugImage = extractor.extractDebugImageFrom(stackTrace); + + expect( + debugImage?.debugId, equals('c0bcc3f1-9827-fe65-3058-404b2831d9e6')); + }); + test('extracts correct debug ID for iOS', () { final stackTrace = ''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** @@ -78,6 +77,19 @@ isolate_dso_base: 40000000 expect(androidDebugImage?.type, equals('elf')); expect(iosDebugImage?.type, equals('macho')); }); + + test('debug image is null on unsupported platforms', () { + final stackTrace = ''' +*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** +build_id: 'b680cb890f9e3c12a24b172d050dec73' +isolate_dso_base: 40000000 +'''; + final extractor = fixture.getSut(platform: MockPlatform.linux()); + + final debugImage = extractor.extractDebugImageFrom(stackTrace); + + expect(debugImage, isNull); + }); }); } diff --git a/dart/test/mocks/mock_platform.dart b/dart/test/mocks/mock_platform.dart index a045f794af..d8cc6ee77e 100644 --- a/dart/test/mocks/mock_platform.dart +++ b/dart/test/mocks/mock_platform.dart @@ -13,6 +13,10 @@ class MockPlatform extends Platform with NoSuchMethodProvider { return MockPlatform(os: 'ios'); } + factory MockPlatform.linux() { + return MockPlatform(os: 'linux'); + } + @override String operatingSystem; } From e10f9b77bc083e444cca335f6f546844a531125b Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 16:47:15 +0200 Subject: [PATCH 28/40] fix test --- dart/lib/src/debug_image_extractor.dart | 18 ++++++++++++++++-- ...oad_dart_debug_images_integration_test.dart | 1 - 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_image_extractor.dart index b8433a7385..e9bedc5a7e 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -1,6 +1,5 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; -import 'package:uuid/parsing.dart'; import 'package:uuid/uuid.dart'; import '../sentry.dart'; @@ -120,7 +119,7 @@ class _DebugInfo { } final first16Bytes = codeId.substring(0, 32); - final byteData = UuidParsing.parseHexToBytes(first16Bytes); + final byteData = _parseHexToBytes(first16Bytes); if (byteData.isEmpty) { _options.logger( @@ -131,6 +130,21 @@ class _DebugInfo { return bigToLittleEndianUuid(UuidValue.fromByteList(byteData).uuid); } + Uint8List _parseHexToBytes(String hex) { + if (hex.length % 2 != 0) { + throw ArgumentError('Invalid hex string'); + } + if (hex.startsWith('0x')) { + hex = hex.substring(2); + } + + var bytes = Uint8List(hex.length ~/ 2); + for (var i = 0; i < hex.length; i += 2) { + bytes[i ~/ 2] = int.parse(hex.substring(i, i + 2), radix: 16); + } + return bytes; + } + String bigToLittleEndianUuid(String bigEndianUuid) { // Remove hyphens and convert to a byte array final byteArray = diff --git a/dart/test/load_dart_debug_images_integration_test.dart b/dart/test/load_dart_debug_images_integration_test.dart index 870c692747..f185d3e2f0 100644 --- a/dart/test/load_dart_debug_images_integration_test.dart +++ b/dart/test/load_dart_debug_images_integration_test.dart @@ -66,7 +66,6 @@ build_id: 'b680cb890f9e3c12a24b172d050dec73' isolate_dso_base: 10000000 '''; SentryEvent event = _getEvent(); - final processor = fixture.options.eventProcessors.first; final resultEvent = await processor.apply( event, Hint()..set(hintRawStackTraceKey, stackTrace)); From 72c28444e0c2b8ba56242090c1a2887c2af7b69b Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 29 Aug 2024 16:58:29 +0200 Subject: [PATCH 29/40] fix test --- ...ad_dart_debug_images_integration_test.dart | 137 ++++++++++-------- dart/test/mocks/mock_platform.dart | 4 + 2 files changed, 79 insertions(+), 62 deletions(-) diff --git a/dart/test/load_dart_debug_images_integration_test.dart b/dart/test/load_dart_debug_images_integration_test.dart index f185d3e2f0..74dca46d2a 100644 --- a/dart/test/load_dart_debug_images_integration_test.dart +++ b/dart/test/load_dart_debug_images_integration_test.dart @@ -5,76 +5,89 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/load_dart_debug_images_integration.dart'; import 'package:test/test.dart'; +import 'mocks/mock_platform.dart'; +import 'mocks/mock_platform_checker.dart'; + void main() { group(LoadDartDebugImagesIntegration(), () { late Fixture fixture; - setUp(() { - fixture = Fixture(); - }); - - test('adds itself to sdk.integrations', () { - expect( - fixture.options.sdk.integrations.contains('loadDartImageIntegration'), - true, - ); - }); - - test('Event processor is added to options', () { - expect(fixture.options.eventProcessors.length, 1); - expect( - fixture.options.eventProcessors.first.runtimeType.toString(), - '_LoadImageIntegrationEventProcessor', - ); - }); - - test( - 'Event processor does not add debug image if symbolication is not needed', - () async { - final event = _getEvent(needsSymbolication: false); - final processor = fixture.options.eventProcessors.first; - final resultEvent = await processor.apply(event, Hint()); - - expect(resultEvent, equals(event)); - }); - - test('Event processor does not add debug image if stackTrace is null', - () async { - final event = _getEvent(); - final processor = fixture.options.eventProcessors.first; - final resultEvent = await processor.apply(event, Hint()); - - expect(resultEvent, equals(event)); - }); - - test( - 'Event processor does not add debug image if enableDartSymbolication is false', - () async { - fixture.options.enableDartSymbolication = false; - final event = _getEvent(); - final processor = fixture.options.eventProcessors.first; - final resultEvent = await processor.apply(event, Hint()); - - expect(resultEvent, equals(event)); - }); - - test('Event processor adds debug image when symbolication is needed', - () async { - final stackTrace = ''' + final platforms = [ + MockPlatform.iOS(), + MockPlatform.macOS(), + MockPlatform.android(), + ]; + + for (final platform in platforms) { + setUp(() { + fixture = Fixture(); + fixture.options.platformChecker = + MockPlatformChecker(platform: platform); + }); + + test('adds itself to sdk.integrations', () { + expect( + fixture.options.sdk.integrations.contains('loadDartImageIntegration'), + true, + ); + }); + + test('Event processor is added to options', () { + expect(fixture.options.eventProcessors.length, 1); + expect( + fixture.options.eventProcessors.first.runtimeType.toString(), + '_LoadImageIntegrationEventProcessor', + ); + }); + + test( + 'Event processor does not add debug image if symbolication is not needed', + () async { + final event = _getEvent(needsSymbolication: false); + final processor = fixture.options.eventProcessors.first; + final resultEvent = await processor.apply(event, Hint()); + + expect(resultEvent, equals(event)); + }); + + test('Event processor does not add debug image if stackTrace is null', + () async { + final event = _getEvent(); + final processor = fixture.options.eventProcessors.first; + final resultEvent = await processor.apply(event, Hint()); + + expect(resultEvent, equals(event)); + }); + + test( + 'Event processor does not add debug image if enableDartSymbolication is false', + () async { + fixture.options.enableDartSymbolication = false; + final event = _getEvent(); + final processor = fixture.options.eventProcessors.first; + final resultEvent = await processor.apply(event, Hint()); + + expect(resultEvent, equals(event)); + }); + + test('Event processor adds debug image when symbolication is needed', + () async { + final stackTrace = ''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** build_id: 'b680cb890f9e3c12a24b172d050dec73' isolate_dso_base: 10000000 '''; - SentryEvent event = _getEvent(); - final processor = fixture.options.eventProcessors.first; - final resultEvent = await processor.apply( - event, Hint()..set(hintRawStackTraceKey, stackTrace)); - - expect(resultEvent?.debugMeta?.images.length, 1); - final debugImage = resultEvent?.debugMeta?.images.first; - expect(debugImage?.debugId, isNotEmpty); - expect(debugImage?.imageAddr, equals('0x10000000')); - }); + SentryEvent event = _getEvent(); + final processor = fixture.options.eventProcessors.first; + final resultEvent = await processor.apply( + event, Hint()..set(hintRawStackTraceKey, stackTrace)); + + expect(resultEvent?.debugMeta?.images.length, 1); + final debugImage = resultEvent?.debugMeta?.images.first; + expect(debugImage?.debugId, isNotEmpty); + expect(debugImage?.imageAddr, equals('0x10000000')); + }); + } }); } diff --git a/dart/test/mocks/mock_platform.dart b/dart/test/mocks/mock_platform.dart index d8cc6ee77e..9f8f391c88 100644 --- a/dart/test/mocks/mock_platform.dart +++ b/dart/test/mocks/mock_platform.dart @@ -13,6 +13,10 @@ class MockPlatform extends Platform with NoSuchMethodProvider { return MockPlatform(os: 'ios'); } + factory MockPlatform.macOS() { + return MockPlatform(os: 'macos'); + } + factory MockPlatform.linux() { return MockPlatform(os: 'linux'); } From 16ded4376e93e7dfc68039679f32b19db1b83e60 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Fri, 30 Aug 2024 00:49:56 +0200 Subject: [PATCH 30/40] cache debug image --- dart/lib/src/debug_image_extractor.dart | 12 ++++++++- dart/test/debug_image_extractor_test.dart | 31 +++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_image_extractor.dart index e9bedc5a7e..8aea703726 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -19,8 +19,18 @@ class DebugImageExtractor { final SentryOptions _options; + // We don't need to always parse the debug image, so we cache it here. + DebugImage? _debugImage; + + @visibleForTesting + DebugImage? get debugImageForTesting => _debugImage; + DebugImage? extractDebugImageFrom(String stackTraceString) { - return _extractDebugInfoFrom(stackTraceString).toDebugImage(); + if (_debugImage != null) { + return _debugImage; + } + _debugImage = _extractDebugInfoFrom(stackTraceString).toDebugImage(); + return _debugImage; } _DebugInfo _extractDebugInfoFrom(String stackTraceString) { diff --git a/dart/test/debug_image_extractor_test.dart b/dart/test/debug_image_extractor_test.dart index bd748d26a5..e03269cd1a 100644 --- a/dart/test/debug_image_extractor_test.dart +++ b/dart/test/debug_image_extractor_test.dart @@ -61,6 +61,19 @@ isolate_dso_base: 30000000 expect(debugImage?.codeId, isNull); }); + test('reuse debug image if it has already been extracted', () { + final stackTrace = ''' +*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** +build_id: 'b680cb890f9e3c12a24b172d050dec73' +isolate_dso_base: 40000000 +'''; + final extractor = fixture.getSut(platform: MockPlatform.iOS()); + + final debugImage = extractor.extractDebugImageFrom(stackTrace); + + expect(debugImage, isNull); + }); + test('sets correct type based on platform', () { final stackTrace = ''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** @@ -90,6 +103,24 @@ isolate_dso_base: 40000000 expect(debugImage, isNull); }); + + test('debugImage is cached after first extraction', () { + final stackTrace = ''' +*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** +build_id: 'b680cb890f9e3c12a24b172d050dec73' +isolate_dso_base: 10000000 +'''; + final extractor = fixture.getSut(platform: MockPlatform.android()); + + // First extraction + final debugImage1 = extractor.extractDebugImageFrom(stackTrace); + expect(debugImage1, isNotNull); + expect(extractor.debugImageForTesting, equals(debugImage1)); + + // Second extraction + final debugImage2 = extractor.extractDebugImageFrom(stackTrace); + expect(debugImage2, equals(debugImage1)); + }); }); } From 09864a73a79343696cc925e1e0e2ee13c0cd1461 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Fri, 30 Aug 2024 00:54:53 +0200 Subject: [PATCH 31/40] updaet --- dart/lib/src/debug_image_extractor.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_image_extractor.dart index 8aea703726..90a97260d3 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -110,6 +110,7 @@ class _DebugInfo { ); return null; } + return DebugImage( type: type, imageAddr: imageAddr, From b50b2d88134a18e5cd49f743e353017377482be2 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Fri, 30 Aug 2024 00:56:38 +0200 Subject: [PATCH 32/40] update var name --- dart/lib/src/load_dart_debug_images_integration.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dart/lib/src/load_dart_debug_images_integration.dart b/dart/lib/src/load_dart_debug_images_integration.dart index 29e3f0eea8..15d0be04a5 100644 --- a/dart/lib/src/load_dart_debug_images_integration.dart +++ b/dart/lib/src/load_dart_debug_images_integration.dart @@ -20,16 +20,16 @@ class _LoadImageIntegrationEventProcessor implements EventProcessor { @override Future apply(SentryEvent event, Hint hint) async { - final stackTrace = hint.get(hintRawStackTraceKey) as String?; + final rawStackTrace = hint.get(hintRawStackTraceKey) as String?; if (!_options.enableDartSymbolication || !event.needsSymbolication() || - stackTrace == null) { + rawStackTrace == null) { return event; } try { final syntheticImage = - _debugImageExtractor.extractDebugImageFrom(stackTrace); + _debugImageExtractor.extractDebugImageFrom(rawStackTrace); if (syntheticImage == null) { return event; } From 30a12fd3a165e081adffe462a23762b7cf78f661 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Fri, 30 Aug 2024 00:58:03 +0200 Subject: [PATCH 33/40] updaet --- dart/lib/src/debug_image_extractor.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_image_extractor.dart index 90a97260d3..001c2d15be 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -157,11 +157,9 @@ class _DebugInfo { } String bigToLittleEndianUuid(String bigEndianUuid) { - // Remove hyphens and convert to a byte array final byteArray = Uuid.parse(bigEndianUuid, validationMode: ValidationMode.nonStrict); - // Reverse the necessary sections according to the UUID fields final reversedByteArray = Uint8List.fromList([ ...byteArray.sublist(0, 4).reversed, ...byteArray.sublist(4, 6).reversed, From 05044c2260d1257db0adda6d57c804954402432a Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Fri, 30 Aug 2024 00:58:34 +0200 Subject: [PATCH 34/40] update naming --- dart/lib/src/debug_image_extractor.dart | 2 +- .../load_dart_debug_images_integration.dart | 3 +-- dart/test/debug_image_extractor_test.dart | 21 +++++++++---------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_image_extractor.dart index 001c2d15be..e20374e472 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -25,7 +25,7 @@ class DebugImageExtractor { @visibleForTesting DebugImage? get debugImageForTesting => _debugImage; - DebugImage? extractDebugImageFrom(String stackTraceString) { + DebugImage? extractFrom(String stackTraceString) { if (_debugImage != null) { return _debugImage; } diff --git a/dart/lib/src/load_dart_debug_images_integration.dart b/dart/lib/src/load_dart_debug_images_integration.dart index 15d0be04a5..81cf7cc679 100644 --- a/dart/lib/src/load_dart_debug_images_integration.dart +++ b/dart/lib/src/load_dart_debug_images_integration.dart @@ -28,8 +28,7 @@ class _LoadImageIntegrationEventProcessor implements EventProcessor { } try { - final syntheticImage = - _debugImageExtractor.extractDebugImageFrom(rawStackTrace); + final syntheticImage = _debugImageExtractor.extractFrom(rawStackTrace); if (syntheticImage == null) { return event; } diff --git a/dart/test/debug_image_extractor_test.dart b/dart/test/debug_image_extractor_test.dart index e03269cd1a..a588104ba2 100644 --- a/dart/test/debug_image_extractor_test.dart +++ b/dart/test/debug_image_extractor_test.dart @@ -16,7 +16,7 @@ void main() { test('returns null for invalid stack trace', () { final stackTrace = 'Invalid stack trace'; final extractor = fixture.getSut(platform: MockPlatform.android()); - final debugImage = extractor.extractDebugImageFrom(stackTrace); + final debugImage = extractor.extractFrom(stackTrace); expect(debugImage, isNull); }); @@ -28,7 +28,7 @@ build_id: 'b680cb890f9e3c12a24b172d050dec73' isolate_dso_base: 20000000 '''; final extractor = fixture.getSut(platform: MockPlatform.android()); - final debugImage = extractor.extractDebugImageFrom(stackTrace); + final debugImage = extractor.extractFrom(stackTrace); expect( debugImage?.debugId, equals('89cb80b6-9e0f-123c-a24b-172d050dec73')); @@ -41,7 +41,7 @@ build_id: 'f1c3bcc0279865fe3058404b2831d9e64135386c' isolate_dso_base: 30000000 '''; final extractor = fixture.getSut(platform: MockPlatform.android()); - final debugImage = extractor.extractDebugImageFrom(stackTrace); + final debugImage = extractor.extractFrom(stackTrace); expect( debugImage?.debugId, equals('c0bcc3f1-9827-fe65-3058-404b2831d9e6')); @@ -54,7 +54,7 @@ build_id: 'b680cb890f9e3c12a24b172d050dec73' isolate_dso_base: 30000000 '''; final extractor = fixture.getSut(platform: MockPlatform.iOS()); - final debugImage = extractor.extractDebugImageFrom(stackTrace); + final debugImage = extractor.extractFrom(stackTrace); expect( debugImage?.debugId, equals('b680cb89-0f9e-3c12-a24b-172d050dec73')); @@ -69,7 +69,7 @@ isolate_dso_base: 40000000 '''; final extractor = fixture.getSut(platform: MockPlatform.iOS()); - final debugImage = extractor.extractDebugImageFrom(stackTrace); + final debugImage = extractor.extractFrom(stackTrace); expect(debugImage, isNull); }); @@ -83,9 +83,8 @@ isolate_dso_base: 40000000 final androidExtractor = fixture.getSut(platform: MockPlatform.android()); final iosExtractor = fixture.getSut(platform: MockPlatform.iOS()); - final androidDebugImage = - androidExtractor.extractDebugImageFrom(stackTrace); - final iosDebugImage = iosExtractor.extractDebugImageFrom(stackTrace); + final androidDebugImage = androidExtractor.extractFrom(stackTrace); + final iosDebugImage = iosExtractor.extractFrom(stackTrace); expect(androidDebugImage?.type, equals('elf')); expect(iosDebugImage?.type, equals('macho')); @@ -99,7 +98,7 @@ isolate_dso_base: 40000000 '''; final extractor = fixture.getSut(platform: MockPlatform.linux()); - final debugImage = extractor.extractDebugImageFrom(stackTrace); + final debugImage = extractor.extractFrom(stackTrace); expect(debugImage, isNull); }); @@ -113,12 +112,12 @@ isolate_dso_base: 10000000 final extractor = fixture.getSut(platform: MockPlatform.android()); // First extraction - final debugImage1 = extractor.extractDebugImageFrom(stackTrace); + final debugImage1 = extractor.extractFrom(stackTrace); expect(debugImage1, isNotNull); expect(extractor.debugImageForTesting, equals(debugImage1)); // Second extraction - final debugImage2 = extractor.extractDebugImageFrom(stackTrace); + final debugImage2 = extractor.extractFrom(stackTrace); expect(debugImage2, equals(debugImage1)); }); }); From d592ffdc84b3f091f38fc3c98dd67980baa73d8e Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Fri, 30 Aug 2024 01:01:21 +0200 Subject: [PATCH 35/40] improve names --- dart/test/debug_image_extractor_test.dart | 15 +-------------- .../load_dart_debug_images_integration_test.dart | 2 +- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/dart/test/debug_image_extractor_test.dart b/dart/test/debug_image_extractor_test.dart index a588104ba2..6218c726a9 100644 --- a/dart/test/debug_image_extractor_test.dart +++ b/dart/test/debug_image_extractor_test.dart @@ -6,7 +6,7 @@ import 'mocks/mock_platform.dart'; import 'mocks/mock_platform_checker.dart'; void main() { - group('DebugImageExtractor', () { + group(DebugImageExtractor, () { late Fixture fixture; setUp(() { @@ -61,19 +61,6 @@ isolate_dso_base: 30000000 expect(debugImage?.codeId, isNull); }); - test('reuse debug image if it has already been extracted', () { - final stackTrace = ''' -*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** -build_id: 'b680cb890f9e3c12a24b172d050dec73' -isolate_dso_base: 40000000 -'''; - final extractor = fixture.getSut(platform: MockPlatform.iOS()); - - final debugImage = extractor.extractFrom(stackTrace); - - expect(debugImage, isNull); - }); - test('sets correct type based on platform', () { final stackTrace = ''' *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** diff --git a/dart/test/load_dart_debug_images_integration_test.dart b/dart/test/load_dart_debug_images_integration_test.dart index 74dca46d2a..8b10a62328 100644 --- a/dart/test/load_dart_debug_images_integration_test.dart +++ b/dart/test/load_dart_debug_images_integration_test.dart @@ -9,7 +9,7 @@ import 'mocks/mock_platform.dart'; import 'mocks/mock_platform_checker.dart'; void main() { - group(LoadDartDebugImagesIntegration(), () { + group(LoadDartDebugImagesIntegration, () { late Fixture fixture; final platforms = [ From 11ef042fd42eb68e6e29ec7144cbe10d161a304e Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 4 Sep 2024 14:08:15 +0200 Subject: [PATCH 36/40] break early safeguard for parsing stacktrace and dont throw in hex format parsing --- dart/lib/src/debug_image_extractor.dart | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/dart/lib/src/debug_image_extractor.dart b/dart/lib/src/debug_image_extractor.dart index e20374e472..99776ee12b 100644 --- a/dart/lib/src/debug_image_extractor.dart +++ b/dart/lib/src/debug_image_extractor.dart @@ -43,6 +43,12 @@ class DebugImageExtractor { if (_isHeaderStartLine(line)) { continue; } + // Stop parsing as soon as we get to the stack frames + // This should never happen but is a safeguard to avoid looping + // through every line of the stack trace + if (line.contains("#00 abs")) { + break; + } buildId ??= _extractBuildId(line); isolateDsoBase ??= _extractIsolateDsoBase(line); @@ -132,7 +138,7 @@ class _DebugInfo { final first16Bytes = codeId.substring(0, 32); final byteData = _parseHexToBytes(first16Bytes); - if (byteData.isEmpty) { + if (byteData == null || byteData.isEmpty) { _options.logger( SentryLevel.warning, 'Failed to convert code ID to debug ID'); return null; @@ -141,9 +147,11 @@ class _DebugInfo { return bigToLittleEndianUuid(UuidValue.fromByteList(byteData).uuid); } - Uint8List _parseHexToBytes(String hex) { + Uint8List? _parseHexToBytes(String hex) { if (hex.length % 2 != 0) { - throw ArgumentError('Invalid hex string'); + _options.logger( + SentryLevel.warning, 'Invalid hex string during debug image parsing'); + return null; } if (hex.startsWith('0x')) { hex = hex.substring(2); From 6dab4d8446dd3c43c9c050e77494f41dd42274ba Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 4 Sep 2024 14:15:51 +0200 Subject: [PATCH 37/40] revert load native image list integration --- flutter/lib/src/integrations/integrations.dart | 2 +- ...egration.dart => load_image_list_integration.dart} | 5 +++-- flutter/lib/src/sentry_flutter.dart | 2 +- ...ntegration_test.dart => load_image_list_test.dart} | 11 +++++------ 4 files changed, 10 insertions(+), 10 deletions(-) rename flutter/lib/src/integrations/{load_native_debug_images_integration.dart => load_image_list_integration.dart} (88%) rename flutter/test/integrations/{load_native_debug_images_integration_test.dart => load_image_list_test.dart} (90%) diff --git a/flutter/lib/src/integrations/integrations.dart b/flutter/lib/src/integrations/integrations.dart index df4fe0a025..ac16732f58 100644 --- a/flutter/lib/src/integrations/integrations.dart +++ b/flutter/lib/src/integrations/integrations.dart @@ -1,7 +1,7 @@ export 'debug_print_integration.dart'; export 'flutter_error_integration.dart'; export 'load_contexts_integration.dart'; -export 'load_native_debug_images_integration.dart'; +export 'load_image_list_integration.dart'; export 'load_release_integration.dart'; export 'native_app_start_integration.dart'; export 'native_sdk_integration.dart'; diff --git a/flutter/lib/src/integrations/load_native_debug_images_integration.dart b/flutter/lib/src/integrations/load_image_list_integration.dart similarity index 88% rename from flutter/lib/src/integrations/load_native_debug_images_integration.dart rename to flutter/lib/src/integrations/load_image_list_integration.dart index daeeede597..24d6e62439 100644 --- a/flutter/lib/src/integrations/load_native_debug_images_integration.dart +++ b/flutter/lib/src/integrations/load_image_list_integration.dart @@ -9,11 +9,12 @@ import 'package:sentry/src/load_dart_debug_images_integration.dart' show NeedsSymbolication; /// Loads the native debug image list for stack trace symbolication. -class LoadNativeDebugImagesIntegration +class LoadImageListIntegration +/// TODO: rename to LoadNativeDebugImagesIntegration in the next major version extends Integration { final SentryNativeBinding _native; - LoadNativeDebugImagesIntegration(this._native); + LoadImageListIntegration(this._native); @override void call(Hub hub, SentryFlutterOptions options) { diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index 5705473734..4d875edea2 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -180,7 +180,7 @@ mixin SentryFlutter { if (native != null) { integrations.add(NativeSdkIntegration(native)); integrations.add(LoadContextsIntegration(native)); - integrations.add(LoadNativeDebugImagesIntegration(native)); + integrations.add(LoadImageListIntegration(native)); options.enableDartSymbolication = false; } diff --git a/flutter/test/integrations/load_native_debug_images_integration_test.dart b/flutter/test/integrations/load_image_list_test.dart similarity index 90% rename from flutter/test/integrations/load_native_debug_images_integration_test.dart rename to flutter/test/integrations/load_image_list_test.dart index 743a1c4fc3..35e59b7599 100644 --- a/flutter/test/integrations/load_native_debug_images_integration_test.dart +++ b/flutter/test/integrations/load_image_list_test.dart @@ -4,12 +4,12 @@ library flutter_test; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:sentry_flutter/src/integrations/load_native_debug_images_integration.dart'; +import 'package:sentry_flutter/src/integrations/load_image_list_integration.dart'; import 'fixture.dart'; void main() { - group(LoadNativeDebugImagesIntegration, () { + group(LoadImageListIntegration, () { final imageList = [ DebugImage.fromJson({ 'code_file': '/apex/com.android.art/javalib/arm64/boot.oat', @@ -22,17 +22,16 @@ void main() { }) ]; - late IntegrationTestFixture fixture; + late IntegrationTestFixture fixture; setUp(() async { - fixture = IntegrationTestFixture(LoadNativeDebugImagesIntegration.new); + fixture = IntegrationTestFixture(LoadImageListIntegration.new); when(fixture.binding.loadDebugImages()) .thenAnswer((_) async => imageList); await fixture.registerIntegration(); }); - test('$LoadNativeDebugImagesIntegration adds itself to sdk.integrations', - () async { + test('$LoadImageListIntegration adds itself to sdk.integrations', () async { expect( fixture.options.sdk.integrations.contains('loadImageListIntegration'), true, From 22741a39afda8398dd8d1dfb057ae8a6e9ee2b08 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Wed, 4 Sep 2024 14:16:59 +0200 Subject: [PATCH 38/40] update --- .../lib/src/integrations/load_image_list_integration.dart | 5 ++--- flutter/test/sentry_flutter_test.dart | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/flutter/lib/src/integrations/load_image_list_integration.dart b/flutter/lib/src/integrations/load_image_list_integration.dart index 24d6e62439..776c86640d 100644 --- a/flutter/lib/src/integrations/load_image_list_integration.dart +++ b/flutter/lib/src/integrations/load_image_list_integration.dart @@ -9,9 +9,8 @@ import 'package:sentry/src/load_dart_debug_images_integration.dart' show NeedsSymbolication; /// Loads the native debug image list for stack trace symbolication. -class LoadImageListIntegration -/// TODO: rename to LoadNativeDebugImagesIntegration in the next major version - extends Integration { +class LoadImageListIntegration extends Integration { + /// TODO: rename to LoadNativeDebugImagesIntegration in the next major version final SentryNativeBinding _native; LoadImageListIntegration(this._native); diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index cdfdd3a16b..534dd6ac1e 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -38,13 +38,13 @@ final nonWebIntegrations = [ // These should be added to Android final androidIntegrations = [ - LoadNativeDebugImagesIntegration, + LoadImageListIntegration, LoadContextsIntegration, ]; // These should be added to iOS and macOS final iOsAndMacOsIntegrations = [ - LoadNativeDebugImagesIntegration, + LoadImageListIntegration, LoadContextsIntegration, ]; From e1d019b5b4b8dc819f01d12d9075b4ef3de7107e Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Mon, 9 Sep 2024 12:51:58 +0200 Subject: [PATCH 39/40] fix analyze --- flutter/example/lib/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 0f1cd9279d..73fea638f8 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -1051,6 +1051,7 @@ Future showDialogWithTextAndImage(BuildContext context) async { routeSettings: const RouteSettings( name: 'AssetBundle dialog', ), + // ignore: use_build_context_synchronously builder: (context) { return AlertDialog( title: const Text('Asset Example'), From 6a5a5551aa7bc1401468f2dc9b370324669663df Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Mon, 9 Sep 2024 12:53:19 +0200 Subject: [PATCH 40/40] fix analyze --- flutter/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 73fea638f8..3454696b3f 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -1046,12 +1046,12 @@ Future showDialogWithTextAndImage(BuildContext context) async { final imageBytes = await DefaultAssetBundle.of(context).load('assets/sentry-wordmark.png'); await showDialog( + // ignore: use_build_context_synchronously context: context, // gets tracked if using SentryNavigatorObserver routeSettings: const RouteSettings( name: 'AssetBundle dialog', ), - // ignore: use_build_context_synchronously builder: (context) { return AlertDialog( title: const Text('Asset Example'),