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.