This is ObjectError.m in view mode; [Download] [Up]
// ObjectError.m
//
// (see .h for information)
//
// NO WARRANTY:
// ANY IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
// IS HEREBY DISCLAIMED. IN NO EVENT WILL THE AFOREMENTIONED PARTIES BE LIABLE
// FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES ARISING OUT OF THE USE OF OR INABILITY TO USE THIS CODE.
#import "ObjectError.h"
#import <appkit/Application.h>
#import <appkit/nextstd.h>
#import <appkit/appkit.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;
static BOOL muddleOn;
+ setup
// Initialize everything and poseAs the Object class.
{
[[self class] poseAs:[Object class]];
[[self class] setSignalHandler:handle_signal];
ignoreCrashes=NO;
muddleOn=NO;
return self;
}
+ setMuddleOn:(BOOL)flag
{
muddleOn=flag;
return self;
}
+ setSignalHandler:(void (*)())handler
{
SignalItem *cur;
for (cur=signals; cur->number; cur++) {
if (cur->isOn) {
signal(cur->number, handler);
}
}
return self;
}
+ resumeHandlingCrashes
{
[[self class] setSignalHandler:handle_signal];
ignoreCrashes=NO;
return self;
}
+ stopHandlingCrashes
{
[[self class] setSignalHandler:(void (*)())SIG_DFL];
ignoreCrashes=YES;
return self;
}
// ASSUMPTION: The layout of a stack 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 invoked
// fp+16 bytes: first argument
//
// ASSUMPTION: The layout of a stack frame for a function invocation is:
// fp+0 bytes: calling frame
// fp+4 bytes: calling pc
// fp+8 bytes: first argument
//
// Clearly these are shady assumptions, however we're already
// in the process of crashing, so what harm can be done?
#define MAX_FUNCTION_ARGS 4 // Print at most four args to a function
+ 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)?", ":""),
*(((unsigned 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 buffer[256];
char line[1024];
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 the first two args which are self and _cmd
while (argNum<numArgs) {
ObjcType *cur;
method_getArgumentInfo(m, argNum, &type, &offset);
for (cur=encodings; cur->encoding; cur++) {
// Find the ObjcType
if (cur->encoding == type[0]) {
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 a whole lotta 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 a whole cycle.
while (frameCount<MAX_FRAMES && framePointer) {
// If this frame is a method call 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 up one frame
}
return self;
}
+ dumpBacktrace:(const char *)message
{
NXLogError("%s\n", message);
NXLogError("Here's the stack frame 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];
if (!muddleOn) {
exit(1);
}
[ObjectError tryToContinue];
}
- 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];
if (muddleOn)
{
[[self class] tryToContinue];
return self;
}
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];
if (muddleOn)
{
[self tryToContinue];
return self;
}
return [super error:buffer];
}
+ tryToContinue;
{
ignoreCrashes = NO;
NXLogError("Trying to continue...\n");
NXRunAlertPanel("Internal Error","An internal error has occurred. Try to save work, then quit and restart the application.","Continue",NULL,NULL);
[NXApp abortModal];
return self;
}
#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"},
{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 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 is exceeded"},
{SIGXFSZ, OFF, "File size limit exceeded"},
{SIGVTALRM, OFF, "Virtual timer 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, "%c", "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 type"},
};
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.