Skip to content

Commit ced502e

Browse files
committed
fix: Use correct parsing for stackframes
1 parent ca1c55d commit ced502e

File tree

6 files changed

+130
-294
lines changed

6 files changed

+130
-294
lines changed

Sentry.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,8 @@
11021102
FA90FAA82E06614E008CAAE8 /* SentryExtraPackages.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA90FAA72E06614B008CAAE8 /* SentryExtraPackages.swift */; };
11031103
FA90FAFD2E070A3B008CAAE8 /* SentryURLRequestFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA90FAFC2E070A3B008CAAE8 /* SentryURLRequestFactory.swift */; };
11041104
FA914E6D2ECFD7D800C54BDD /* SentryFeedbackAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA914E6C2ECFD7D800C54BDD /* SentryFeedbackAPI.swift */; };
1105+
FA914E9B2ED61AA800C54BDD /* SentryFormatterSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = FA914E952ED61AA300C54BDD /* SentryFormatterSwift.h */; };
1106+
FA914E9E2ED61BA800C54BDD /* SentryFormatterSwift.m in Sources */ = {isa = PBXBuildFile; fileRef = FA914E9C2ED61AB900C54BDD /* SentryFormatterSwift.m */; };
11051107
FA94E6912E6B92C100576666 /* SentryClientReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA94E68B2E6B92BE00576666 /* SentryClientReport.swift */; };
11061108
FA94E6B22E6D265800576666 /* SentryEnvelope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA94E6B12E6D265500576666 /* SentryEnvelope.swift */; };
11071109
FA94E7242E6F339400576666 /* SentryEnvelopeItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA94E7232E6F32FA00576666 /* SentryEnvelopeItemType.swift */; };
@@ -2485,6 +2487,8 @@
24852487
FA90FAA72E06614B008CAAE8 /* SentryExtraPackages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryExtraPackages.swift; sourceTree = "<group>"; };
24862488
FA90FAFC2E070A3B008CAAE8 /* SentryURLRequestFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryURLRequestFactory.swift; sourceTree = "<group>"; };
24872489
FA914E6C2ECFD7D800C54BDD /* SentryFeedbackAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryFeedbackAPI.swift; sourceTree = "<group>"; };
2490+
FA914E952ED61AA300C54BDD /* SentryFormatterSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryFormatterSwift.h; path = include/SentryFormatterSwift.h; sourceTree = "<group>"; };
2491+
FA914E9C2ED61AB900C54BDD /* SentryFormatterSwift.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryFormatterSwift.m; sourceTree = "<group>"; };
24882492
FA94E68B2E6B92BE00576666 /* SentryClientReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryClientReport.swift; sourceTree = "<group>"; };
24892493
FA94E6B12E6D265500576666 /* SentryEnvelope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryEnvelope.swift; sourceTree = "<group>"; };
24902494
FA94E7232E6F32FA00576666 /* SentryEnvelopeItemType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryEnvelopeItemType.swift; sourceTree = "<group>"; };
@@ -2950,6 +2954,8 @@
29502954
639889D51EDF10BE00EA7442 /* Helper */ = {
29512955
isa = PBXGroup;
29522956
children = (
2957+
FA914E952ED61AA300C54BDD /* SentryFormatterSwift.h */,
2958+
FA914E9C2ED61AB900C54BDD /* SentryFormatterSwift.m */,
29532959
FA4FB8252ECB7D27008C9EC3 /* SentryLevel.h */,
29542960
63AA76951EB9C1C200D153DE /* SentryDefines.h */,
29552961
627E7588299F6FE40085504D /* SentryInternalDefines.h */,
@@ -5243,6 +5249,7 @@
52435249
FAB359982E05D7E90083D5E3 /* SentryEventSwiftHelper.h in Headers */,
52445250
7B31C291277B04A000337126 /* SentryCrashPlatformSpecificDefines.h in Headers */,
52455251
D452FC732DDB553100AFF56F /* SentryWatchdogTerminationBreadcrumbProcessor.h in Headers */,
5252+
FA914E9B2ED61AA800C54BDD /* SentryFormatterSwift.h in Headers */,
52465253
D456B4382D706BFE007068CB /* SentrySpanDataKey.h in Headers */,
52475254
7B77BE3527EC8445003C9020 /* SentryDiscardReasonMapper.h in Headers */,
52485255
7B610D602512390E00B0B5D9 /* SentrySDK+Private.h in Headers */,
@@ -5833,6 +5840,7 @@
58335840
D48891CC2E98F22A00212823 /* SentryInfoPlistWrapperProvider.swift in Sources */,
58345841
F458D1172E186DF20028273E /* SentryScopePersistentStore+Fingerprint.swift in Sources */,
58355842
D8CB7417294724CC00A5F964 /* SentryEnvelopeAttachmentHeader.m in Sources */,
5843+
FA914E9E2ED61BA800C54BDD /* SentryFormatterSwift.m in Sources */,
58365844
D84793262788737D00BE8E99 /* SentryByteCountFormatter.m in Sources */,
58375845
FAEFA12F2E4FAE1900C431D9 /* SentrySDKSettings.swift in Sources */,
58385846
63AA769E1EB9C57A00D153DE /* SentryError.mm in Sources */,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#import "SentryFormatter.h"
2+
3+
NSString *
4+
sentry_formatHexAddressUInt64Swift(uint64_t value)
5+
{
6+
return sentry_formatHexAddressUInt64(value);
7+
}

Sources/Sentry/SentryMetricKitIntegration.m

Lines changed: 3 additions & 275 deletions
Original file line numberDiff line numberDiff line change
@@ -204,187 +204,12 @@ - (void)didReceiveHangDiagnostic:(MXHangDiagnostic *)diagnostic
204204
- (void)captureMXEvent:(SentryMXCallStackTree *)callStackTree
205205
params:(SentryMXExceptionParams *)params
206206
diagnosticJSON:(NSData *)diagnosticJSON
207-
{
208-
// When receiving MXCrashDiagnostic the callStackPerThread was always true. In that case, the
209-
// MXCallStacks of the MXCallStackTree were individual threads, all belonging to the process
210-
// when the crash occurred. For MXCPUException, the callStackPerThread was always false. In that
211-
// case, the MXCallStacks stem from CPU-hungry multiple locations in the sample app during an
212-
// observation time of 90 seconds of one app run. It's a collection of stack traces that are
213-
// CPU-hungry.
214-
if (callStackTree.callStackPerThread) {
215-
SentryEvent *event = [self createEvent:params];
216-
217-
event.threads = [self convertToSentryThreads:callStackTree];
218-
219-
SentryThread *crashedThread = event.threads[0];
220-
crashedThread.crashed = @(!params.handled);
221-
222-
SentryException *exception = event.exceptions[0];
223-
exception.stacktrace = crashedThread.stacktrace;
224-
exception.threadId = crashedThread.threadId;
225-
226-
event.debugMeta = [self extractDebugMetaFromMXCallStacks:callStackTree.callStacks];
227-
228-
// The crash event can be way from the past. We don't want to impact the current session.
229-
// Therefore we don't call captureFatalEvent.
230-
[self captureEvent:event withDiagnosticJSON:diagnosticJSON];
231-
} else {
232-
for (SentryMXCallStack *callStack in callStackTree.callStacks) {
233-
[self buildAndCaptureMXEventFor:callStack.callStackRootFrames
234-
params:params
235-
diagnosticJSON:diagnosticJSON];
236-
}
237-
}
238-
}
239-
240-
/**
241-
* If @c callStackPerThread is @c NO , MetricKit organizes the stacktraces in a tree structure. See
242-
* https://developer.apple.com/videos/play/wwdc2020/10078/?time=224. The stacktrace consists of the
243-
* last sibling leaf frame plus its ancestors.
244-
*
245-
* The algorithm adds all frames to a list until it finds a leaf frame being the last sibling. Then
246-
* it reports that frame with its siblings and ancestors as a stacktrace.
247-
*
248-
* In the following example, the algorithm starts with frame 0, continues until frame 6, and reports
249-
* a stacktrace. Then it pops all sibling frames, goes back up to frame 3, and continues the search.
250-
*
251-
* It is worth noting that for the first stacktrace [0, 1, 3, 4, 5, 6] frame 2 is not included
252-
* because the logic only includes direct siblings and direct ancestors. Frame 3 is an ancestors of
253-
* [4,5,6], frame 1 of frame 3, but frame 2 is not a direct ancestors of [4,5,6]. It's the sibling
254-
* of the direct ancestor frame 3. Although this might seem a bit illogical, that is what
255-
* observations of MetricKit data unveiled.
256-
*
257-
* @code
258-
* | frame 0 |
259-
* | frame 1 |
260-
* | frame 2 |
261-
* | frame 3 |
262-
* | frame 4 |
263-
* | frame 5 |
264-
* | frame 6 | -> stack trace consists of [0, 1, 3, 4, 5, 6]
265-
* | frame 7 |
266-
* | frame 8 | -> stack trace consists of [0, 1, 2, 3, 7, 8]
267-
* | frame 9 | -> stack trace consists of [0, 1, 9]
268-
* | frame 10 |
269-
* | frame 11 |
270-
* | frame 12 |
271-
* | frame 13 | -> stack trace consists of [10, 11, 12, 13]
272-
* @endcode
273-
*
274-
* The above stacktrace turns into the following two trees.
275-
* @code
276-
* 0
277-
* |
278-
* 1
279-
* / \ \
280-
* 3 2 9
281-
* | |
282-
* 4 3
283-
* | |
284-
* 5 7
285-
* | |
286-
* 6 8
287-
*
288-
* 10
289-
* |
290-
* 11
291-
* |
292-
* 12
293-
* |
294-
* 13
295-
* @endcode
296-
*/
297-
- (void)buildAndCaptureMXEventFor:(NSArray<SentryMXFrame *> *)rootFrames
298-
params:(SentryMXExceptionParams *)params
299-
diagnosticJSON:(NSData *)diagnosticJSON
300-
{
301-
for (SentryMXFrame *rootFrame in rootFrames) {
302-
NSMutableArray<SentryMXFrame *> *stackTraceFrames = [NSMutableArray array];
303-
NSMutableSet<NSNumber *> *processedFrameAddresses = [NSMutableSet set];
304-
NSMutableDictionary<NSNumber *, SentryMXFrame *> *addressesToParentFrames =
305-
[NSMutableDictionary dictionary];
306-
307-
SentryMXFrame *currentFrame = rootFrame;
308-
[stackTraceFrames addObject:currentFrame];
309-
310-
while (stackTraceFrames.count > 0) {
311-
currentFrame = [stackTraceFrames lastObject];
312-
[processedFrameAddresses addObject:@(currentFrame.address)];
313-
314-
for (SentryMXFrame *subFrame in currentFrame.subFrames) {
315-
addressesToParentFrames[@(subFrame.address)] = currentFrame;
316-
}
317-
SentryMXFrame *parentFrame = addressesToParentFrames[@(currentFrame.address)];
318-
319-
SentryMXFrame *firstUnprocessedSibling =
320-
[self getFirstUnprocessedSubFrames:parentFrame.subFrames ?: @[]
321-
processedFrameAddresses:processedFrameAddresses];
322-
323-
BOOL lastUnprocessedSibling = firstUnprocessedSibling == nil;
324-
BOOL noChildren = currentFrame.subFrames.count == 0;
325-
326-
if (noChildren && lastUnprocessedSibling) {
327-
[self captureEventNotPerThread:stackTraceFrames
328-
params:params
329-
diagnosticJSON:diagnosticJSON];
330-
331-
if (parentFrame == nil) {
332-
// No parent frames
333-
[stackTraceFrames removeLastObject];
334-
} else {
335-
// Pop all sibling frames
336-
for (int i = 0; i < parentFrame.subFrames.count; i++) {
337-
[stackTraceFrames removeLastObject];
338-
}
339-
}
340-
} else {
341-
SentryMXFrame *nonProcessedSubFrame =
342-
[self getFirstUnprocessedSubFrames:currentFrame.subFrames ?: @[]
343-
processedFrameAddresses:processedFrameAddresses];
344-
345-
// Keep adding sub frames
346-
if (nonProcessedSubFrame != nil) {
347-
[stackTraceFrames addObject:nonProcessedSubFrame];
348-
} // Keep adding sibling frames
349-
else if (firstUnprocessedSibling != nil) {
350-
[stackTraceFrames addObject:firstUnprocessedSibling];
351-
} // Keep popping
352-
else {
353-
[stackTraceFrames removeLastObject];
354-
}
355-
}
356-
}
357-
}
358-
}
359-
360-
- (nullable SentryMXFrame *)getFirstUnprocessedSubFrames:(NSArray<SentryMXFrame *> *)subFrames
361-
processedFrameAddresses:
362-
(NSSet<NSNumber *> *)processedFrameAddresses
363-
{
364-
return [subFrames filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
365-
SentryMXFrame *frame,
366-
NSDictionary<NSString *, id> *bindings) {
367-
return ![processedFrameAddresses containsObject:@(frame.address)];
368-
}]].firstObject;
369-
}
370-
371-
- (void)captureEventNotPerThread:(NSArray<SentryMXFrame *> *)frames
372-
params:(SentryMXExceptionParams *)params
373-
diagnosticJSON:(NSData *)diagnosticJSON
374207
{
375208
SentryEvent *event = [self createEvent:params];
209+
[callStackTree prepareWithEvent:event inAppLogic:self.inAppLogic handled:params.handled];
376210

377-
SentryThread *thread = [[SentryThread alloc] initWithThreadId:@0];
378-
thread.crashed = @(!params.handled);
379-
thread.stacktrace = [self convertMXFramesToSentryStacktrace:frames.objectEnumerator];
380-
381-
SentryException *exception = event.exceptions[0];
382-
exception.stacktrace = thread.stacktrace;
383-
exception.threadId = thread.threadId;
384-
385-
event.threads = @[ thread ];
386-
event.debugMeta = [self extractDebugMetaFromMXFrames:frames];
387-
211+
// The crash event can be way from the past. We don't want to impact the current session.
212+
// Therefore we don't call captureFatalEvent.
388213
[self captureEvent:event withDiagnosticJSON:diagnosticJSON];
389214
}
390215

@@ -419,103 +244,6 @@ - (void)captureEvent:(SentryEvent *)event withDiagnosticJSON:(NSData *)diagnosti
419244
}
420245
}
421246

422-
- (NSArray<SentryThread *> *)convertToSentryThreads:(SentryMXCallStackTree *)callStackTree
423-
{
424-
NSUInteger i = 0;
425-
NSMutableArray<SentryThread *> *threads = [NSMutableArray array];
426-
for (SentryMXCallStack *callStack in callStackTree.callStacks) {
427-
NSEnumerator<SentryMXFrame *> *frameEnumerator
428-
= callStack.flattenedRootFrames.objectEnumerator;
429-
// The MXFrames are in reversed order when callStackPerThread is true. The Apple docs don't
430-
// state that. This is an assumption based on observing MetricKit data.
431-
if (callStackTree.callStackPerThread) {
432-
frameEnumerator = [callStack.flattenedRootFrames reverseObjectEnumerator];
433-
}
434-
435-
SentryStacktrace *stacktrace = [self convertMXFramesToSentryStacktrace:frameEnumerator];
436-
437-
SentryThread *thread = [[SentryThread alloc] initWithThreadId:@(i)];
438-
thread.stacktrace = stacktrace;
439-
440-
[threads addObject:thread];
441-
442-
i++;
443-
}
444-
445-
return threads;
446-
}
447-
448-
- (SentryStacktrace *)convertMXFramesToSentryStacktrace:(NSEnumerator<SentryMXFrame *> *)mxFrames
449-
{
450-
NSMutableArray<SentryFrame *> *frames = [NSMutableArray array];
451-
452-
for (SentryMXFrame *mxFrame in mxFrames) {
453-
SentryFrame *frame = [[SentryFrame alloc] init];
454-
frame.package = mxFrame.binaryName;
455-
frame.inApp = @([self.inAppLogic isInApp:mxFrame.binaryName]);
456-
frame.instructionAddress = sentry_formatHexAddressUInt64(mxFrame.address);
457-
uint64_t imageAddress = mxFrame.address - mxFrame.offsetIntoBinaryTextSegment;
458-
frame.imageAddress = sentry_formatHexAddressUInt64(imageAddress);
459-
460-
[frames addObject:frame];
461-
}
462-
463-
SentryStacktrace *stacktrace = [[SentryStacktrace alloc] initWithFrames:frames registers:@{}];
464-
465-
return stacktrace;
466-
}
467-
468-
/**
469-
* We must extract the debug images from the MetricKit stacktraces as the image addresses change
470-
* when you restart the app.
471-
*/
472-
- (NSArray<SentryDebugMeta *> *)extractDebugMetaFromMXCallStacks:
473-
(NSArray<SentryMXCallStack *> *)callStacks
474-
{
475-
NSMutableDictionary<NSString *, SentryDebugMeta *> *debugMetas =
476-
[NSMutableDictionary dictionary];
477-
for (SentryMXCallStack *callStack in callStacks) {
478-
479-
NSArray<SentryDebugMeta *> *callStackDebugMetas =
480-
[self extractDebugMetaFromMXFrames:callStack.flattenedRootFrames];
481-
482-
for (SentryDebugMeta *debugMeta in callStackDebugMetas) {
483-
if (debugMeta.debugID != nil) {
484-
debugMetas[SENTRY_UNWRAP_NULLABLE_VALUE(id<NSCopying>, debugMeta.debugID)]
485-
= debugMeta;
486-
}
487-
}
488-
}
489-
490-
return [debugMetas allValues];
491-
}
492-
493-
- (NSArray<SentryDebugMeta *> *)extractDebugMetaFromMXFrames:(NSArray<SentryMXFrame *> *)mxFrames
494-
{
495-
NSMutableDictionary<NSString *, SentryDebugMeta *> *debugMetas =
496-
[NSMutableDictionary dictionary];
497-
498-
for (SentryMXFrame *mxFrame in mxFrames) {
499-
500-
NSString *binaryUUID = [mxFrame.binaryUUID UUIDString];
501-
if (debugMetas[binaryUUID]) {
502-
continue;
503-
}
504-
505-
SentryDebugMeta *debugMeta = [[SentryDebugMeta alloc] init];
506-
debugMeta.type = SentryDebugImageType;
507-
debugMeta.debugID = binaryUUID;
508-
debugMeta.codeFile = mxFrame.binaryName;
509-
510-
uint64_t imageAddress = mxFrame.address - mxFrame.offsetIntoBinaryTextSegment;
511-
debugMeta.imageAddress = sentry_formatHexAddressUInt64(imageAddress);
512-
513-
debugMetas[binaryUUID] = debugMeta;
514-
}
515-
516-
return [debugMetas allValues];
517-
}
518-
519247
@end
520248

521249
NS_ASSUME_NONNULL_END
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
extern NSString *sentry_formatHexAddressUInt64Swift(uint64_t value);

Sources/Sentry/include/SentryPrivate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
#import "SentryDsn+Private.h"
2222
#import "SentryEnvelopeAttachmentHeader.h"
2323
#import "SentryEventSwiftHelper.h"
24+
#import "SentryFormatterSwift.h"
2425
#import "SentryHub+Private.h"
26+
#import "SentryInternalDefines.h"
2527
#import "SentryNSDataUtils.h"
2628
#import "SentrySDK+Private.h"
2729
#import "SentryTime.h"

0 commit comments

Comments
 (0)