Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to identify the cause of my SIGTrap Crash?

My team recently launched an app for which there's been quite a lot of SIGTRAP Crashes. Previously I've found these relatively simple to fix as it was a matter of finding a poor force cast or an implicitly unwrapped optional being set to nil in the offending function. This time though I can't find anything of the sort. My best guess is that maybe one of the TimeBlock objects or its properties is nil due to a calendar error.

Our app is a meeting organizer that shows the user their native iOS calendar events in terms of FreeTime, Conflicts and Meeting TimeBlocks. I have access to the calendars of several users who have crashed.

Apple SigTrap Definition

Swift code will terminate with this exception type if an unexpected condition is encountered at runtime such as:

  • a non-optional type with a nil value
  • a failed forced type conversion

Crashing Function

    /**
    Updates the conflictHours and meetingHours according to the timeblocks
    it is used as quick light reference to the button
 */
func updateTimeHours(timeblocks : [Timeblock]) {
    for timeblock in timeblocks {
        switch timeblock {
        case is MeetingTimeblock:
            for i in timeblock.startHour...timeblock.endHour {
                self.meetingHours[i] = true
            }
            break
        case is ConflictTimeblock:
            for i in timeblock.startHour...timeblock.endHour {
                self.conflictsHours[i] = true
            }
            break
        default: break
        }
    }
    updateButtonByOffset(offset: self.scrollTimeline.contentOffset.x)
}

Call of Crashing Function

    /**
    This function inits the variables and button layout according to the timeblocks
 */
func handleTimeblocksDependantComponents() {
    buttonLayout()
    guard Scheduler.sharedInstance.timelines.count > SharedGlobals.Calendar.TODAY_INDEX else {
        return
    }
    updateTimeHours(timeblocks : (Scheduler.sharedInstance.timelines[SharedGlobals.Calendar.TODAY_INDEX].timeblocks))
}

SetHeaderHeight

/**
 Adjusts the height of the header depending on whether there are hosted meetings or 
 meeting VIP's or not.
*/
private func setHeaderHeight() {
    self.tableView.tableHeaderView = self.headerView
    let hostedMeetings = OverviewInteractor.getHostedMeetings(dayIndex: SharedGlobals.Calendar.SELECTED_DAY)
    let vips = OverviewInteractor.getVIPS(dayIndex: SharedGlobals.Calendar.SELECTED_DAY)

    self.tableView.beginUpdates()
    if let headerView = self.tableView.tableHeaderView {
        var height = 360.0
        if(vips.count == 0) { height -= 80.0 }
        if(hostedMeetings.count == 0) { height -= 80.0 }
        headerView.frame.size.height = CGFloat(height)
    }
    self.tableView.endUpdates()

}

TimeBlock Definition

// The Timeblock parent class. It simply holds a start and end time and provides its own duration. Not to be used as such
public class Timeblock {
    public let startTime: Date
    public let endTime: Date

    /// Returns the hour the Timeblock starts, in current timezone
    public var startHour: Int {
        get {
            return Calendar.current.component(.hour, from: startTime)
        }
    }

    /// Returns the hour the Timeblocks ends, in current timezone
    public var endHour: Int {
        get {
            return Calendar.current.component(.hour, from: endTime)
        }
    }

    /// Returns the minutes the Timeblocks starts
    public var startMinutes: Int {
        get {
            return Calendar.current.component(.minute, from: startTime)
        }
    }

    /// Returns the minutes the Timeblocks ends
    public var endMinutes: Int {
        get {
            return Calendar.current.component(.minute, from: endTime)
        }
    }

    /**
        Initialises the instance with a start and end time
        - Parameters:
            - startTime: The start time of the timeblock
            - endTime: The end time of the timeblock
    */
    public init(startTime: Date, endTime: Date) {
        self.startTime = startTime
        self.endTime = endTime
    }

    /**
        Provides the Timeblock's duration in the form of a DateInterval
        - warning: Only available on iOS 10.0 and up
        - returns: A DateInterval of the duration of the Timeblock
    */
    @available(iOS 10.0, *)
    public func getTimeInterval() -> DateInterval {
        return DateInterval(start: self.startTime, end: self.endTime)
    }

    /**
        Provides the Timeblock's duration in the form of a `Double` (number of seconds)

        - returns: The number of seconds that this Timeblock goes on for
    */
    public func getDuration() -> Double {
        return self.endTime.timeIntervalSince(self.startTime)
    }

}

Crash Report

Incident Identifier: 98D4F477-C57B-4767-B957-E9EA2E0EE3EA CrashReporter Key: 0000000000000000000000000000000000000000 Hardware Model: undefined Process: xxxxxxx [784] Identifier:
com.xxx.xxx.xx.xxxxxxx Version: 4.0.3 Code Type:
arm64

