Search code examples
swiftbreakpoints

Break on any occurrence of "fatal error: unexpectedly found nil while unwrapping an Optional value"


I have a huge code base and I want to break on the error "fatal error: unexpectedly found nil while unwrapping an Optional value"

What is the best approach to do that?

I tried adding both: (In the breakpoints sub-window)

  • "Add Swift Error Breakpoint"
  • "Add Exception Breakpoint"

But that didn't do it. Does that mean that the nil was in some framework code and not my code?


Solution

  • Unwrapping optionals gets translated by the compiler into calls to _getOptionalValue(), which calls _preconditionFailure() in case of anil value, which gets translated into a call to _fatalErrorMessage().

    So you need to set breakpoints for _fatalErrorMessage. The trick here is that you need to set the breakpoints for the mangled name of the function, which is _TTSf4s_s_d_d___TFSs18_fatalErrorMessageFTVSs12StaticStringS_S_Su_T_.

    If you add a symbolic breakpoint for this, the breakpoint will be hit everytime a fatal error occurs. This means you'll catch all nil unwraps, however you'll also catch some other pieces of code that trigger fatal errors. You should be able to set some conditions on the arguments of the function so that your breakpoint is hit only when the unexpectedly found nil while unwrapping an Optional value is passed, however I personally didn't played with this feature of lldb so I'm not sure how you need to set this condition in the breakpoint editor.


    Update. In Swift 3 (Xcode 8 beta 6), the breakpoint should be set for _TTSfq4n_n_d_d_n___TFs18_fatalErrorMessageFTVs12StaticStringS_S_Su5flagsVs6UInt32_Os5Never

    Here's how you can find out for your own the mangled function name for any Swift version, in order to set a breakpoint to:

    1. Find you build app location (can be easily found in Xcode Navigator -> Products -> Your App Name.app)
    2. List all your global symbols for the libswiftCore.dylib embeded within your app bundle, grep by fatalError. So for example if your build app is at /Users/your.username/Library/Developer/Xcode/DerivedData/YourApp-randomchars/Build/Products/Debug-iphonesimulator/YourApp.app/, you should run

      nm -g /Users/your.username/Library/Developer/Xcode/DerivedData/YourApp-randomchars/Build/Products/Debug-iphonesimulator/YourApp.app//Frameworks/libswiftCore.dylib | grep fatalError
      
    3. Check the output and grab the mangled method names. There could be more than one symbol, if the function is overloaded. A sample output looks like this:

      0000000000032f50 T __TFs18_fatalErrorMessageFTVs12StaticStringS_S_Su5flagsVs6UInt32_Os5Never
      00000000001663b0 T __TTSfq4n_n_d_d_n___TFs18_fatalErrorMessageFTVs12StaticStringS_S_Su5flagsVs6UInt32_Os5Never
      
    4. Make sure you remove the leading underscore before adding the breakpoint, that one comes from the C name mangling (so for the above output you should remain with only one leading underscore for the two symbols).

    5. Add the breakpoints and force unwrap a nil value, to make sure they work.

    Happy nil-unwrapping hunting :)