#import "Aprica.h"
// Aprica2
// copyright Pirmin Braun 1997-2007 - pirmin@pirmin.de
// all Rights reserved;
#ifndef GNUSTEP
#import <Foundation/NSDebug.h>
#import <objc/Object.h>
#import <objc/objc-class.h>
#import <objc/objc-runtime.h>
#ifndef WIN32
#import <sys/signal.h>
#else WIN32
#import <signal.h>
#endif WIN32
#import "OEObject.h"
// ---------------- Typedef, Struct, and Union Declarations -------------------
typedef struct _signalItem
{ int number;
BOOL isOn;
NSString *message;
} OESignalItem_t;
typedef struct _objcTypeDesc
{ NSString *format;
NSString *name;
} OEObjcTypeDesc_t;
typedef struct _objcType
{ char encoding;
OEObjcTypeDesc_t description;
} OEObjcType_t;
typedef struct _returnInfo
{ int type;
int size;
} OEReturnInfo_t;
// --------------------- Constant and Enum Definitions ------------------------
// Name of exception raised to continue execution.
NSString *OECrashException = @"Crash";
// Name of defaults variables that can be used to set the function argument and
// stack frame counts.
NSString *OEFunctionArgCountDefaultsName = @"OEFunctionArgCount";
NSString *OEFrameCountDefaultsName = @"OEFrameCount";
// Default number of function arguments printed.
static unsigned functionArgCountG = 4;
// Default number of stack frames printed.
static unsigned frameCountG = 50;
// Telltale string that occurs in error message when a freed object is
// messaged.
static NSString *freedObjectMessageG = @"freed object";
// Class name printed in backtrace for a freed object.
static NSString *freedObjectNameG = @"<FreedObject>";
#ifndef WIN32
static NSString *endOfLineG = @"\n";
#else WIN32
// Make stack backtrace display nicely in NT's EventViewer.
static NSString *endOfLineG = @"\r\n";
#endif WIN32
// 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?
// WIN32 assumptions
// Stack layout is the same, except in cases where the return value from the
// current frame is a structure, when all arguments get shifted away.
// Stack unwind definitions. These are offsets from the frame pointer where
// the previous frame pointer is stored, and where the return pointer is
// stored. (These offsets don't seem to be used, so why are they defined??)
// Mach m68k/i486 assumptions
// Given the frame pointer, where are the arguments?
static int argStartPositionG = 8;
// Given the location of the arguments, where are self and the selector?
#if defined(STACK_GROWS_UP)
static int selfIndexG = -1;
static int sELIndexG = -2;
#else
static int selfIndexG = 0;
static int sELIndexG = 1;
#endif
#define ON 1
#define OFF 0
#ifndef WIN32
static OESignalItem_t OESignals[] =
{ {0, OFF, @"Exit from the shell"}, {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"}};
#else WIN32
static OESignalItem_t OESignals[] =
{ {0, OFF, @"Exit from the shell"}, {SIGINT, OFF, @"Interrupt"}, {SIGILL, ON, @"Illegal instruction"}, {SIGFPE, ON, @"Floating point exception"}, {SIGSEGV, ON, @"Segmentation violation"}, {SIGTERM, OFF, @"Software termination"}, {SIGBREAK, ON, @"Cntrl-Break sequence"}, {SIGABRT, ON, @"Abnormal termination triggered by abort call"}};
#endif WIN32
static OEObjcType_t OEEncodings[] =
{ {_C_ID, {@"%@", @"id"}}, {_C_CLASS, {@"%@", @"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"}}, {_C_LNG, {@"%l", @"long"}}, {_C_ULNG, {@"%l", @"unsigned long"}}, {_C_FLT, {@"%f", @"float"}}, {_C_DBL, {@"%f", @"double"}}, {_C_VOID, {@"0x%06x", @"void"}}, {_C_PTR, {@"0x%06x", @"ptr"}}, {_C_CHARPTR, {@"%s", @"char *"}}, {_C_STRUCT_B, {@"%x", @"struct"}}};
// ------------------------- Function Declarations ----------------------------
static void handle_signal(int aSignal);
// The _error() function prototype has an Object rather than NSObject first
// argument type.
static void (*_originalError)(Object *anObject, const char *format,
va_list ap);
static void _objectError(Object *anObject, const char *aFormat,
va_list theArguments);
@interface OEObject(Private)
// ---------------------- Private Method Declarations -------------------------
+ (void)_setSignalHandler:(void (*)())aHandler;
+ (NSString *)_functionMessageFromFP:(void *)aFramePointer;
+ (NSString *)_methodMessageWithFP:(void *)aFramePointer
selector:(SEL)aSelector;
+ (void)_tryToContinue;
+ (BOOL)_isReturnStructInRegistersWithSize:(int)aSize;
// returnValue argument is always NULL - future enhancement??
+ (id)_selfWithFrame:(void *)anArgumentFrame
returnValue:(OEReturnInfo_t *)aReturnValue;
+ (SEL)_selectorWithFrame:(void *)anArgumentFrame
returnValue:(OEReturnInfo_t *)aReturnValue;
+ (NSString *)_argumentStringWithObjcType:(const char *)anObjcType
offset:(void *)anOffset;
+ (void)_logMessage:(NSString *)aMessage;
@end
@implementation OEObject
// --------------------- Class Variable Declarations --------------------------
static BOOL ignoreCrashesC;
static BOOL continueAfterErrorC;
static unsigned freedAddressC;
static unsigned functionArgCountC;
static unsigned frameCountC;
static NSMutableDictionary *signalMessagesC;
static NSMutableDictionary *objcTypeEncodingsC;
// ----------------------- Class Method Definitions ---------------------------
+ (void)initialize
{ if (self == [OEObject class])
{ signalMessagesC = nil;
objcTypeEncodingsC = nil;
freedAddressC = 0;
ignoreCrashesC = NO;
functionArgCountC = functionArgCountG;
frameCountC = frameCountG;
continueAfterErrorC = NO;
}
}
// Pose as the NSObject class.
+ (void)setup
{ if (signalMessagesC == nil)
{ NSUserDefaults *userDefaultsL = [NSUserDefaults standardUserDefaults];
NSNumber *functionArgCountL = [userDefaultsL
ofk:OEFunctionArgCountDefaultsName];
NSNumber *frameCountL = [userDefaultsL
ofk:OEFrameCountDefaultsName];
if (functionArgCountL != nil)
{ functionArgCountC = [functionArgCountL unsignedIntValue];
}
if (frameCountL != nil)
{ frameCountC = [frameCountL unsignedIntValue];
}
[self poseAsClass:[NSObject class]];
_originalError = _error;
_error = _objectError;
signalMessagesC = [[NSMutableDictionary alloc]
initWithCapacity:sizeof(OESignals) / sizeof(OESignalItem_t)];
[self _setSignalHandler:handle_signal];
}
}
+ (void)setContinueAfterError:(BOOL)aFlag
{ continueAfterErrorC = aFlag;
}
+ (void)resumeHandlingCrashes
{ [self _setSignalHandler:handle_signal];
ignoreCrashesC = NO;
}
+ (void)stopHandlingCrashes
{ [self _setSignalHandler:(void (*)())SIG_DFL];
ignoreCrashesC = YES;
}
+ (void)printCurrentStack;
{ [OEObject printCurrentStack:30];
}
+ (void)printCurrentStackShort;
{ [OEObject printCurrentStack:2];
}
+ (void)printCurrentStack:(int)stackDepth;
{// Counter for number of frames printed
unsigned frameCountL;
unsigned maxFrameCountL;
// Pointer to current frame
void *framePointerL;
NSMutableString *aMessage = [NSMutableString stringWithCapacity:2048];
NSString *s;
[aMessage setString:@"current Stack:\n======================================================================\n"];
maxFrameCountL = frameCountC + 3;
stackDepth+=3;
for (frameCountL = 2; frameCountL < stackDepth; frameCountL++)
{ SEL possibleSelectorL;
// Start the frame pointer off at our frame.
framePointerL = NSFrameAddress(frameCountL);
if (framePointerL == NULL) break;
// This cast may be bogus, but it shuts up the compiler.
possibleSelectorL = [self
_selectorWithFrame:(void *)((unsigned)framePointerL +
argStartPositionG)
returnValue:NULL];
if (sel_isMapped(possibleSelectorL))
{ s = [self _methodMessageWithFP:framePointerL selector:possibleSelectorL];
}
else
{ s = [self _functionMessageFromFP:framePointerL];
}
if([s length]>256)s = [[s substringToIndex:256] stringByAppendingString:@"...\n"];
[aMessage appendString:s];
}
LOG(aMessage);
}
+ (NSString *)printBacktrace:(NSString *)aMessage
{ // Counter for number of frames printed
unsigned frameCountL;
unsigned maxFrameCountL;
// Pointer to current frame
void *framePointerL;
NSScanner *scannerL = [NSScanner scannerWithString:aMessage];
// Identify message to freed object error message and extract the address
// of the freed object so that it can be used to identify the object in the
// stack frame report.
if ([scannerL scanUpToString:freedObjectMessageG intoString:NULL] &&
[scannerL scanString:freedObjectMessageG intoString:NULL])
{ [scannerL setScanLocation:[scannerL scanLocation] + 1];
[scannerL scanHexInt:&freedAddressC];
}
aMessage = [aMessage
stringByAppendingFormat:@"Attempting stack frame backtrace:%@",
endOfLineG];
// Try to avoid re-entry problems.
[self stopHandlingCrashes];
maxFrameCountL = frameCountC + 3;
// Skip 3 OEObject frames. 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.
for (frameCountL = 3; frameCountL < maxFrameCountL; frameCountL++)
{ SEL possibleSelectorL;
// Start the frame pointer off at our frame.
framePointerL = NSFrameAddress(frameCountL);
if (framePointerL == NULL) break;
// This cast may be bogus, but it shuts up the compiler.
possibleSelectorL = [self
_selectorWithFrame:(void *)((unsigned)framePointerL +
argStartPositionG)
returnValue:NULL];
if (sel_isMapped(possibleSelectorL))
{ aMessage = [aMessage stringByAppendingString:[self
_methodMessageWithFP:framePointerL selector:possibleSelectorL]];
}
else
{ aMessage = [aMessage stringByAppendingString:[self
_functionMessageFromFP:framePointerL]];
}
}
freedAddressC = 0;
return aMessage;
}
// ---------------- Overridden Instance Method Definitions --------------------
- (void)doesNotRecognizeSelector:(SEL)aSelector
{ NS_DURING
[super doesNotRecognizeSelector:aSelector];
NS_HANDLER
Class selfClassL = [self class];
[selfClassL _logMessage:[selfClassL printBacktrace:[[localException
reason] stringByAppendingString:endOfLineG]]];
NS_ENDHANDLER
}
// -------------------- New Instance Method Definitions -----------------------
// ----------------- Delegate Instance Method Definitions ---------------------
@end
@implementation OEObject(Private)
// ---------------------- Private Method Definitions --------------------------
+ (void)_setSignalHandler:(void (*)())aHandler
{ OESignalItem_t *signalItemL;
for (signalItemL = OESignals + (sizeof(OESignals) / sizeof(OESignalItem_t))
- 1; 0 < signalItemL->number; signalItemL--)
{ if (signalItemL->isOn)
{ [signalMessagesC setObject:signalItemL->message
forKey:[NSNumber
numberWithInt:signalItemL->number]];
signal(signalItemL->number, aHandler);
}
}
}
+ (NSString *)_functionMessageFromFP:(void *)aFramePointer
{ void *argStartL;
unsigned argNumL; // Index into arguments;
NSString *messageL = @"function("; // This cast may be bogus, but it shuts up the compiler.
argStartL = (void *)((unsigned)aFramePointer + argStartPositionG);
for (argNumL = 0; argNumL < functionArgCountC; argNumL++)
{ NSString *punctuationL;
if (argNumL != 0)
{ punctuationL = @", ";
}
else
{ punctuationL = EON;
}
messageL = [messageL stringByAppendingFormat:@"%@0x%06x",
punctuationL, (unsigned)argStartL + argNumL];
}
return [messageL stringByAppendingFormat:@")%@", endOfLineG];
}
+ (NSString *)_methodMessageWithFP:(void *)aFramePointer
selector:(SEL)aSelector
{ NSString *messageL;
// This cast may be bogus, but it shuts up the compiler.
id objectL = [self _selfWithFrame:(void *)((unsigned)aFramePointer +
argStartPositionG)
returnValue:NULL];
if (freedAddressC == (int)objectL)
{ // Cause of crash is freed object, so no need to print method info.
messageL = [NSSWF@"? [%@ %@]", freedObjectNameG,
NSStringFromSelector(aSelector)];
}
else
{ char methodCharL;
Method methodL;
Class classL = [objectL class];
if (objectL == classL)
{ methodCharL = '+';
methodL = class_getClassMethod(classL, aSelector);
}
else
{ methodCharL = '-';
methodL = class_getInstanceMethod(classL, aSelector);
}
messageL = [NSSWF@"%c [%@ ", methodCharL,
NSStringFromClass(classL)];
if (methodL != NULL)
{ NSString *selectorWithArgsL;
int argCountL = method_getNumberOfArguments(methodL);
NSString *selectorStringL = NSStringFromSelector(aSelector);
if (2 < argCountL)
{ unsigned argNumL;
NSString *selectorComponentL;
void *argStartL = (void *)((unsigned)aFramePointer +
argStartPositionG);
NSScanner *argScannerL = [NSScanner
scannerWithString:selectorStringL];
selectorWithArgsL = nil;
// Skip the first two args which are self and _cmd.
for (argNumL = 2; argNumL < argCountL; argNumL++)
{ const char *typeL;
int offsetL;
// Deal with case in which argument includes no
// description.
if (![argScannerL scanUpToString:@":"
intoString:&selectorComponentL])
{ selectorComponentL = EON;
}
if (selectorWithArgsL != nil)
{ selectorWithArgsL = [selectorWithArgsL
stringByAppendingFormat:@" %@",
selectorComponentL];
}
else
{ selectorWithArgsL = selectorComponentL;
}
method_getArgumentInfo(methodL, argNumL, &typeL, &offsetL);
selectorWithArgsL = [selectorWithArgsL
stringByAppendingString:[self
_argumentStringWithObjcType:typeL
offset:(void *)((unsigned)argStartL + offsetL)]];
[argScannerL scanString:@":" intoString:NULL];
}
}
else
{ selectorWithArgsL = selectorStringL;
}
messageL = [messageL stringByAppendingFormat:@"%@]",
selectorWithArgsL];
}
else
{ messageL = [messageL
stringByAppendingString:@" <unknown method>]"];
}
}
return [messageL stringByAppendingString:endOfLineG];
}
// Two implementations of _tryToContinue are provided. Select the desired
// implementation by defining OE_SELF_CONTAINED or not.
// #define OE_SELF_CONTAINED
#ifndef OE_SELF_CONTAINED
// This implementation requires that NSApplication's run method be overridden
// such that when OECrashException is caught, the main run loop is reentered.
// This implementation feels less hackish because raising NSAbortModalException
// for a non-existent modal run loop isn't required.
+ (void)_tryToContinue
{ ignoreCrashesC = NO;
[self resumeHandlingCrashes];
// OECrashException should be caught such that main run loop can be
// reentered.
[NSException raise:OECrashException format:@"Trying to continue...%@",
endOfLineG];
}
#else OE_SELF_CONTAINED
// This implementation is "self-contained" in that it doesn't require that
// NSApplication's run method be overridden. However, this implementation
// feels hackish because raising NSAbortModalException for a non-existent modal
// run loop is required.
+ (void)_tryToContinue
{ ignoreCrashesC = NO;
// Attempts to raise an exception that can be caught in NSApp's run method
// can fail under NT for reasons I don't understand. Therefore, resort to
// the approach used by HKCrashTrap.
if (NSRunCriticalAlertPanel(nil, @"An internal error has occurred. Try to save any unsaved changes. Continuing may result in repeated errors. If so, quit and restart %@.",
@"Continue", @"Quit", nil,
[[NSProcessInfo processInfo] processName]) ==
NSAlertDefaultReturn)
{ [self resumeHandlingCrashes];
// HKCrashTrap uses abortModal to return to the main run loop. This
// feels hackish because there is no modal run loop to abort. It also
// results in an "abort modal" message being logged which could be
// confusing.
// [NSApp abortModal];
// LOGS(@"Trying to continue...%@", endOfLineG);
// OECrashException seems to be caught by some AppKit method. This
// returns to the main run loop and logs a more informative message.
// Is this just luck? If so, revert to the HKCrashTrap implementation.
[NSException raise:OECrashException
format:@"Trying to continue...%@", endOfLineG];
}
else
{ exit(1);
}
}
#endif OE_SELF_CONTAINED
// The following encapsulates how to find self and selectors on the various
// architectures as best I know how.
+ (BOOL)_isReturnStructInRegistersWithSize:(int)aSize
{#if defined(sparc) || defined(__alpha__)
// Sparc, PDO (alpha, sparc) never put struct in reg on return
return NO;
#else
// these are heuristics (i.e. wrong in many cases)
// nextstep hppa
# if defined(hppa) && !defined(hpux)
return (BOOL)(aSize == 1 || aSize == 2 || aSize == 4);
# elif defined(m68k) || defined(i386) || defined(hppa)
return (BOOL)(aSize == 1 || aSize == 2 || aSize == 4 || aSize == 8);
# else
return NO;
# endif
#endif
}
+ (id)_selfWithFrame:(void *)anArgumentFrame
returnValue:(OEReturnInfo_t *)aReturnValue
{ if (aReturnValue == NULL)
{ // Take our best guess.
return ((id *)anArgumentFrame)[selfIndexG];
}
#if defined(__alpha__)
{ int offsetL;
if (aReturnValue->type == NSObjCStructType)
{ offsetL = 1;
}
else
{ offsetL = 0;
}
return ((id *)anArgumentFrame)[selfIndexG + offsetL];
}
#elif defined(WIN32)
{ // If the return type is a struct, and the struct is NOT passed in
// registers, then the address of the struct return is on the stack
// in the 0th slot. Therefore, all other normal parameters are shifted
// by one position.
if ((aReturnValue->type == NSObjCStructType)
&& ![self _isReturnStructInRegistersWithSize:aReturnValue->size])
{ return ((id *)anArgumentFrame)[selfIndexG + 1];
}
// if it's not a struct return OR if the struct is in registers
// (i.e., it's less than 8 bytes) then just return the selfIndex.
return ((id *)anArgumentFrame)[selfIndexG];
}
#else // ! __alpha__
return ((id *)anArgumentFrame)[selfIndexG];
#endif
}
+ (SEL)_selectorWithFrame:(void *)anArgumentFrame
returnValue:(OEReturnInfo_t *)aReturnValue
{ if (aReturnValue == NULL)
{ // Take our best guess.
return ((SEL *)anArgumentFrame)[sELIndexG];
}
#if defined(__alpha__)
{ int offsetL;
if (aReturnValue->type == NSObjCStructType)
{ offsetL = 1;
}
else
{ offsetL = 0;
}
return ((SEL *)anArgumentFrame)[sELIndexG + offsetL];
}
#elif defined(WIN32)
{ // If the return type is a struct, and the struct is NOT passed in
// registers, then the address of the struct return is on the stack
// in the 0th slot. Therefore, all other normal parameters are shifted
// by one position.
if ((aReturnValue->type == NSObjCStructType)
&& ![self _isReturnStructInRegistersWithSize:aReturnValue->size])
{ return ((SEL *)anArgumentFrame)[sELIndexG + 1];
}
// if it's not a struct return OR if the struct is in registers
// (i.e., it's less than 8 bytes) then just return the sELIndex.
return ((SEL *)anArgumentFrame)[sELIndexG];
}
#else // ! __alpha__
return ((SEL *)anArgumentFrame)[sELIndexG];
#endif
}
+ (NSString *)_argumentStringWithObjcType:(const char *)anObjcType
offset:(void *)anOffset
{ NSData *descriptionL;
if (objcTypeEncodingsC == nil)
{ int indexL;
int sizeL = sizeof(OEEncodings) / sizeof(OEObjcType_t);
objcTypeEncodingsC = [[NSMutableDictionary alloc]
initWithCapacity:sizeL];
for (indexL = sizeL - 1; 0 <= indexL; indexL--)
{ OEObjcType_t encodingL = OEEncodings[indexL];
NSNumber *charEncodingL = [NSNumber
numberWithChar:encodingL.encoding];
OEObjcTypeDesc_t objcTypeDescL = encodingL.description;
descriptionL = [NSData dataWithBytes:(void *)&objcTypeDescL
length:sizeof(OEObjcTypeDesc_t)];
[objcTypeEncodingsC setSecureObject:descriptionL
forKey:(NSString *)charEncodingL];
}
}
descriptionL = [objcTypeEncodingsC ofk:[NSNumber
numberWithChar:anObjcType[0]]];
if (descriptionL != nil)
{ OEObjcTypeDesc_t objcTypeDescL;
NSString *formatL;
[descriptionL getBytes:(void *)&objcTypeDescL
length:sizeof(OEObjcTypeDesc_t)];
formatL = [@":(%@)" stringByAppendingString:objcTypeDescL.format];
// This cast may be bogus, but it shuts up the compiler.
return [NSSWF formatL, objcTypeDescL.name,*(unsigned *)anOffset];
}
else
{ // This cast may be bogus, but it shuts up the compiler.
return [NSSWF @" :(<unknown type>)0x%06x",*(unsigned *)anOffset];
}
}
+ (void)_logMessage:(NSString *)aMessage
{ LOGS(aMessage);
if (continueAfterErrorC)
{ [OEObject _tryToContinue];
}
else
{ exit(1);
}
}
@end
// ------------------------- Function Definitions -----------------------------
static void handle_signal(int aSignal)
{ NSString *messageL;
NSString *signalMessageL = [signalMessagesC ofk:[NSNumber
numberWithInt:aSignal]];
if (signalMessageL != nil)
{ messageL = [NSSWF@"Caught signal #%d: \"%@\"%@",
aSignal, signalMessageL, endOfLineG];
}
else
{ messageL = [NSString
stringWithFormat:@"Caught unrecognized signal #%d%@", aSignal,
endOfLineG];
}
[OEObject _logMessage:[OEObject printBacktrace:messageL]];
}
static void _objectError(Object *anObject, const char *aFormat,
va_list theArguments)
{ char bufferL[BUFSIZ];
NSString *messageL;
vsprintf(bufferL, aFormat, theArguments);
messageL = [NSSWF@"%s%@", bufferL, endOfLineG];
_error = _originalError;
messageL = [OEObject printBacktrace:messageL];
_error = _objectError;
[OEObject _logMessage:messageL];
}
#else /* GNUSTEP */
#include <execinfo.h>
#define BT_MAX 64
@implementation OEObject
+ (void)setup;
{}+ (void)printCurrentStack;
{ NSMutableString *stck = [NSMutableString stringWithCapacity: 1024];
char **strs;
void *btdata[BT_MAX];
int i,c;
c = backtrace (btdata,BT_MAX);
strs = backtrace_symbols(btdata,c);
for (i=0;i<c;i++) { [stck appendFormat:@"BT %i/%i: %s\n",i,c,strs[i]];
}
LOGS(stck);
}
@end
#endif /* GNUSTEP */