Skip to content

Instantly share code, notes, and snippets.

@Machx
Created June 23, 2012 21:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Machx/2980225 to your computer and use it in GitHub Desktop.
Save Machx/2980225 to your computer and use it in GitHub Desktop.
Objective-C Crash Catching in 1992
// ObjectError.h -- Subclass of Object which handles errors.
// Copyright (c) 1992 by Proponent, Inc. All rights reserved.
// To make this work, the application must send a "setup" message
// to the class. This will poseAs: object and intercept a number of things.
#import <objc/Object.h>
@interface ObjectError : Object
{ // No instance variables (this one poses!)
}
// PUBLIC CLASS METHODS
+ setup; // initialize everything and pose as Object class
+ setSignalHandler:(void (*)()) handler; // address of function, or SIG_DFL
@end
// Contributors:
// Bill Bumgarner and Andrew Stone (Stone Design)
// Mike Morton (Proponent)
// Julie Zelenski (NeXT)
//
// A class that poses as Object and does crash-reporting.
// Catches terminating signals (seg faults, bus errors)
// and fatal Objective-C runtime errors and writes a
// backtrace out to the console using some shady hacks.
// Could be modified to write backtrace to file, mail
// message, etc. for purposes of beta-testing perhaps.
//
// You may freely copy, distribute and reuse this code.
// but you do so at your own risk.
// Julie Zelenski, her mother, NeXT Inc., Congress, Macy's
// the Mormon Tabernacle Choir , your local 7-11, and the
// Honda Motor Company disclaim any warranty of any kind,
// expressed, implied, imagined or hoped for, as to its fitness
// for any particular use.
#import "ObjectError.h"
#import <appkit/nextstd.h>
#import <strings.h>
#import <stdio.h>
#import <sys/signal.h>
typedef struct _sig{
int number;
BOOL isOn;
char *message;
} SignalItem;
typedef struct _type{
char encoding;
char *format;
char *name;
} ObjcType;
extern SignalItem signals[];
extern ObjcType encodings[];
@implementation ObjectError
static void handle_signal (int signal);
static BOOL ignoreCrashes;
+ setup; // initialize everything and pose as Object class
{
[self poseAs:[Object class]];
[self setSignalHandler:handle_signal];
ignoreCrashes = NO;
return self;
}
+ setSignalHandler:(void (*)()) handler;
{
SignalItem *cur;
for (cur = signals; cur->number; cur++)
if (cur->isOn)
signal(cur->number,handler);
return self;
}
+ resumeHandlingCrashes
{
[self setSignalHandler:handle_signal];
ignoreCrashes = NO;
return self;
}
+ stopHandlingCrashes;
{
[[self class] setSignalHandler :(void (*)()) SIG_DFL];
ignoreCrashes = YES;
return self;
}
// ASSUMPTION: The layout of a frame for a method invocation is:
// fp+0 bytes: calling frame
// fp+4 bytes: calling pc
// fp+8 bytes: self
// fp+12 bytes: selector for method invocation
// fp+16 bytes: first argument
// The layout for a function invocation is:
// fp+0 bytes: calling frame
// fp+4 bytes: calling pc
// fp+8 bytes: first argument
// Clearly, it's shady to assume these things.
// But we're already crashing if we reach here...
#define MAX_FUNCTION_ARGS 4 // print at most four args
+ printFunctionFromFP:(void *)framePointer
{
char buffer[256];
char line[1024];
void *argStart;
long argNum; // index into arguments
sprintf(line, "function (");
argStart = framePointer + 8;
for (argNum = 0; argNum < MAX_FUNCTION_ARGS; argNum++) {
sprintf (buffer, "%s0x%06lx", (argNum != 0 ? ", " : ""),
*(((long *)argStart)+argNum));
strcat (line, buffer);
}
strcat (line, ")");
NXLogError("%s\n", line);
return self;
}
#define IS_CLASS(object) ([object class] == object)
+ printMethodFromFP:(void *)framePointer
{
char line[1024];
char buffer[256];
SEL selector;
id object;
Method m;
BOOL isClassMethod;
object = *(id *)(framePointer + 8); // receiver is 8 bytes from fp
selector = *(SEL *)(framePointer + 12); // selector is 12 bytes from fp
isClassMethod = IS_CLASS(object);
sprintf (line, "%c[%s %s", (isClassMethod ? '+' : '-'),
object_getClassName (object), sel_getName(selector));
m = (isClassMethod ? class_getClassMethod([object class], selector)
: class_getInstanceMethod([object class],
selector));
if (m) {
void *argStart = (framePointer + 8);
int argNum, numArgs, offset;
char *type;
numArgs = method_getNumberOfArguments(m);
argNum = 2; //(skip first two arguments, they are self & SEL)
while (argNum < numArgs) {
ObjcType *cur;
method_getArgumentInfo(m, argNum, &type, &offset);
for (cur = encodings; cur->encoding; cur++) {
if (cur->encoding == type[0]) { // Find Obj-C type
sprintf(buffer," :(%s)",cur->name);
strcat(line,buffer);
sprintf(buffer,cur->format,*(long *)(argStart +
offset));
strcat(line,buffer);
break;
}
}
argNum++;
}
} else
strcat(line, " Unknown method");
strcat (line, "]");
NXLogError("%s\n",line);
return self;
}
#define MAX_FRAMES 50
+ printBacktrace
{
void *framePointer; // pointer to current frame
unsigned int frameCount; // counter for number of frames printed
[self stopHandlingCrashes]; // try to avoid re-entry problems
// Start the frame pointer off at our frame
framePointer = ((void *) &self) - 8;
frameCount = 0;
// Assume that lotsa frames means either (a) we're trashed or
// (b) we're in a recursive deathtrap. In the latter case, we've
// probably got enough info to see the cycle.
while (frameCount < MAX_FRAMES && framePointer) {
// If this is a method, we'll have a valid selector at (fp+12).
if (sel_isMapped(*(SEL *)(framePointer + 12)))
[self printMethodFromFP:framePointer];
else
[self printFunctionFromFP:framePointer];
framePointer = (void *)*(long *)framePointer; // go to calling
frame
}
return self;
}
+ dumpBacktrace:(const char *)message;
{
NXLogError("%s\n", message);
NXLogError("Here's the backtrace\n");
[self printBacktrace];
return self;
}
static void handle_signal(int signal)
{
const char *msg = NULL;
char buf [256];
SignalItem *cur;
msg = "Unrecognized signal";
for (cur = signals; cur->number; cur++) {
if (cur->number == signal) {
msg = cur->message;
break;
}
}
sprintf (buf, "Caught signal #%d: \"%s\"", signal, msg);
[ObjectError dumpBacktrace:buf];
}
- error:(const char *) aString, ...;
{
va_list ap;
char buffer[1024];
va_start(ap,aString);
vsprintf(buffer, aString, ap);
va_end(ap);
if (!ignoreCrashes) [[self class] dumpBacktrace :buffer];
return [super error:buffer];
}
+ error:(const char *)aString, ...
{
va_list ap;
char buffer[1024];
va_start(ap,aString);
vsprintf(buffer, aString, ap);
va_end(ap);
if (!ignoreCrashes) [[self class] dumpBacktrace :buffer];
return [super error:buffer];
}
#define ON 1
#define OFF 0
SignalItem signals[] = {
{ SIGHUP , OFF, "hangup"},
{ SIGINT , OFF, "interrupt"},
{ SIGQUIT , ON, "quit"},
{ SIGILL , ON, "illegal instruction"},
{ SIGTRAP , ON, "trace trap"},
{ SIGIOT , ON, "IOT instruction"},
{ SIGEMT , ON, "EMT instruction"},
{ SIGFPE , ON, "floating point exception"},
{ SIGKILL , OFF, "kill"},
{ SIGBUS , ON, "bus error"},
{ SIGSEGV , ON, "segmentation violation"},
{ SIGSYS , ON, "bad argument to system call"},
{ SIGPIPE , OFF, "write on a pipe with no one to read it"},
{ SIGALRM , OFF, "alarm clock"},
{ SIGTERM , OFF, "software termination signal"},
{ SIGURG , OFF, "urgent condition present on socket"},
{ SIGSTOP , OFF, "stop"},
{ SIGTSTP , OFF, "stop signal generated from keyboard"},
{ SIGCONT , OFF, "continue after stop"},
{ SIGCHLD , OFF, "child status has changed"},
{ SIGTTIN , OFF, "background read attempted from control terminal"},
{ SIGTTOU , OFF, "background write attempted to control terminal"},
{ SIGIO , OFF, "i/o is possible on a descriptor"},
{ SIGXCPU , OFF, "cpu time limit exceeded"},
{ SIGXFSZ , OFF, "file size limit exceeded"},
{ SIGVTALRM , OFF, "virtual time alarm"},
{ SIGPROF , OFF, "profiling timer alarm"},
{ SIGWINCH , OFF, "Window size change"},
{ SIGUSR1 , OFF, "User defined signal 1"},
{ SIGUSR2 , OFF, "User defined signal 2"},
{0},
};
ObjcType encodings[] = {
{ _C_ID, "0x%06lx", "id"},
{ _C_CLASS, "0x%06lx", "Class"},
{ _C_SEL, "%s", "SEL"},
{ _C_CHR, "%c", "char"},
{ _C_UCHR,"%c", "unsigned char"},
{ _C_SHT, "%s", "short"},
{ _C_USHT, "%s", "unsigned short"},
{ _C_INT, "%d", "int"},
{ _C_UINT, "%d", "unsigned int"},
{ _C_LNG, "%l", "long",},
{ _C_ULNG, "%l", "unsigned long"},
{ _C_FLT, "%f", "float"},
{ _C_DBL, "%f", "double"},
{ _C_VOID, "0x%06lx","void"},
{ _C_PTR, "0x%06lx", "ptr"},
{ _C_CHARPTR, "%s", "char *"},
{ _C_STRUCT_B, "%x" ,"struct"},
{0, "0x%06lx", "Unknown"},
};
@end
@sudhakrish
Copy link

sudhakrish commented Mar 9, 2017

How can I use this ? can you give some example in Objective C?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment