Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions Sources/Sentry/SentryProfiler.mm
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#import "SentryProfiler.h"

#if SENTRY_TARGET_PROFILING_SUPPORTED
# import "NSDate+SentryExtras.h"
# import "SentryBacktrace.hpp"
# import "SentryClient+Private.h"
# import "SentryCurrentDate.h"
# import "SentryDebugImageProvider.h"
# import "SentryDebugMeta.h"
# import "SentryDefines.h"
Expand Down Expand Up @@ -356,9 +358,9 @@ - (void)start
const auto queueMetadata = [NSMutableDictionary<NSString *, NSDictionary *> dictionary];
sampledProfile[@"thread_metadata"] = threadMetadata;
sampledProfile[@"queue_metadata"] = queueMetadata;
_profile[@"sampled_profile"] = sampledProfile;
_profile[@"profile"] = sampledProfile;
_startTimestamp = getAbsoluteTime();
_startDate = [NSDate date];
_startDate = [SentryCurrentDate date];

SENTRY_LOG_DEBUG(@"Starting profiler %@ at system time %llu.", self, _startTimestamp);

Expand All @@ -378,9 +380,6 @@ - (void)start
NSMutableDictionary<NSString *, id> *metadata = threadMetadata[threadID];
if (metadata == nil) {
metadata = [NSMutableDictionary<NSString *, id> dictionary];
if (backtrace.threadMetadata.threadID == mainThreadID) {
metadata[@"is_main_thread"] = @YES;
}
threadMetadata[threadID] = metadata;
}
if (!backtrace.threadMetadata.name.empty() && metadata[@"name"] == nil) {
Expand Down Expand Up @@ -427,7 +426,7 @@ - (void)start
}

const auto sample = [NSMutableDictionary<NSString *, id> dictionary];
sample[@"relative_timestamp_ns"] =
sample[@"elapsed_since_start_ns"] =
[@(getDurationNs(strongSelf->_startTimestamp, backtrace.absoluteTimestamp))
stringValue];
sample[@"thread_id"] = threadID;
Expand Down Expand Up @@ -475,7 +474,7 @@ - (void)stop