Date/Time: Sun Dec 24 2017 09:55:23 GMT+0000 (GMT) Launch Time: Invalid Date OS Version: undefined 11.0.3 (15A432) Report Version: 105

Exception Type: SIGTRAP Exception Subtype: undefined

Thread 0 name: Thread 0 Crashed: 0 CallIn
0x0000000102c224e4 specialized TimelineHeader.updateTimeHours(timeblocks:) (TimelineHeader.swift:0) 1 CallIn 0x0000000102c20af0 TimelineHeader.handleTimeblocksDependantComponents() (TimelineHeader.swift:0) 2 CallIn
0x0000000102c7a28c specialized MeetingTableViewController.tableView(:viewForHeaderInSection:) (TimelineHeader.swift:78) 3 CallIn
0x0000000102c75d54 @objc MeetingTableViewController.tableView(
:viewForHeaderInSection:) (MeetingTableViewController.swift:0) 4 UIKit
0x000000018d1157d8 -[UITableView _delegateViewForHeaderInSection:] (UIKit) 5 UIKit 0x000000018d11def0 96-[UITableView _sectionHeaderView:withFrame:forSection:floating:reuseViewIfPossible:willDisplay:]_block_invoke (UIKit) 6 UIKit 0x000000018cdf1a14 +[UIView(Animation) performWithoutAnimation:] (UIKit) 7 UIKit 0x000000018d11dc60 -[UITableView _sectionHeaderView:withFrame:forSection:floating:reuseViewIfPossible:willDisplay:] (UIKit) 8 UIKit 0x000000018cfc6c04 -[_UITableViewUpdateSupport(Private) _setupAnimationsForExistingHeadersAndFooters] (UIKit) 9 UIKit 0x000000018cfc1070 -[_UITableViewUpdateSupport _setupAnimations] (UIKit) 10 UIKit 0x000000018cfc0944 -[UITableView _updateWithItems:updateSupport:] (UIKit) 11 UIKit 0x000000018cfa8448 -[UITableView endCellAnimationsWithContext:] (UIKit) 12 UIKit 0x000000018cfa46e4 -[UITableView endUpdates] (UIKit) 13 CallIn 0x0000000102c761a4 MeetingTableViewController.setHeaderHeight() (MeetingTableViewController.swift:398) 14 CallIn
0x0000000102c7a7c8 specialized closure #1 in MeetingTableViewController.refreshTable(
:) (MeetingTableViewController.swift:513) 15 CallIn
0x0000000102c7aacc partial apply for closure #1 in MeetingTableViewController.refreshTable(_:) (MeetingTableViewController.swift:0) 16 CallIn
0x0000000102cd17f0 thunk for @callee_owned () -> () (LoginPageViewController.swift:0) 17 libdispatch.dylib
0x000000018327ea54 _dispatch_call_block_and_release (libdispatch.dylib) 18 libdispatch.dylib
0x000000018327ea14 _dispatch_client_callout (libdispatch.dylib) 19 libdispatch.dylib 0x000000018328b698 _dispatch_main_queue_callback_4CF$VARIANT$mp (libdispatch.dylib) 20 CoreFoundation 0x00000001838aa544 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
(CoreFoundation) 21 CoreFoundation 0x00000001838a8120 __CFRunLoopRun (CoreFoundation) 22 CoreFoundation
0x00000001837c7e58 CFRunLoopRunSpecific (CoreFoundation) 23 GraphicsServices 0x0000000185674f84 GSEventRunModal (GraphicsServices) 24 UIKit
0x000000018ce4767c UIApplicationMain (UIKit) 25 CallIn
0x0000000102c02c08 main (AppDelegate.swift:18) 26 libdyld.dylib
0x00000001832e456c start (libdyld.dylib)

like image 289
Declan McKenna Avatar asked Dec 27 '17 11:12

Declan McKenna


Video Answer


1 Answers

Reproduction

This was caused by the index being messed up by Timeblock.startHour and Timeblock.endHour being Int values. After looking at our users calendars I noticed all of them had meetings that ended at midnight.

So for a meeting that went on between 5PM to Midnight we'd get the following happening.

for i in timeblock.startHour...timeblock.endHour became for i in 0...17

This would give quite a precise error telling me I couldn't iterate backwards once I reproduced the crash which unfortunately didn't appear in the crash report which was a bit misleading.

Fix

We had capped the times that create the timeblock.endHour property so > 0:00 of the next day was changed to 23:59 to prevent this. The fix was simply applying the cap to >= 0:00 so a meeting ending at midnight isn’t treated as a multi-day meeting.

A future refactor will likely be to set the end hour for all times that end on the hour to the previous hour as technically if your meeting ends at 11:00am you still have the hour free. I'd also like to handle this all within the Timeblock object rather than filter the argument used to create it.

like image 61
Declan McKenna Avatar answered Oct 01 '22 16:10

Declan McKenna