Skip to content

Commit

Permalink
feat: allow profiling from hybrid SDKs (#3194)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew McKnight <[email protected]>
  • Loading branch information
vaind and armcknight committed Aug 10, 2023
1 parent 279841c commit 020745f
Show file tree
Hide file tree
Showing 17 changed files with 256 additions and 165 deletions.
27 changes: 27 additions & 0 deletions Sources/Sentry/PrivateSentrySDKOnly.m
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#import "PrivateSentrySDKOnly.h"
#import "SentryBreadcrumb+Private.h"
#import "SentryClient.h"
#import "SentryCurrentDateProvider.h"
#import "SentryDebugImageProvider.h"
#import "SentryExtraContextProvider.h"
#import "SentryHub+Private.h"
#import "SentryInstallation.h"
#import "SentryInternalDefines.h"
#import "SentryMeta.h"
#import "SentryProfiler.h"
#import "SentrySDK+Private.h"
#import "SentrySerialization.h"
#import "SentryUser+Private.h"
Expand Down Expand Up @@ -117,6 +120,30 @@ + (NSDictionary *)getExtraContext
return [[SentryExtraContextProvider sharedInstance] getExtraContext];
}

#if SENTRY_TARGET_PROFILING_SUPPORTED
+ (uint64_t)startProfilingForTrace:(SentryId *)traceId;
{
[SentryProfiler startWithTracer:traceId];
return SentryDependencyContainer.sharedInstance.dateProvider.systemTime;
}

+ (nullable NSDictionary<NSString *, id> *)collectProfileForTrace:(SentryId *)traceId
since:(uint64_t)startSystemTime;
{
NSMutableDictionary<NSString *, id> *payload = [SentryProfiler
collectProfileBetween:startSystemTime
and:SentryDependencyContainer.sharedInstance.dateProvider.systemTime
forTrace:traceId
onHub:[SentrySDK currentHub]];

if (payload != nil) {
payload[@"platform"] = SentryPlatformName;
}

return payload;
}
#endif // SENTRY_TARGET_PROFILING_SUPPORTED

#if SENTRY_HAS_UIKIT

+ (BOOL)framesTrackingMeasurementHybridSDKMode
Expand Down
13 changes: 6 additions & 7 deletions Sources/Sentry/Profiling/SentryProfiledTracerConcurrency.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
# import "SentryInternalDefines.h"
# import "SentryLog.h"
# import "SentryProfiler+Private.h"
# import "SentryTracer.h"
# include <mutex>

# if SENTRY_HAS_UIKIT
Expand Down Expand Up @@ -55,12 +54,12 @@
std::mutex _gStateLock;

void
trackProfilerForTracer(SentryProfiler *profiler, SentryTracer *tracer)
trackProfilerForTracer(SentryProfiler *profiler, SentryId *traceId)
{
std::lock_guard<std::mutex> l(_gStateLock);

const auto profilerKey = profiler.profileId.sentryIdString;
const auto tracerKey = tracer.traceId.sentryIdString;
const auto tracerKey = traceId.sentryIdString;

SENTRY_LOG_DEBUG(
@"Tracking relationship between profiler id %@ and tracer id %@", profilerKey, tracerKey);
Expand All @@ -83,14 +82,14 @@
}

