This is HKCrashTrap.m in view mode; [Download] [Up]
// // HKCrashTrap version 1.0 // // Based on ObjectError // Original class by: // Bill Bumgarner, Andrew Stone, Mike Morton, and Julie Zalenski // // Modifications by: // Ivo Rothschild (ivo@hasc.ca) // // A class that poses as object and does crash-reporting. // Catches terminating signals (ie seg faults, bus errors) // and fatal Objective-C runtime errors and writes a backtrace // out to the console using some shady hacks. This could be modified // to write backtrace to a file, mail message, etc... if desired. // // // You may freely copy, distribute, and reuse the code in this class. // // 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 "HKCrashTrap.h" #import <appkit/Application.h> #import <appkit/nextstd.h> #import <appkit/appkit.h> #import <strings.h> #import <stdio.h> #import <sys/signal.h> #import <objc/objc-runtime.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[]; static void handle_signal(int signal); static BOOL ignoreCrashes; static BOOL continueAfterError; #define FREED_OBJECT_NAME "<FreedObject>" static void(*_originalError)(Object *anObject, const char *format, va_list ap); /* ** Define an error function that dumps a backtrace. */ static void _objectError(Object *anObject, const char *format, va_list ap) { char buf[BUFSIZ]; vsprintf(buf,format,ap); _error = _originalError; [HKCrashTrap dumpBacktrace:buf]; _error = _objectError; if (!continueAfterError) { abort(); } [HKCrashTrap tryToContinue]; return; } @implementation HKCrashTrap + setup // Initialize everything and poseAs the Object class. { _originalError = _error; _error = _objectError; [[self class] poseAs:[Object class]]; [[self class] setSignalHandler:handle_signal]; ignoreCrashes=NO; continueAfterError=NO; return self; } + setContinueAfterError:(BOOL)flag; { continueAfterError = flag; return self; } + (BOOL)continueAfterError; { return continueAfterError; } + 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, class, classIsa; Method m; BOOL isClassMethod; object = *(id *)(framePointer+8); // receiver is 8 bytes from fp selector = *(SEL *)(framePointer+12); // selector is 12 bytes from fp classIsa = nil; object_getInstanceVariable(object, "isa", (void **)&class); if (class) object_getInstanceVariable(class, "isa", (void **)&classIsa); if (!classIsa) { // Freed object. sprintf(line, "%c[%s %s",'?',FREED_OBJECT_NAME, sel_getName(selector)); } else { 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); [HKCrashTrap dumpBacktrace:buf]; if (!continueAfterError) { exit(1); } [HKCrashTrap 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 (continueAfterError) { [[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 (continueAfterError) { [self tryToContinue]; return self; } return [super error:buffer]; } + tryToContinue; { int mask; NXLogError("Trying to continue...\n"); NXRunAlertPanel("Error Alert!","An internal error has occurred. Try to save work, then quit and restart the application.","Continue",NULL,NULL); mask = 0; sigsetmask(mask); // unblock all signals... [self resumeHandlingCrashes]; [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.