From time to time, it’s useful to break into the debugger programmatically. The simplest way to accomplish this on Mac OS X and iPhone OS is with raise(SIGINT), to wit:
#define DebugBreak() raise(SIGINT)
There is one problem with this technique, however: the debugger (gdb) will break well inside the raise call (actually inside __kill called by kill called by raise), and more often then not will be unable to display the contents of local variables in the function that called raise(SIGINT).
The secret, of course, is to define an entirely inline version of raise(SIGINT). The implementation for raise is simple enough; kill is an even simpler wrapper around __kill.
int raise(int signo)
{
return kill(getpid(), signo);
}
int kill(pid_t pid, int signo)
{
return __kill(pid, signo, 1);
}
The final pieces, getpid and __kill, simply call system routines through the syscall mechanism.
So now we can update Matt Gallagher’s article by adding support for ARM (iPhone and iPod Touch) like so:
#if __ppc64__ || __ppc__
#define BSDebugBreak() \
do { \
__asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
: : : "memory","r0","r3","r4" ); \
} while (0)
#elif __i386__ || __x86_64__
#define BSDebugBreak() \
do {__asm__{"int $3\n" : : );} while (0)
#elif __arm__
#define BSDebugBreak() \
do { \
__asm__("mov r0, #20\nmov ip, r0\nsvc 128\nmov r1, #37\nmov ip, r1\nmov r1, #2\nmov r2, #1\n svc 128\n" \
: : : "memory","ip","r0","r1","r2"); \
} while (0)
#else // unknown architecture
#define BSDebugBreak() raise(SIGINT)
#endif
I have omitted Matt’s references to if(AmIBeingDebugged()) as I prefer to make that test optional and explicit in the client code rather than include it in the BSDebugBreak macro. I’ve also added a clause for unknown, future architectures (it is usually wise to assume another architecture is just over the horizon). My personal version of this macro definition has an #error directive in place of the raise(SIGINT) version.
Download BSDebugBreak.h