void
discardProfilerForTracer(SentryTracer *tracer)
discardProfilerForTracer(SentryId *traceId)
{
std::lock_guard<std::mutex> l(_gStateLock);

SENTRY_CASSERT(_gTracersToProfilers != nil && _gProfilersToTracers != nil,
@"Structures should have already been initialized by the time they are being queried");

const auto tracerKey = tracer.traceId.sentryIdString;
const auto tracerKey = traceId.sentryIdString;
const auto profiler = _gTracersToProfilers[tracerKey];

if (profiler == nil) {
Expand All @@ -106,14 +105,14 @@
# endif // SENTRY_HAS_UIKIT
}

SentryProfiler *_Nullable profilerForFinishedTracer(SentryTracer *tracer)
SentryProfiler *_Nullable profilerForFinishedTracer(SentryId *traceId)
{
std::lock_guard<std::mutex> l(_gStateLock);

SENTRY_CASSERT(_gTracersToProfilers != nil && _gProfilersToTracers != nil,
@"Structures should have already been initialized by the time they are being queried");

const auto tracerKey = tracer.traceId.sentryIdString;
const auto tracerKey = traceId.sentryIdString;
const auto profiler = _gTracersToProfilers[tracerKey];

if (!SENTRY_CASSERT_RETURN(profiler != nil,
Expand Down
5 changes: 3 additions & 2 deletions Sources/Sentry/SentryEvent.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#import "SentryEvent+Private.h"
#import "SentryException.h"
#import "SentryId.h"
#import "SentryInternalDefines.h"
#import "SentryLevelMapper.h"
#import "SentryMessage.h"
#import "SentryMeta.h"
Expand All @@ -31,7 +32,7 @@ - (instancetype)initWithLevel:(enum SentryLevel)level
if (self) {
self.eventId = [[SentryId alloc] init];
self.level = level;
self.platform = @"cocoa";
self.platform = SentryPlatformName;
self.timestamp = [SentryDependencyContainer.sharedInstance.dateProvider date];
}
return self;
Expand All @@ -53,7 +54,7 @@ - (instancetype)initWithError:(NSError *)error
NSMutableDictionary *serializedData = @{
@"event_id" : self.eventId.sentryIdString,
@"timestamp" : @(self.timestamp.timeIntervalSince1970),
@"platform" : @"cocoa",
@"platform" : SentryPlatformName,
}
.mutableCopy;

Expand Down
21 changes: 11 additions & 10 deletions Sources/Sentry/SentryMetricProfiler.mm
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,22 @@ @implementation SentryMetricReading
*/
SentrySerializedMetricEntry *_Nullable serializeValuesWithNormalizedTime(
NSArray<SentryMetricReading *> *absoluteTimestampValues, NSString *unit,
SentryTransaction *transaction)
uint64_t startSystemTime, uint64_t endSystemTime)
{
const auto *timestampNormalizedValues = [NSMutableArray<SentrySerializedMetricReading *> array];
[absoluteTimestampValues enumerateObjectsUsingBlock:^(
SentryMetricReading *_Nonnull reading, NSUInteger idx, BOOL *_Nonnull stop) {
// if the metric reading wasn't recorded until the transaction ended, don't include it
if (!orderedChronologically(reading.absoluteTimestamp, transaction.endSystemTime)) {
if (!orderedChronologically(reading.absoluteTimestamp, endSystemTime)) {
return;
}

// if the metric reading was taken before the transaction started, don't include it
if (!orderedChronologically(transaction.startSystemTime, reading.absoluteTimestamp)) {
if (!orderedChronologically(startSystemTime, reading.absoluteTimestamp)) {
return;
}

const auto relativeTimestamp
= getDurationNs(transaction.startSystemTime, reading.absoluteTimestamp);
const auto relativeTimestamp = getDurationNs(startSystemTime, reading.absoluteTimestamp);

[timestampNormalizedValues addObject:@ {
@"elapsed_since_start_ns" : sentry_stringForUInt64(relativeTimestamp),
Expand Down Expand Up @@ -120,7 +119,8 @@ - (void)stop
[_dispatchSource cancel];
}

- (NSMutableDictionary<NSString *, id> *)serializeForTransaction:(SentryTransaction *)transaction
- (NSMutableDictionary<NSString *, id> *)serializeBetween:(uint64_t)startSystemTime
and:(uint64_t)endSystemTime;
{
NSArray<SentryMetricReading *> *memoryFootprint;
NSDictionary<NSNumber *, NSArray<SentryMetricReading *> *> *cpuUsage;
Expand All @@ -133,17 +133,18 @@ - (void)stop
const auto dict = [NSMutableDictionary<NSString *, id> dictionary];
if (memoryFootprint.count > 0) {
dict[kSentryMetricProfilerSerializationKeyMemoryFootprint]
= serializeValuesWithNormalizedTime(
memoryFootprint, kSentryMetricProfilerSerializationUnitBytes, transaction);
= serializeValuesWithNormalizedTime(memoryFootprint,
kSentryMetricProfilerSerializationUnitBytes, startSystemTime, endSystemTime);
}

[cpuUsage enumerateKeysAndObjectsUsingBlock:^(NSNumber *_Nonnull core,
NSArray<SentryMetricReading *> *_Nonnull readings, BOOL *_Nonnull stop) {
if (readings.count > 0) {
dict[[NSString stringWithFormat:kSentryMetricProfilerSerializationKeyCPUUsageFormat,
core.intValue]]
= serializeValuesWithNormalizedTime(
readings, kSentryMetricProfilerSerializationUnitPercentage, transaction);
= serializeValuesWithNormalizedTime(readings,
kSentryMetricProfilerSerializationUnitPercentage, startSystemTime,
endSystemTime);
}
}];

Expand Down
22 changes: 9 additions & 13 deletions Sources/Sentry/SentryProfileTimeseries.mm
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
void
logSlicingFailureWithArray(
NSArray<SentrySample *> *array, SentryTransaction *transaction, BOOL start)
NSArray<SentrySample *> *array, uint64_t startSystemTime, uint64_t endSystemTime, BOOL start)
{
if (!SENTRY_CASSERT_RETURN(
array.count > 0, @"Should not have attempted to slice an empty array.")) {
Expand All @@ -29,52 +29,48 @@

const auto firstSampleAbsoluteTime = array.firstObject.absoluteTimestamp;
const auto lastSampleAbsoluteTime = array.lastObject.absoluteTimestamp;
const auto firstSampleRelativeToTransactionStart
= firstSampleAbsoluteTime - transaction.startSystemTime;
const auto lastSampleRelativeToTransactionStart
= lastSampleAbsoluteTime - transaction.startSystemTime;
const auto firstSampleRelativeToTransactionStart = firstSampleAbsoluteTime - startSystemTime;
const auto lastSampleRelativeToTransactionStart = lastSampleAbsoluteTime - startSystemTime;
SENTRY_LOG_DEBUG(@"[slice %@] Could not find any%@ sample taken during the transaction "
@"(first sample taken at: %llu; last: %llu; transaction start: %llu; end: "
@"%llu; first sample relative to transaction start: %lld; last: %lld).",
start ? @"start" : @"end", start ? @"" : @" other", firstSampleAbsoluteTime,
lastSampleAbsoluteTime, transaction.startSystemTime, transaction.endSystemTime,
lastSampleAbsoluteTime, startSystemTime, endSystemTime,
firstSampleRelativeToTransactionStart, lastSampleRelativeToTransactionStart);
}

NSArray<SentrySample *> *_Nullable slicedProfileSamples(
NSArray<SentrySample *> *samples, SentryTransaction *transaction)
NSArray<SentrySample *> *samples, uint64_t startSystemTime, uint64_t endSystemTime)
{
if (samples.count == 0) {
return nil;
}

const auto transactionStart = transaction.startSystemTime;
const auto firstIndex =
[samples indexOfObjectWithOptions:NSEnumerationConcurrent
passingTest:^BOOL(SentrySample *_Nonnull sample, NSUInteger idx,
BOOL *_Nonnull stop) {
*stop = sample.absoluteTimestamp >= transactionStart;
*stop = sample.absoluteTimestamp >= startSystemTime;
return *stop;
}];

if (firstIndex == NSNotFound) {
logSlicingFailureWithArray(samples, transaction, /*start*/ YES);
logSlicingFailureWithArray(samples, startSystemTime, endSystemTime, /*start*/ YES);
return nil;
} else {
SENTRY_LOG_DEBUG(@"Found first slice sample at index %lu", firstIndex);
}

const auto transactionEnd = transaction.endSystemTime;
const auto lastIndex =
[samples indexOfObjectWithOptions:NSEnumerationConcurrent | NSEnumerationReverse
passingTest:^BOOL(SentrySample *_Nonnull sample, NSUInteger idx,
BOOL *_Nonnull stop) {
*stop = sample.absoluteTimestamp <= transactionEnd;
*stop = sample.absoluteTimestamp <= endSystemTime;
return *stop;
}];

if (lastIndex == NSNotFound) {
logSlicingFailureWithArray(samples, transaction, /*start*/ NO);
logSlicingFailureWithArray(samples, startSystemTime, endSystemTime, /*start*/ NO);
return nil;
} else {
SENTRY_LOG_DEBUG(@"Found last slice sample at index %lu", lastIndex);
Expand Down
Loading

0 comments on commit 020745f

Please sign in to comment.