0

I am trying to get simple crash detection working. Ten years ago was that no problem with Obj-C thanks to the UncaughtExceptionHandler. But Swift needs more work and I am not sure I am on the right path.

I tried the following implementation using signals, based on other approaches and comments. To my surprise my handlers seem not to get called. Any idea what I am missing or doing wrong?

import Foundation

class CrashDetection: NSObject {
    private static let observedSignals = [SIGABRT, SIGILL, SIGSEGV, SIGFPE, SIGBUS, SIGPIPE, SIGTRAP]
    private static var previousSignalHandlers: [Int32:(@convention(c) (Int32) -> Void)] = [:]

    @objc class func start() {
        NSSetUncaughtExceptionHandler(CrashDetection.recieveException)
        observedSignals.forEach { signalType in
            let oldHandler = signal(signalType, CrashDetection.recieveSignal)
            CrashDetection.previousSignalHandlers[signalType] = oldHandler
        }
    }

    private static let recieveException: @convention(c) (NSException) -> Swift.Void = {
        (recieveException) -> Void in
        UserDefaults.standard.setValue(true, forKey: "crashDidOccur")
    }

    private static let recieveSignal: @convention(c) (Int32) -> Void = {
        (recievedSignal) -> Void in
        UserDefaults.standard.setValue(true, forKey: "crashDidOccur")
        NSSetUncaughtExceptionHandler(nil)
        observedSignals.forEach { signalType in
            signal(signalType, previousSignalHandlers[signalType])
        }
    }

}
Helge Becker
  • 3,219
  • 1
  • 20
  • 33
  • I found this question and answer from 2016 https://stackoverflow.com/questions/38737880/uncaught-error-exception-handling-in-swift I think it still applies, but I'm not going to vote to close as a duplicate because I've already written an answer and, in any case, the given answer uses non-async signal safe functions. – JeremyP Jul 19 '23 at 12:44

1 Answers1

0

I don't have an answer for you, but this comment needs more space than is available in comments.

I am not at all surprised that this isn't working for you. There are a number of problems with using signal handlers from Swift.

  1. I would be extremely surprised if the code that is executed in UserDefaults.standard.setValue(:, forKey:) is async signal safe. If the key doesn't already exist, you will definitely be doing memory allocation, which means you risk corrupting the heap.

  2. SIG_IGN and SIG_DFL are defined thus:

     #define SIG_DFL         (void (*)(int))0
     #define SIG_IGN         (void (*)(int))1
    

    They are not real functions. If the current signal handler is either one of those, you can't just call it.

  3. On modern systems, the result of doing anything in a signal handler may not be immediately apparent outside of it. I assume this is because signals aren't necessarily handled on the same core and there are caching issues. I found the only way to reliably change a variable in a signal handler so the outside world could see it was to use an atomic operation.

I did once write a signal handling API for Swift so that I could detect ctrlc mainly. All the low level work was done in C and it still took me ages to get it to work (mainly because of the memory barrier problem).

You may have some luck using DispatchSourceSignal although I'd be doubtful if that will work with segmentation violations.

JeremyP
  • 84,577
  • 15
  • 123
  • 161