How to intercept EXC_BAD_INSTRUCTION when unwrapping Nil - ios

I am building some rudimentary crash logging system based on this blog post (Yes, I am aware of PLCRashReporter, and No, I can't use it and need to roll my own, however limited. Thank you).
My code registers both an exception handler (with NSSetUncaughtExceptionHandler()) and a signal handler for most signals:
SIGQUIT
SIGILL
SIGTRAP
SIGABRT
SIGEMT
SIGFPE
SIGBUS
SIGSEGV
SIGSYS
SIGPIPE
SIGALRM
SIGXCPU
SIGXFSZ
It seems to work fine with basic Objective-C stuff such as "unrecognized selector sent to instance...", etc.
Next, I tried it with the following Swift code:
var empty:String! = nil
let index = empty.startIndex // < KA-BOOM!
...but the thrown exception EXC_BAD_INSTRUCTION is not caught (my handler is not called), and instead I get the familiar:
fatal error: unexpectedly found nil while unwrapping an Optional value
...in the Xcode console.
What am I missing?
This answer says that this kind of runtime error can be handled with SIGTRAP; however I am already handling that signal and it doesn't seem to work.
ADDENDUM: It seems there is no centralized exception handling system in place for Swift runtime errors (out of bounds, nil unwrap, etc.); see this post. However, Xcode is still able to crash with EXC_BAD_INSTRUCTION, so my question stands: How is this possible, and why can't my signal handlers handle this?
ADDENDUM 2: Just to be safe, I tried registering my signal handler for all 20+ signals defined in signal.h (not just the ones listed above); still, no change.
ADDENDUM 3: Apple's official document Understanding and Analyzing Application Crash Reports contains this snippet:
Trace Trap [EXC_BREAKPOINT // SIGTRAP]
Similar to an Abnormal Exit, this exception is intended to give an
attached debugger the chance to interrupt the process at a specific
point in its execution. You can trigger this exception from your own
code using the __builtin_trap() function. If no debugger is attached,
the process is terminated and a crash report is generated.
Lower-level libraries (e.g, libdispatch) will trap the process upon
encountering a fatal error. Additional information about the error can
be found in the Additional Diagnostic Information section of the crash
report, or in the device's console.
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
Look at the Backtraces to determine where the unexpected condition was
encountered. Additional information may have also been logged to the
device's console. You should modify the code at the crashing location
to gracefully handle the runtime failure. For example, use Optional
Binding instead of force unwrapping an optional.
(emphasis mine)
Still: Why Can't I Handle This With My SIGTRAP Signal Handler?

TL;DR: Signal handler works fine when debugger is not attached.
(We can even catch fatal_error's SIGTRAP signal, and log stack-trace)
In order to see what was happening, I set up a small demo project where I registered a signal handler for SIGUSR1 (one of the 'user-defined' signals available) and subsequently sent said signal to my process using the kill() function:
kill(0, SIGUSR1);
At first, I wasn't able to have Xcode's debugger jump to my signal handler, and instead it would stop at the line with the kill() call, highlighting it in red just as in my question.
After reading this question and its answers, it struck me that the debugger must pretty much depend on signals in order to be able to stop/resume program execution e.g., at breakpoints.
Using the symbolic breakpoint specified in one of the answers:
process handle SIGUSR1 -n true -p true -s false
...I was able to make Xcode not stop on this command:
kill(pid, SIGUSR1);
...and instead jump to my signal handler, all while debugging. I could even step inside the signal handler and execute its statements one by one; display an alert view from within it, etc.
Although I am not able to achieve the same result with SIGTRAP (or even SIGABRT), my signal handler does get called (and my crash log does get saved to disk!) when I launch my app without a debugger attached (e.g., by tapping the app's icon on the home screen) and have it throw a Swift runtime error like the one I mentioned in my question.
My guess on why I was able to intercept unrecognized selector sent to instance... seems to be that, as an Objective-C exception, it is an entirely different beast from Unix signals, and has nothing to do with the workings of the debugger (unless you set up an exception breakpoint for "Objective-C exceptions"?).
The very fact that the handler is set with the unrelated function NSSetUncaughtExceptionHandler() should have been a hint...
(I'm pretty much talking out of my limited knowledge here, so please feel free to add answers/comments with corrections as you see fit)

Related

Delphi effective way to handle socket error [duplicate]

I've set Application.OnException to a custom exception handler so that I can log crashes and give an option to exit. However, I'm now finding that this runs even on exceptions that I've already handled, for instance, exceptions that come up when validating number inputs. Is there a way to have the custom exception handler only run on unhandled exceptions?
Edit: it turns out that I get the expected behavior when I run outside the debugger. Maybe it's just a debugger thing. I do not find the Delphi debugger's interaction with exceptions to be intuitive, to say the least.
If behavior changes inside and outside the debugger, then it's not really your program that's telling you about exceptions. I've written about this phenomenon on my Web site:
Why do I continue getting error messages even after I have written an exception handler?
An excerpt:
In its default settings, the Delphi IDE notifies you whenever an exception occurs in your program ... . What’s important to realize is that at that point, none of your program’s exception-handling code has run yet. It’s all Delphi itself; its special status as a debugger allows it to get first notification of any exception in your program, even before your program knows about it.
After you dismiss Delphi’s message box, execution will be paused at the best line of your source code Delphi could find to represent the source of the exception. Press the “Run” button to have your program resume. Control will pass to the next finally or except
block. Before you resume your program, you may use the various debugging tools at your disposal. You can inspect the values of any variables in scope, and you can even modify their values.
So, how do you notify Delphi that you've already handled an exception? You don't — because your program hasn't handled it yet. And why can't the debugger detect whether your program is going to handle an exception? Because in order to do that, it needs to execute your program further. Detecting the lack of an exception handler is like solving the halting problem. The only way to determine whether an exception is going to be handled is to let the program run and then see whether the exception gets handled. But by that point, it's too late to do any debugging, so the debugger has no choice but to pause your program when it first detects an exception and then let you figure out what to do from there.
My article goes on to describe some ways you can avoid having the debugger catch certain exceptions, summarized here:
Use advanced breakpoints to temporarily disable breaking on exceptions for certain regions of the code.
Configure the debugger to ignore certain classes of exceptions (and their descendants).
Tell the debugger not to notify you about any exceptions.
Disable integrated debugging altogether.
There's another options that I didn't include in my article:
Change your program such that the exception doesn't get raised in the first place.
You say you're validating numeric input. That sounds to me like you're doing something like calling StrToInt and then catching the EConvertError exception when the input isn't a valid integer. That's an expensive way of validating input. Instead of that, use TryStrToInt, which will tell you whether the conversion succeeded, or StrToIntDef, which will silently return a default value instead of raising an exception. Another option is plain old Val, which tries to convert a string, and if it fails, it tells you which position in the string caused the failure. Val is particularly useful if you want to consume as many characters as possible for the conversion and then resume parsing at the next non-numeric character.
Quoting from the documentation (Delphi 7) on TApplication.OnException
"Use OnException to change the default behavior that occurs when an exception is not
handled by application code."
So: Only unhandled exception will be available in the OnException event handler. What you are experiencing is probably the Delphi IDE breaking on the exception. This is (at least in Delphi 7) configurable.
In Delphi 7 you can configure it by clicking on the Tools->Debugger Options menu. Then select the "Language Exceptions" an un-ckech the "Stop on Delphi exceptions" checkbox. It might however be different in other delphi versions.
An alternative might be to not use Application.OnException. But to simply catch all exceptions in your "main" function. That way you can catch all exceptions you have not cached before, log the exception, and then crash.
Application.OnException should only fire for unhandled exceptions. Re-raising an exception in a try-except block will cause the exception to be handled by Application.OnException. For input validation that raises an exception, you could display a message to the user and then re-raise the exception only if you want it recorded into the error log.

Try - except not displaying Exception error message [duplicate]

I've set Application.OnException to a custom exception handler so that I can log crashes and give an option to exit. However, I'm now finding that this runs even on exceptions that I've already handled, for instance, exceptions that come up when validating number inputs. Is there a way to have the custom exception handler only run on unhandled exceptions?
Edit: it turns out that I get the expected behavior when I run outside the debugger. Maybe it's just a debugger thing. I do not find the Delphi debugger's interaction with exceptions to be intuitive, to say the least.
If behavior changes inside and outside the debugger, then it's not really your program that's telling you about exceptions. I've written about this phenomenon on my Web site:
Why do I continue getting error messages even after I have written an exception handler?
An excerpt:
In its default settings, the Delphi IDE notifies you whenever an exception occurs in your program ... . What’s important to realize is that at that point, none of your program’s exception-handling code has run yet. It’s all Delphi itself; its special status as a debugger allows it to get first notification of any exception in your program, even before your program knows about it.
After you dismiss Delphi’s message box, execution will be paused at the best line of your source code Delphi could find to represent the source of the exception. Press the “Run” button to have your program resume. Control will pass to the next finally or except
block. Before you resume your program, you may use the various debugging tools at your disposal. You can inspect the values of any variables in scope, and you can even modify their values.
So, how do you notify Delphi that you've already handled an exception? You don't — because your program hasn't handled it yet. And why can't the debugger detect whether your program is going to handle an exception? Because in order to do that, it needs to execute your program further. Detecting the lack of an exception handler is like solving the halting problem. The only way to determine whether an exception is going to be handled is to let the program run and then see whether the exception gets handled. But by that point, it's too late to do any debugging, so the debugger has no choice but to pause your program when it first detects an exception and then let you figure out what to do from there.
My article goes on to describe some ways you can avoid having the debugger catch certain exceptions, summarized here:
Use advanced breakpoints to temporarily disable breaking on exceptions for certain regions of the code.
Configure the debugger to ignore certain classes of exceptions (and their descendants).
Tell the debugger not to notify you about any exceptions.
Disable integrated debugging altogether.
There's another options that I didn't include in my article:
Change your program such that the exception doesn't get raised in the first place.
You say you're validating numeric input. That sounds to me like you're doing something like calling StrToInt and then catching the EConvertError exception when the input isn't a valid integer. That's an expensive way of validating input. Instead of that, use TryStrToInt, which will tell you whether the conversion succeeded, or StrToIntDef, which will silently return a default value instead of raising an exception. Another option is plain old Val, which tries to convert a string, and if it fails, it tells you which position in the string caused the failure. Val is particularly useful if you want to consume as many characters as possible for the conversion and then resume parsing at the next non-numeric character.
Quoting from the documentation (Delphi 7) on TApplication.OnException
"Use OnException to change the default behavior that occurs when an exception is not
handled by application code."
So: Only unhandled exception will be available in the OnException event handler. What you are experiencing is probably the Delphi IDE breaking on the exception. This is (at least in Delphi 7) configurable.
In Delphi 7 you can configure it by clicking on the Tools->Debugger Options menu. Then select the "Language Exceptions" an un-ckech the "Stop on Delphi exceptions" checkbox. It might however be different in other delphi versions.
An alternative might be to not use Application.OnException. But to simply catch all exceptions in your "main" function. That way you can catch all exceptions you have not cached before, log the exception, and then crash.
Application.OnException should only fire for unhandled exceptions. Re-raising an exception in a try-except block will cause the exception to be handled by Application.OnException. For input validation that raises an exception, you could display a message to the user and then re-raise the exception only if you want it recorded into the error log.

Swift debug: app stops at breakpoints and doesn't crash

In the age of Objective C, when I had error in my code and run it I received green thread breakpoint, but if I push button "Continue program execution" in the Debug Area, my app crashes and shows me exact error description.
Right now I'm suffer from "breakpoint loop" which occurs if I push continue button it stops at the same place and never crash. App doesn't crash at all and I haven't got any app error description. This is really annoying me, Thread 1: EXC_BREAKPOINT (code=EXC_ARM_BREAKPOINT, subcode=0xe7ffdefe) isn't very useful information in point of view of good old fatal error descriptions and so on.
Is there a way to break this "breakpoint loop" and crash the app to see error description?
EDIT:
Note: here I used word breakpoint, I didn't mean breakpoints that was set by myself, I meant default runtime crash breakpoints. Here is image of it (correct my if I named it wrong):
My breakpoint navigator:
The EXC_BREAKPOINT exception just means that the program tried to execute some particular "trap" instruction. That happens to be the instruction that debuggers use to set breakpoints. However, there are a number of system components that also use the trap instruction to halt execution when the program hits some exceptional condition. The idea being that if you are in the debugger, your app will stop and you can analyze the problem, but if not the app will die at that point.
You can tell whether the debugger is responsible for the trap because if lldb recognizes the EXC_BREAKPOINT as coming from a trap that it inserted, then it will tell you the stop reason was "breakpoint 1.1" or whatever - it won't show the raw EXC_BREAKPOINT as the stop reason. So if you are seeing EXC_BREAKPOINT it means you got some kind of internal assertion failure.
Usually you can tell what's going on by going to the bottom-most frame in the stack and looking at the code that has the trap inserted in it. Sometimes this isn't obvious in Xcode because if you stop for some exceptional reason in a stack where the bottom-most frames are assembly code, Xcode will select the lowest frame in the stack that has debug information. But if you look at the stack, you may see more frames below the stack.
You will also sometimes see little chunks of code that belong to your function, but come after the normal return instruction for you function. For instance, the stack guard mechanism will write a little error-handling section after some functions, and jump there and then trap if it notices stack overflow.

Performing one last TFLog when the app crashes by SIGSEGV

TestFlight has been giving me some SIGSEGV crash reports.
I found a way to pinpoint the exact problem. However, to do this, I need to perform one last TFLog exactly at the moment this crashes, which will contain important information to help me find the culprit code (because TestFlight's backtrace is currently useless to me).
I imagine that this is indeed possible - after all, TestFlight is capable of sending the crash report upon, well, crashing. But, where? Where can I put my TFLog?
You can install a SIGSEGV handler using sigaction. From the handler, you can log what you need, then kill the app (e.g. with abort()).
Example:
void sigsegv_handler(int signo, siginfo_t *info, void *ucontext) {
/* Inspect info to see where the crash occurred */
}
/* in main() or another suitable entry point */
struct sigaction sa;
sa.sa_sigaction = sigsegv_handler;
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
Note that sigsegv_handler can access globals, etc. but may not be able to easily access local variables of the crashing stack frame. If you clarify what you need to log, it may be possible to work out how to extract that information from within the SIGSEGV handler.
Note too that some functions are technically not safe to call from a SIGSEGV handler. However, given that the entire app is about to die, you aren't likely to make things significantly worse by calling these functions (unless they cause a deadlock because you attempt to reacquire a lock, for example). Significantly, however, you will want to avoid calling malloc or free in the signal handler because your program may have crashed inside either one, and you definitely don't want to cause a second segfault from within your signal handler.

Exception breakpoints in xcode for iOS, is exceptions excusable?

Am new to the iOS development. While looking into one of the App I work on, with exception breakpoint enabled in xcode. I see so many repetitive exceptions been raised.
But till now, we never realised this, as iOS was excusing those exceptions. The main exception was happening in our service layer, where, in an XML parsing, one of the element mentioned as key was missing. since so many back end requests are fired, for every XML response, and for multiple items inside it, this exception is thrown.
Now that our app is published, will this impact the device's stability? We are planning to fix thee kind of issues. But now, Will these be logged into Apple's diagnostic information. Just curious to know these. Can someone shed some light over it?
In Objective-C, Exceptions are used to signal fatal errors, that is programmer errors and "unexpected" and unrecoverable runtime errors. In Objective-C, Exceptions are a debugging and testing aid, not a construct to control program flow or to regain control after an error.
Usually, an application in a release state shouldn't throw exceptions. If it happens anyway, the only correct course of action is to terminate the program as soon as possible. In general, the Objective-C system frameworks are not exception safe. This means, after an exception has been thrown the application's state will be corrupt. If your program continues, even worse things may happen.
There are only a few, always undocumented harmless cases where exceptions will be thrown and caught by system libraries without leaving the program in an unstable condition.
See also https://developer.apple.com/library/ios/documentation/cocoa/conceptual/Exceptions/Exceptions.html

Resources