Sometimes you need to crash your Cocoa app to analyse its crash behaviour.
Apple offers some guidance on this http://developer.apple.com/tools/xcode/symbolizingcrashdumps.html
#define CRASH_CODE 1
#if CRASH_CODE
(void)strlen((const char *)1);
#endif
On Leopard with Xcode 3.1.3 and GCC 4.2 it seems that something more is required.
The most reliable way to ensure abnormal termination is courtesy of stdlib.h:
abort();
abort() may optionally flush streams and delete temporary files. But if your app crashes for real it will simple receive a terminating signal.
abort() is defined by the ISO C reference to call SIGABRT. So the following also proves effective:
raise(SIGABRT);
We can confirm this by examining the OS X stdlib source for abort:
extern void (*__cleanup)();
extern void __abort(void) __dead2;
#define TIMEOUT 10000 /* 10 milliseconds */
void
abort()
{
struct sigaction act;
/*
* POSIX requires we flush stdio buffers on abort.
* XXX ISO C requires that abort() be async-signal-safe.
*/
if (__cleanup)
(*__cleanup)();
sigfillset(&act.sa_mask);
/*
* Don't block SIGABRT to give any handler a chance; we ignore
* any errors -- ISO C doesn't allow abort to return anyway.
*/
sigdelset(&act.sa_mask, SIGABRT);
(void)_sigprocmask(SIG_SETMASK, &act.sa_mask, NULL);
(void)raise(SIGABRT);
usleep(TIMEOUT); /* give time for signal to happen */
/*
* If SIGABRT was ignored, or caught and the handler returns, do
* it again, only harder.
*/
__abort();
}
__private_extern__ void
__abort()
{
struct sigaction act;
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
sigfillset(&act.sa_mask);
(void)_sigaction(SIGABRT, &act, NULL);
sigdelset(&act.sa_mask, SIGABRT);
(void)_sigprocmask(SIG_SETMASK, &act.sa_mask, NULL);
(void)raise(SIGABRT);
usleep(TIMEOUT); /* give time for signal to happen */
__builtin_trap(); /* never exit normally */
}
Several other signals are also configured by default to cause termination, eg:
raise(SIGBUS);
raise(SIGFPE);
raise(SIGSEGV);
It is possible that a developer who wishes to test out the crash behaviour their app will insert callable crash code like the above into their app. This can be avoided by sending a suitable signal to the application from the terminal using the kill(1) command.
kill -s ABRT process_id
When an exception occurs within the mach kernel that exception is mapped to a UNIX signal which is ultimately sent to your app. The xnu function responsible for this performs the following mapping between mach kernel exceptions and UNIX signals.
switch(exception) {
case EXC_BAD_ACCESS:
if (code == KERN_INVALID_ADDRESS)
*ux_signal = SIGSEGV;
else
*ux_signal = SIGBUS;
break;
case EXC_BAD_INSTRUCTION:
*ux_signal = SIGILL;
break;
case EXC_ARITHMETIC:
*ux_signal = SIGFPE;
break;
case EXC_EMULATION:
*ux_signal = SIGEMT;
break;
case EXC_SOFTWARE:
switch (code) {
case EXC_UNIX_BAD_SYSCALL:
*ux_signal = SIGSYS;
break;
case EXC_UNIX_BAD_PIPE:
*ux_signal = SIGPIPE;
break;
case EXC_UNIX_ABORT:
*ux_signal = SIGABRT;
break;
case EXC_SOFT_SIGNAL:
*ux_signal = SIGKILL;
break;
}
break;
case EXC_BREAKPOINT:
*ux_signal = SIGTRAP;
break;
}
And all Cocoa developers have certainly seen plenty of occurrences of EXC_BAD_ACCESS whilst debugging. EXC_BAD_ACCESS is simply the underlying mach exception which results in a SIGSEGV or SIGBUS error being sent to our app.
If you want to crash the app using a Cocoa call try:
NSDecimalNumber *n = (NSDecimalNumber *)[NSDecimalNumber numberWithInt:0];
[n decimalNumberByAdding:nil];
Post new comment