-
-
Notifications
You must be signed in to change notification settings - Fork 323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: allow profiling from hybrid SDKs #3194
Conversation
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #3194 +/- ##
=============================================
+ Coverage 89.179% 89.182% +0.003%
=============================================
Files 502 502
Lines 54026 54060 +34
Branches 19379 19398 +19
=============================================
+ Hits 48180 48212 +32
+ Misses 4994 4880 -114
- Partials 852 968 +116
... and 31 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
4b7a717
to
16507cc
Compare
Performance metrics 🚀
|
Revision | Plain | With Sentry | Diff |
---|---|---|---|
302ee8b | 1194.02 ms | 1223.34 ms | 29.32 ms |
1e065bc | 1239.69 ms | 1258.18 ms | 18.49 ms |
be51b56 | 1220.84 ms | 1249.36 ms | 28.52 ms |
32c4446 | 1225.00 ms | 1231.29 ms | 6.29 ms |
3277f18 | 1229.29 ms | 1248.92 ms | 19.63 ms |
15b8c61 | 1223.16 ms | 1244.83 ms | 21.67 ms |
794f87f | 1225.78 ms | 1243.46 ms | 17.68 ms |
98a8c16 | 1206.40 ms | 1232.14 ms | 25.74 ms |
1db04d8 | 1250.20 ms | 1258.12 ms | 7.92 ms |
c2ec420 | 1256.27 ms | 1269.24 ms | 12.97 ms |
App size
Revision | Plain | With Sentry | Diff |
---|---|---|---|
302ee8b | 20.76 KiB | 419.62 KiB | 398.87 KiB |
1e065bc | 20.76 KiB | 425.78 KiB | 405.01 KiB |
be51b56 | 20.76 KiB | 432.20 KiB | 411.44 KiB |
32c4446 | 22.84 KiB | 403.24 KiB | 380.39 KiB |
3277f18 | 22.84 KiB | 402.88 KiB | 380.03 KiB |
15b8c61 | 20.76 KiB | 419.67 KiB | 398.91 KiB |
794f87f | 20.76 KiB | 401.37 KiB | 380.61 KiB |
98a8c16 | 20.76 KiB | 431.00 KiB | 410.24 KiB |
1db04d8 | 20.76 KiB | 435.50 KiB | 414.74 KiB |
c2ec420 | 20.76 KiB | 401.65 KiB | 380.89 KiB |
Previous results on branch: feat/hybrid-sdk-profiling
Startup times
Revision | Plain | With Sentry | Diff |
---|---|---|---|
03a521f | 1253.02 ms | 1255.46 ms | 2.44 ms |
bbe7566 | 1224.92 ms | 1233.66 ms | 8.74 ms |
194680d | 1254.00 ms | 1269.65 ms | 15.65 ms |
ca7bde3 | 1231.17 ms | 1256.17 ms | 25.00 ms |
1c02a7b | 1219.43 ms | 1232.71 ms | 13.29 ms |
App size
Revision | Plain | With Sentry | Diff |
---|---|---|---|
03a521f | 22.84 KiB | 403.13 KiB | 380.29 KiB |
bbe7566 | 22.84 KiB | 403.14 KiB | 380.29 KiB |
194680d | 22.84 KiB | 403.14 KiB | 380.29 KiB |
ca7bde3 | 22.84 KiB | 403.14 KiB | 380.29 KiB |
1c02a7b | 22.84 KiB | 403.14 KiB | 380.29 KiB |
8e0cb3d
to
18bb7bd
Compare
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks ok to me, but I would like for @armcknight to also review this.
@"elapsed_since_start_ns" : sentry_stringForUInt64( | ||
getDurationNs(transaction.startSystemTime, sample.absoluteTimestamp)), | ||
@"elapsed_since_start_ns" : | ||
sentry_stringForUInt64(getDurationNs(startSystemTime, sample.absoluteTimestamp)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to this PR: this should be an integer, not a string, right? https://develop.sentry.dev/sdk/profiles/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it must be a string because the backend JSON implementation can't support unsigned 64 bit integers. Unless that has changed, cc @phacops
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it has changed, consider the impact on self hosted Sentry though. We prefer adding breaking changes (ideally never) on. SDK major releases and be clear on changelog which self hosted version is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't changed any of this here, just noticed and pointed out that the timestamp is sent as a string here but an integer in (some of the) other SDKs
SENTRY_LOG_WARN(@"Expected a profiler for tracer id %@ but none was found", | ||
transaction.trace.traceId.sentryIdString); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The warning is already part of the profilerForFinishedTracer()
under the SENTRY_CASSERT_RETURN
macro
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice find 👍🏻
onHub:[SentrySDK currentHub]]; | ||
|
||
if (payload != nil) { | ||
payload[@"platform"] = @"cocoa"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the usual profiler code path, this is copied from transaction
(a.k.a. SentryEvent
). Ideally, there would be a constant we could use in both places but there doesn't seem to be one. I wasn't sure about a place to move it to so I didn't...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is hardcoded the same way in SentryEvent.m. We could define it in SentryInternalDefines.h and use that constant in all three places, are you able to import that header here?
18bb7bd
to
b21637e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The refactor of the profiling code to expose the +[collectProfileBetween:...]
looks fine, the part I'm confused about is why things were changed from passing a SentryTracer or SentryTransaction, in favor more parameters that are all encapsulated by them already? There is a SentryTracer and SentryTransaction in the dart codebase, so I assume we'd be able to call a function like +[PrivateSentrySDKOnly startProfilingForTrace:(SentryTracer *)]
instead of +[PrivateSentrySDKOnly startProfilingForTrace:(SentryId *)]
. Let me know if I'm missing something there, I don't have any experience writing Dart or with our SDK for it.
onHub:[SentrySDK currentHub]]; | ||
|
||
if (payload != nil) { | ||
payload[@"platform"] = @"cocoa"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is hardcoded the same way in SentryEvent.m. We could define it in SentryInternalDefines.h and use that constant in all three places, are you able to import that header here?
@@ -55,12 +54,12 @@ | |||
std::mutex _gStateLock; | |||
|
|||
void | |||
trackProfilerForTracer(SentryProfiler *profiler, SentryTracer *tracer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I purposely used SentryTracer here to keep type safety. SentryEvent, SentrySpan and SentryTracer all have a SentryId property, but they're not interchangeable, so I did this to prevent any mistakes.
@@ -46,23 +46,22 @@ @implementation SentryMetricReading | |||
*/ | |||
SentrySerializedMetricEntry *_Nullable serializeValuesWithNormalizedTime( | |||
NSArray<SentryMetricReading *> *absoluteTimestampValues, NSString *unit, | |||
SentryTransaction *transaction) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing here with type safety, in addition to cutting down the amount of parameters. Unless this is required for this PR due to some nuance with compilation for hybrid platforms, I'd prefer this be reverted.
@@ -43,7 +44,7 @@ SENTRY_EXTERN_C_END | |||
/** | |||
* Start a profiler, if one isn't already running. | |||
*/ | |||
+ (void)startWithTracer:(SentryTracer *)tracer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same note for type safety here.
SENTRY_LOG_WARN(@"Expected a profiler for tracer id %@ but none was found", | ||
transaction.trace.traceId.sentryIdString); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice find 👍🏻
const auto payload = [self collectProfileBetween:transaction.startSystemTime | ||
and:transaction.endSystemTime | ||
forTrace:transaction.trace.traceId | ||
onHub:transaction.trace.hub]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar thought here w.r.t. parameters, why not just pass the transaction? This is more repetitive and verbose.
Yes, in Dart, there are types named the same. However, they are not actually the same types, they're not objective-c Instead, it seems a better option to decouple obj-c Profiler from I hope that makes sense to you. |
Co-authored-by: Andrew McKnight <[email protected]>
Thanks for the thoughtful reply @vaind . There is an issue though:
This is not true for our other platforms right now, so it would require backend changes to make it so. We currently must always have a transaction associated to each profile for dynamic sampling reasons, and profiles are uploaded as attachment items in transaction envelopes. We can't wind up with a profile which has had its transaction DS'd out, for instance. So we also can't have a profile that never had a transaction in the first place. The only way for profiler to start currently is when a tracer starts: sentry-cocoa/Sources/Sentry/SentryTracer.m Lines 164 to 170 in 279841c
And then when a tracer ends is the only place a profile can possibly be uploaded: sentry-cocoa/Sources/Sentry/SentryTracer.m Lines 526 to 552 in 279841c
FYI, we do want to get to a place where we can separate profiles from transactions, but that is a rough plan for the future. Does this change how you see the state of things? |
No, not really, I am aware how profiling works, from other SDKs I've implemented it in. In case of Flutter, we need to capture a native profile. Therefore, the profile will be collected by the native (cocoa) profiler and then handed over to Flutter where it will be serialized and attached (as an additional envelope item) to the same envelope as the transaction. This is an important point - the transaction attached to the profile is a Dart (Flutter) transaction, being created directly in Dart and going through all the handling, user callbacks, etc. in Dart user code. This transaction has nothing to do with objective-c |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I thought that might be the case. Thanks for explaining @vaind 👍🏻
e8fdfdc
to
22df0d7
Compare
📜 Description
💡 Motivation and Context
Cocoa SDK built-in native profiler can be used to support profiling Flutter apps, see getsentry/sentry-dart#1106
This PR refactors some profiler APIs so that they don't depend on the cocoa
Tracer
&Transaction
and instead expect only the necessary arguments.#skip-changelog
💚 How did you test it?
All previous tests pass & adding new tests for new hybrid SDK APIs
📝 Checklist
You have to check all boxes before merging:
sendDefaultPII
is enabled.🔮 Next steps