_profiler->stopSampling();
_endTimestamp = getAbsoluteTime();
_endDate = [NSDate date];
_endDate = [SentryCurrentDate date];
SENTRY_LOG_DEBUG(@"Stopped profiler %@ at system time: %llu.", self, _endTimestamp);
}
}
Expand All @@ -486,6 +485,7 @@ - (void)captureEnvelope
@synchronized(self) {
profile = [_profile mutableCopy];
}
profile[@"version"] = @"1";
const auto debugImages = [NSMutableArray<NSDictionary<NSString *, id> *> new];
const auto debugMeta = [_debugImageProvider getDebugImages];
for (SentryDebugMeta *debugImage in debugMeta) {
Expand Down Expand Up @@ -522,6 +522,9 @@ - (void)captureEnvelope
const auto profileDuration = getDurationNs(_startTimestamp, _endTimestamp);
profile[@"duration_ns"] = [@(profileDuration) stringValue];
profile[@"truncation_reason"] = profilerTruncationReasonName(_truncationReason);
profile[@"platform"] = _transactions.firstObject.platform;
profile[@"environment"] = _hub.scope.environmentString ?: _hub.getClient.options.environment ?: kSentryDefaultEnvironment;
profile[@"timestamp"] = [[SentryCurrentDate date] sentry_toIso8601String];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please confirm that a string such as 2022-11-02T21:16:20.877Z would validate? @phacops

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would.


const auto bundle = NSBundle.mainBundle;
profile[@"release"] =
Expand Down Expand Up @@ -569,8 +572,8 @@ - (void)captureEnvelope
# endif // SENTRY_HAS_UIKIT

// populate info from all transactions that occurred while profiler was running
profile[@"platform"] = _transactions.firstObject.platform;
auto transactionsInfo = [NSMutableArray array];
NSString *mainThreadID = [profile[@"profile"][@"samples"] firstObject][@"thread_id"];
for (SentryTransaction *transaction in _transactions) {
const auto relativeStart =
[NSString stringWithFormat:@"%llu",
Expand All @@ -585,12 +588,15 @@ - (void)captureEnvelope
: (unsigned long long)(
[transaction.timestamp timeIntervalSinceDate:_startDate] * 1e9)];
[transactionsInfo addObject:@{
@"environment" : _hub.scope.environmentString ?: _hub.getClient.options.environment ?: kSentryDefaultEnvironment,
@"id" : transaction.eventId.sentryIdString,
@"trace_id" : transaction.trace.context.traceId.sentryIdString,
@"name" : transaction.transaction,
@"relative_start_ns" : relativeStart,
@"relative_end_ns" : relativeEnd
@"relative_end_ns" : relativeEnd,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a TODO somewhere to make sure the relative_end_ns is correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just filed an issue here for this, thanks for reminding me! #2352

@"active_thread_id" :
mainThreadID // TODO: we are just using the main thread ID for all transactions to
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO captures in #2356

// fix a backend validation error, but this needs to be gathered from
// transaction starts in their contexts and carried forward to here
}];
}
profile[@"transactions"] = transactionsInfo;
Expand Down
2 changes: 1 addition & 1 deletion Sources/Sentry/include/NSDate+SentryExtras.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface
NSDate (SentryExtras)

+ (NSDate *)sentry_fromIso8601String:(NSString *)string;
+ (NSDate *)sentry_fromIso8601String:(NSString *)string NS_SWIFT_NAME(sentry_from(iso8601String:));

- (NSString *)sentry_toIso8601String;

Expand Down
17 changes: 14 additions & 3 deletions Tests/SentryTests/Profiling/SentryProfilerSwiftTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ private extension SentryProfilerSwiftTests {

func assertValidProfileData(data: Data, transactionEnvironment: String = kSentryDefaultEnvironment, numberOfTransactions: Int = 1, shouldTimeout: Bool = false) {
let profile = try! JSONSerialization.jsonObject(with: data) as! [String: Any]

XCTAssertNotNil(profile["version"])
if let timestampString = profile["timestamp"] as? String {
XCTAssertNotNil(NSDate.sentry_from(iso8601String: timestampString))
} else {
XCTFail("Expected a top-level timestamp")
}

let device = profile["device"] as? [String: Any?]
XCTAssertNotNil(device)
XCTAssertEqual("Apple", device!["manufacturer"] as! String)
Expand All @@ -259,6 +267,8 @@ private extension SentryProfilerSwiftTests {

XCTAssertEqual("cocoa", profile["platform"] as! String)

XCTAssertEqual(transactionEnvironment, profile["environment"] as! String)

let version = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) ?? "(null)"
let build = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") ?? "(null)"
let releaseString = "\(version) (\(build))"
Expand All @@ -275,12 +285,11 @@ private extension SentryProfilerSwiftTests {
XCTAssertGreaterThan((firstImage["image_size"] as! Int), 0)
XCTAssertEqual(firstImage["type"] as! String, "macho")

let sampledProfile = profile["sampled_profile"] as! [String: Any]
let sampledProfile = profile["profile"] as! [String: Any]
let threadMetadata = sampledProfile["thread_metadata"] as! [String: [String: Any]]
let queueMetadata = sampledProfile["queue_metadata"] as! [String: Any]
XCTAssertFalse(threadMetadata.isEmpty)
XCTAssertFalse(threadMetadata.values.compactMap { $0["priority"] }.filter { ($0 as! Int) > 0 }.isEmpty)
XCTAssertFalse(threadMetadata.values.filter { $0["is_main_thread"] as? Bool == true }.isEmpty)
XCTAssertFalse(queueMetadata.isEmpty)
XCTAssertFalse(((queueMetadata.first?.value as! [String: Any])["label"] as! String).isEmpty)

Expand Down Expand Up @@ -316,13 +325,15 @@ private extension SentryProfilerSwiftTests {
if let traceIDString = transaction["trace_id"] {
XCTAssertNotEqual(SentryId.empty, SentryId(uuidString: traceIDString))
}
XCTAssertEqual(transactionEnvironment, transaction["environment"])
XCTAssertNotNil(transaction["trace_id"])
XCTAssertNotNil(transaction["relative_start_ns"])
XCTAssertNotNil(transaction["relative_end_ns"])
XCTAssertNotNil(transaction["active_thread_id"])
}

for sample in samples {
XCTAssertNotNil(sample["elapsed_since_start_ns"] as! String)
XCTAssertNotNil(sample["thread_id"])
XCTAssertNotNil(stacks[sample["stack_id"] as! Int])
}

Expand Down