Skip to content

Commit

Permalink
update to the latest cocoa SDK API
Browse files Browse the repository at this point in the history
  • Loading branch information
vaind committed Sep 11, 2023
1 parent fb16009 commit 473dae2
Show file tree
Hide file tree
Showing 14 changed files with 513 additions and 134 deletions.
2 changes: 1 addition & 1 deletion dart/lib/src/hub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ class Hub {
Profiler? profiler;
if (_profilerFactory != null &&
_tracesSampler.sampleProfiling(samplingDecision)) {
profiler = _profilerFactory?.startProfiling(transactionContext);
profiler = _profilerFactory?.startProfiler(transactionContext);
}

final tracer = SentryTracer(
Expand Down
2 changes: 1 addition & 1 deletion dart/lib/src/profiling.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import '../sentry.dart';

@internal
abstract class ProfilerFactory {
Profiler? startProfiling(SentryTransactionContext context);
Profiler? startProfiler(SentryTransactionContext context);
}

@internal
Expand Down
2 changes: 1 addition & 1 deletion dart/lib/src/protocol/sentry_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class SentryEvent with SentryEventLike<SentryEvent> {
/// The ID Sentry.io assigned to the submitted event for future reference.
final SentryId eventId;

/// A timestamp representing when the breadcrumb occurred.
/// A timestamp representing when the event occurred.
final DateTime? timestamp;

/// A string representing the platform the SDK is submitting from. This will be used by the Sentry interface to customize various components in the interface.
Expand Down
8 changes: 4 additions & 4 deletions dart/test/hub_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ void main() {
test('profiler is started according to the sampling rate', () async {
final hub = fixture.getSut();
final factory = MockProfilerFactory();
when(factory.startProfiling(fixture._context)).thenReturn(MockProfiler());
when(factory.startProfiler(fixture._context)).thenReturn(MockProfiler());
hub.profilerFactory = factory;

var tr = hub.startTransactionWithContext(fixture._context);
Expand All @@ -397,15 +397,15 @@ void main() {
hub.options.profilesSampleRate = 1.0;
tr = hub.startTransactionWithContext(fixture._context);
expect((tr as SentryTracer).profiler, isNotNull);
verify(factory.startProfiling(fixture._context)).called(1);
verify(factory.startProfiler(fixture._context)).called(1);
});

test('profiler.finish() is called', () async {
final hub = fixture.getSut();
final factory = MockProfilerFactory();
final profiler = MockProfiler();
final expected = MockProfileInfo();
when(factory.startProfiling(fixture._context)).thenReturn(profiler);
when(factory.startProfiler(fixture._context)).thenReturn(profiler);
when(profiler.finishFor(any)).thenAnswer((_) async => expected);

hub.profilerFactory = factory;
Expand All @@ -421,7 +421,7 @@ void main() {
final factory = MockProfilerFactory();
final profiler = MockProfiler();
final expected = MockProfileInfo();
when(factory.startProfiling(fixture._context)).thenReturn(profiler);
when(factory.startProfiler(fixture._context)).thenReturn(profiler);
when(profiler.finishFor(any)).thenAnswer((_) async => expected);

hub.profilerFactory = factory;
Expand Down
4 changes: 2 additions & 2 deletions dart/test/mocks.mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ class MockProfilerFactory extends _i1.Mock implements _i3.ProfilerFactory {
}

@override
_i3.Profiler? startProfiling(_i2.SentryTransactionContext? context) =>
_i3.Profiler? startProfiler(_i2.SentryTransactionContext? context) =>
(super.noSuchMethod(Invocation.method(
#startProfiling,
#startProfiler,
[context],
)) as _i3.Profiler?);
}
Expand Down
30 changes: 25 additions & 5 deletions flutter/ios/Classes/SentryFlutterPluginApple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,11 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin {
removeTag(key: key, result: result)

#if !os(tvOS) && !os(watchOS)
case "startProfiling":
startProfiling(call, result)
case "startProfiler":
startProfiler(call, result)

case "discardProfiler":
discardProfiler(call, result)

case "collectProfile":
collectProfile(call, result)
Expand Down Expand Up @@ -559,14 +562,14 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin {
}
}

private func startProfiling(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
private func startProfiler(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
guard let traceId = call.arguments as? String else {
print("Cannot start profiling: trace ID missing")
result(FlutterError(code: "5", message: "Cannot start profiling: trace ID missing", details: nil))
return
}

let startTime = PrivateSentrySDKOnly.startProfiling(forTrace: SentryId(uuidString: traceId))
let startTime = PrivateSentrySDKOnly.startProfiler(forTrace: SentryId(uuidString: traceId))
result(startTime)
}

Expand All @@ -584,9 +587,26 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin {
return
}

let payload = PrivateSentrySDKOnly.collectProfile(forTrace: SentryId(uuidString: traceId), since: startTime)
guard let endTime = arguments["endTime"] as? UInt64 else {
print("Cannot collect profile: end time missing")
result(FlutterError(code: "8", message: "Cannot collect profile: end time missing", details: nil))
return
}

let payload = PrivateSentrySDKOnly.collectProfileBetween(startTime, and: endTime, forTrace: SentryId(uuidString: traceId))
result(payload)
}

private func discardProfiler(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
guard let traceId = call.arguments as? String else {
print("Cannot discard a profiler: trace ID missing")
result(FlutterError(code: "9", message: "Cannot discard a profiler: trace ID missing", details: nil))
return
}

PrivateSentrySDKOnly.discardProfiler(forTrace: SentryId(uuidString: traceId))
result(nil)
}
}

// swiftlint:enable function_body_length
49 changes: 32 additions & 17 deletions flutter/lib/src/profiling.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import 'sentry_native.dart';
// ignore: invalid_use_of_internal_member
class NativeProfilerFactory implements ProfilerFactory {
final SentryNative _native;
final ClockProvider _clock;

NativeProfilerFactory(this._native);
NativeProfilerFactory(this._native, this._clock);

static void attachTo(Hub hub) {
// ignore: invalid_use_of_internal_member
Expand All @@ -32,24 +33,26 @@ class NativeProfilerFactory implements ProfilerFactory {
if (options.platformChecker.platform.isMacOS ||
options.platformChecker.platform.isIOS) {
// ignore: invalid_use_of_internal_member
hub.profilerFactory = NativeProfilerFactory(SentryNative());
hub.profilerFactory =
// ignore: invalid_use_of_internal_member
NativeProfilerFactory(SentryNative(), options.clock);
}
}

@override
NativeProfiler? startProfiling(SentryTransactionContext context) {
NativeProfiler? startProfiler(SentryTransactionContext context) {
if (context.traceId == SentryId.empty()) {
return null;
}

final startTime = _native.startProfiling(context.traceId);
final startTime = _native.startProfiler(context.traceId);

// TODO we cannot await the future returned by a method channel because
// startTransaction() is synchronous. In order to make this code fully
// synchronous and actually start the profiler, we need synchronous FFI
// calls, see https://github.com/getsentry/sentry-dart/issues/1444
// For now, return immediately even though the profiler may not have started yet...
return NativeProfiler(_native, startTime, context.traceId);
return NativeProfiler(_native, startTime, context.traceId, _clock);
}
}

Expand All @@ -60,33 +63,45 @@ class NativeProfiler implements Profiler {
final SentryNative _native;
final Future<int?> _startTime;
final SentryId _traceId;
bool _finished = false;
final ClockProvider _clock;

NativeProfiler(this._native, this._startTime, this._traceId);
NativeProfiler(this._native, this._startTime, this._traceId, this._clock);

@override
void dispose() {
// TODO expose in the cocoa SDK
// _startTime.then((_) => _native.discardProfiling(this._traceId));
if (!_finished) {
_finished = true;
_startTime.then((_) => _native.discardProfiler(_traceId));
}
}

@override
Future<NativeProfileInfo?> finishFor(SentryTransaction transaction) async {
final starTime = await _startTime;
if (starTime == null) {
if (_finished) {
return null;
}
_finished = true;

final starTimeNs = await _startTime;
if (starTimeNs == null) {
return null;
}

final payload = await _native.collectProfile(_traceId, starTime);
// ignore: invalid_use_of_internal_member
final transactionEndTime = transaction.timestamp ?? _clock();
final duration = transactionEndTime.difference(transaction.startTimestamp);
final endTimeNs = starTimeNs + (duration.inMicroseconds * 1000);

final payload =
await _native.collectProfile(_traceId, starTimeNs, endTimeNs);
if (payload == null) {
return null;
}

payload["transaction"] = <String, String?>{
"id": transaction.eventId.toString(),
"trace_id": _traceId.toString(),
"name": transaction.transaction,
// "active_thread_id" : [transaction.trace.transactionContext sentry_threadInfo].threadId
};
payload["transaction"]["id"] = transaction.eventId.toString();
payload["transaction"]["trace_id"] = _traceId.toString();
payload["transaction"]["name"] = transaction.transaction;
payload["timestamp"] = transaction.startTimestamp.toIso8601String();
return NativeProfileInfo(payload);
}
Expand Down
12 changes: 8 additions & 4 deletions flutter/lib/src/sentry_native.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,17 @@ class SentryNative {
return await _nativeChannel?.removeTag(key);
}

Future<int?> startProfiling(SentryId traceId) async {
return _nativeChannel?.startProfiling(traceId);
Future<int?> startProfiler(SentryId traceId) async {
return _nativeChannel?.startProfiler(traceId);
}

Future<void> discardProfiler(SentryId traceId) async {
return _nativeChannel?.discardProfiler(traceId);
}

Future<Map<String, dynamic>?> collectProfile(
SentryId traceId, int startTimeNs) async {
return _nativeChannel?.collectProfile(traceId, startTimeNs);
SentryId traceId, int startTimeNs, int endTimeNs) async {
return _nativeChannel?.collectProfile(traceId, startTimeNs, endTimeNs);
}

/// Reset state
Expand Down
23 changes: 17 additions & 6 deletions flutter/lib/src/sentry_native_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,32 @@ class SentryNativeChannel {
}
}

Future<int?> startProfiling(SentryId traceId) async {
Future<int?> startProfiler(SentryId traceId) async {
try {
return await _channel.invokeMethod('startProfiling', traceId.toString())
return await _channel.invokeMethod('startProfiler', traceId.toString())
as int?;
} catch (error, stackTrace) {
_logError('startProfiling', error, stackTrace);
_logError('startProfiler', error, stackTrace);
return null;
}
}

Future<void> discardProfiler(SentryId traceId) async {
try {
return await _channel.invokeMethod('discardProfiler', traceId.toString());
} catch (error, stackTrace) {
_logError('discardProfiler', error, stackTrace);
}
}

Future<Map<String, dynamic>?> collectProfile(
SentryId traceId, int startTimeNs) async {
SentryId traceId, int startTimeNs, int endTimeNs) async {
try {
return await _channel.invokeMapMethod<String, dynamic>('collectProfile',
{'traceId': traceId.toString(), 'startTime': startTimeNs});
return await _channel.invokeMapMethod<String, dynamic>('collectProfile', {
'traceId': traceId.toString(),
'startTime': startTimeNs,
'endTime': endTimeNs,
});
} catch (error, stackTrace) {
_logError('collectProfile', error, stackTrace);
return null;
Expand Down
31 changes: 23 additions & 8 deletions flutter/test/mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ ISentrySpan startTransactionShim(
Transport,
// ignore: invalid_use_of_internal_member
SentryTracer,
SentryTransaction,
MethodChannel,
], customMocks: [
MockSpec<Hub>(fallbackGenerators: {#startTransaction: startTransactionShim})
Expand Down Expand Up @@ -189,7 +190,8 @@ class TestMockSentryNative implements SentryNative {
var numberOfSetTagCalls = 0;
SentryUser? sentryUser;
var numberOfSetUserCalls = 0;
var numberOfStartProfilingCalls = 0;
var numberOfStartProfilerCalls = 0;
var numberOfDiscardProfilerCalls = 0;
var numberOfCollectProfileCalls = 0;

@override
Expand Down Expand Up @@ -270,14 +272,20 @@ class TestMockSentryNative implements SentryNative {

@override
Future<Map<String, dynamic>?> collectProfile(
SentryId traceId, int startTimeNs) {
SentryId traceId, int startTimeNs, int endTimeNs) {
numberOfCollectProfileCalls++;
return Future.value(null);
}

@override
Future<int?> startProfiling(SentryId traceId) {
numberOfStartProfilingCalls++;
Future<int?> startProfiler(SentryId traceId) {
numberOfStartProfilerCalls++;
return Future.value(42);
}

@override
Future<void> discardProfiler(SentryId traceId) {
numberOfDiscardProfilerCalls++;
return Future.value(null);
}
}
Expand All @@ -299,7 +307,8 @@ class MockNativeChannel implements SentryNativeChannel {
int numberOfSetContextsCalls = 0;
int numberOfSetExtraCalls = 0;
int numberOfSetTagCalls = 0;
int numberOfStartProfilingCalls = 0;
int numberOfStartProfilerCalls = 0;
int numberOfDiscardProfilerCalls = 0;
int numberOfCollectProfileCalls = 0;

@override
Expand Down Expand Up @@ -364,14 +373,20 @@ class MockNativeChannel implements SentryNativeChannel {

@override
Future<Map<String, dynamic>?> collectProfile(
SentryId traceId, int startTimeNs) {
SentryId traceId, int startTimeNs, int endTimeNs) {
numberOfCollectProfileCalls++;
return Future.value(null);
}

@override
Future<int?> startProfiling(SentryId traceId) {
numberOfStartProfilingCalls++;
Future<int?> startProfiler(SentryId traceId) {
numberOfStartProfilerCalls++;
return Future.value(null);
}

@override
Future<int?> discardProfiler(SentryId traceId) {
numberOfDiscardProfilerCalls++;
return Future.value(null);
}
}
Expand Down
Loading

0 comments on commit 473dae2

Please sign in to comment.