Created
June 23, 2012 21:58
-
-
Save Machx/2980225 to your computer and use it in GitHub Desktop.
Objective-C Crash Catching in 1992
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How can I use this ? can you give some example in Objective C?