@@ -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
521249NS_ASSUME_NONNULL_END
0 commit comments