mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
Improved stacktrace/debug handling.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@25667 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
2fffab15b7
commit
9c36ec10dd
5 changed files with 234 additions and 66 deletions
13
ChangeLog
13
ChangeLog
|
@ -1,3 +1,16 @@
|
|||
2007-12-03 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSDebug.m:
|
||||
* Source/GSPrivate.h:
|
||||
* Source/NSException.m:
|
||||
* Source/NSThread.m:
|
||||
Rewrite some stackframe handling code for greater efficiency.
|
||||
When raising an exception, just get the current stack and don't
|
||||
bother translating addresses to method/function information until
|
||||
needed by the -description method. Improves performance of
|
||||
exception handling and eliminates the possibility of a recursive
|
||||
exception due to a problem in the stacktrace code.
|
||||
|
||||
2007-12-02 David Ayers <ayers@fsfe.org>
|
||||
|
||||
* Source/NSDecimal.m (GSDecimalDouble): Use NAN when available.
|
||||
|
|
|
@ -378,6 +378,11 @@ void GSPrivateNotifyIdle(void) GS_ATTRIB_PRIVATE;
|
|||
*/
|
||||
BOOL GSPrivateNotifyMore(void) GS_ATTRIB_PRIVATE;
|
||||
|
||||
/* Function to return the current stack return addresses.
|
||||
*/
|
||||
NSMutableArray *
|
||||
GSPrivateStackAddresses(void) GS_ATTRIB_PRIVATE;
|
||||
|
||||
/* Function to return the hash value for a small integer (used by NSNumber).
|
||||
*/
|
||||
unsigned
|
||||
|
|
182
Source/NSDebug.m
182
Source/NSDebug.m
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include "GSPrivate.h"
|
||||
#include "GNUstepBase/GSLock.h"
|
||||
#include "Foundation/NSArray.h"
|
||||
#include "Foundation/NSData.h"
|
||||
|
@ -38,6 +39,7 @@
|
|||
#include "Foundation/NSNotification.h"
|
||||
#include "Foundation/NSNotificationQueue.h"
|
||||
#include "Foundation/NSThread.h"
|
||||
#include "Foundation/NSValue.h"
|
||||
|
||||
typedef struct {
|
||||
Class class;
|
||||
|
@ -854,15 +856,6 @@ GSDebugMethodMsg(id obj, SEL sel, const char *file, int line, NSString *fmt)
|
|||
return message;
|
||||
}
|
||||
|
||||
unsigned NSCountFrames(void)
|
||||
{
|
||||
unsigned x = 0;
|
||||
|
||||
while (NSFrameAddress(x + 1)) x++;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
#define _NS_FRAME_HACK(a) case a: val = __builtin_frame_address(a + 1); break;
|
||||
#define _NS_RETURN_HACK(a) case a: val = __builtin_return_address(a + 1); break;
|
||||
|
||||
|
@ -893,7 +886,7 @@ jbuf()
|
|||
if (d == nil)
|
||||
{
|
||||
d = [[NSMutableData alloc] initWithLength:
|
||||
sizeof(jmp_buf) + sizeof(void(*)(int))];
|
||||
sizeof(jmp_buf) + sizeof(void(*)(int)) + sizeof(void*)];
|
||||
[[[NSThread currentThread] threadDictionary] setObject: d
|
||||
forKey: @"GSjbuf"];
|
||||
RELEASE(d);
|
||||
|
@ -920,7 +913,9 @@ NSFrameAddress(int offset)
|
|||
if (setjmp(*env) == 0)
|
||||
{
|
||||
old = signal(SIGSEGV, recover);
|
||||
memcpy(env + 1, &old, sizeof(old));
|
||||
val = (void*)env;
|
||||
val += sizeof(jmp_buf);
|
||||
memcpy(val, &old, sizeof(old));
|
||||
switch (offset)
|
||||
{
|
||||
_NS_FRAME_HACK(0); _NS_FRAME_HACK(1); _NS_FRAME_HACK(2);
|
||||
|
@ -963,14 +958,88 @@ NSFrameAddress(int offset)
|
|||
}
|
||||
else
|
||||
{
|
||||
env = jbuf();
|
||||
memcpy(&old, env + 1, sizeof(old));
|
||||
val = jbuf();
|
||||
val += sizeof(jmp_buf);
|
||||
memcpy(&old, val, sizeof(old));
|
||||
signal(SIGSEGV, old);
|
||||
val = NULL;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned NSCountFrames(void)
|
||||
{
|
||||
jmp_buf *env;
|
||||
void (*old)(int);
|
||||
void *val;
|
||||
|
||||
env = jbuf();
|
||||
if (setjmp(*env) == 0)
|
||||
{
|
||||
unsigned *loc;
|
||||
|
||||
old = signal(SIGSEGV, recover);
|
||||
val = (void*)env;
|
||||
val += sizeof(jmp_buf);
|
||||
memcpy(val, &old, sizeof(old));
|
||||
val += sizeof(old);
|
||||
loc = (unsigned*)val;
|
||||
*loc = 0;
|
||||
|
||||
#define _NS_COUNT_HACK(X) if (__builtin_frame_address(X + 1) == 0) \
|
||||
goto done; else *loc = X + 1;
|
||||
|
||||
_NS_COUNT_HACK(0); _NS_COUNT_HACK(1); _NS_COUNT_HACK(2);
|
||||
_NS_COUNT_HACK(3); _NS_COUNT_HACK(4); _NS_COUNT_HACK(5);
|
||||
_NS_COUNT_HACK(6); _NS_COUNT_HACK(7); _NS_COUNT_HACK(8);
|
||||
_NS_COUNT_HACK(9); _NS_COUNT_HACK(10); _NS_COUNT_HACK(11);
|
||||
_NS_COUNT_HACK(12); _NS_COUNT_HACK(13); _NS_COUNT_HACK(14);
|
||||
_NS_COUNT_HACK(15); _NS_COUNT_HACK(16); _NS_COUNT_HACK(17);
|
||||
_NS_COUNT_HACK(18); _NS_COUNT_HACK(19); _NS_COUNT_HACK(20);
|
||||
_NS_COUNT_HACK(21); _NS_COUNT_HACK(22); _NS_COUNT_HACK(23);
|
||||
_NS_COUNT_HACK(24); _NS_COUNT_HACK(25); _NS_COUNT_HACK(26);
|
||||
_NS_COUNT_HACK(27); _NS_COUNT_HACK(28); _NS_COUNT_HACK(29);
|
||||
_NS_COUNT_HACK(30); _NS_COUNT_HACK(31); _NS_COUNT_HACK(32);
|
||||
_NS_COUNT_HACK(33); _NS_COUNT_HACK(34); _NS_COUNT_HACK(35);
|
||||
_NS_COUNT_HACK(36); _NS_COUNT_HACK(37); _NS_COUNT_HACK(38);
|
||||
_NS_COUNT_HACK(39); _NS_COUNT_HACK(40); _NS_COUNT_HACK(41);
|
||||
_NS_COUNT_HACK(42); _NS_COUNT_HACK(43); _NS_COUNT_HACK(44);
|
||||
_NS_COUNT_HACK(45); _NS_COUNT_HACK(46); _NS_COUNT_HACK(47);
|
||||
_NS_COUNT_HACK(48); _NS_COUNT_HACK(49); _NS_COUNT_HACK(50);
|
||||
_NS_COUNT_HACK(51); _NS_COUNT_HACK(52); _NS_COUNT_HACK(53);
|
||||
_NS_COUNT_HACK(54); _NS_COUNT_HACK(55); _NS_COUNT_HACK(56);
|
||||
_NS_COUNT_HACK(57); _NS_COUNT_HACK(58); _NS_COUNT_HACK(59);
|
||||
_NS_COUNT_HACK(60); _NS_COUNT_HACK(61); _NS_COUNT_HACK(62);
|
||||
_NS_COUNT_HACK(63); _NS_COUNT_HACK(64); _NS_COUNT_HACK(65);
|
||||
_NS_COUNT_HACK(66); _NS_COUNT_HACK(67); _NS_COUNT_HACK(68);
|
||||
_NS_COUNT_HACK(69); _NS_COUNT_HACK(70); _NS_COUNT_HACK(71);
|
||||
_NS_COUNT_HACK(72); _NS_COUNT_HACK(73); _NS_COUNT_HACK(74);
|
||||
_NS_COUNT_HACK(75); _NS_COUNT_HACK(76); _NS_COUNT_HACK(77);
|
||||
_NS_COUNT_HACK(78); _NS_COUNT_HACK(79); _NS_COUNT_HACK(80);
|
||||
_NS_COUNT_HACK(81); _NS_COUNT_HACK(82); _NS_COUNT_HACK(83);
|
||||
_NS_COUNT_HACK(84); _NS_COUNT_HACK(85); _NS_COUNT_HACK(86);
|
||||
_NS_COUNT_HACK(87); _NS_COUNT_HACK(88); _NS_COUNT_HACK(89);
|
||||
_NS_COUNT_HACK(90); _NS_COUNT_HACK(91); _NS_COUNT_HACK(92);
|
||||
_NS_COUNT_HACK(93); _NS_COUNT_HACK(94); _NS_COUNT_HACK(95);
|
||||
_NS_COUNT_HACK(96); _NS_COUNT_HACK(97); _NS_COUNT_HACK(98);
|
||||
_NS_COUNT_HACK(99);
|
||||
|
||||
done:
|
||||
signal(SIGSEGV, old);
|
||||
}
|
||||
else
|
||||
{
|
||||
env = jbuf();
|
||||
val = (void*)env;
|
||||
val += sizeof(jmp_buf);
|
||||
memcpy(&old, val, sizeof(old));
|
||||
signal(SIGSEGV, old);
|
||||
}
|
||||
|
||||
val = (void*)env + sizeof(jmp_buf) + sizeof(old);
|
||||
return *(unsigned*)val;
|
||||
}
|
||||
|
||||
void *
|
||||
NSReturnAddress(int offset)
|
||||
{
|
||||
|
@ -982,7 +1051,9 @@ NSReturnAddress(int offset)
|
|||
if (setjmp(*env) == 0)
|
||||
{
|
||||
old = signal(SIGSEGV, recover);
|
||||
memcpy(env + 1, &old, sizeof(old));
|
||||
val = (void*)env;
|
||||
val += sizeof(jmp_buf);
|
||||
memcpy(val, &old, sizeof(old));
|
||||
switch (offset)
|
||||
{
|
||||
_NS_RETURN_HACK(0); _NS_RETURN_HACK(1); _NS_RETURN_HACK(2);
|
||||
|
@ -1025,8 +1096,9 @@ NSReturnAddress(int offset)
|
|||
}
|
||||
else
|
||||
{
|
||||
env = jbuf();
|
||||
memcpy(&old, env + 1, sizeof(old));
|
||||
val = jbuf();
|
||||
val += sizeof(jmp_buf);
|
||||
memcpy(&old, val, sizeof(old));
|
||||
signal(SIGSEGV, old);
|
||||
val = NULL;
|
||||
}
|
||||
|
@ -1034,6 +1106,84 @@ NSReturnAddress(int offset)
|
|||
return val;
|
||||
}
|
||||
|
||||
NSMutableArray *
|
||||
GSPrivateStackAddresses(void)
|
||||
{
|
||||
unsigned n = NSCountFrames();
|
||||
NSMutableArray *stack = [NSMutableArray arrayWithCapacity: n];
|
||||
unsigned i;
|
||||
jmp_buf *env;
|
||||
void (*old)(int);
|
||||
void *val;
|
||||
|
||||
env = jbuf();
|
||||
if (setjmp(*env) == 0)
|
||||
{
|
||||
old = signal(SIGSEGV, recover);
|
||||
val = (void*)env;
|
||||
val += sizeof(jmp_buf);
|
||||
memcpy(val, &old, sizeof(old));
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
_NS_RETURN_HACK(0); _NS_RETURN_HACK(1); _NS_RETURN_HACK(2);
|
||||
_NS_RETURN_HACK(3); _NS_RETURN_HACK(4); _NS_RETURN_HACK(5);
|
||||
_NS_RETURN_HACK(6); _NS_RETURN_HACK(7); _NS_RETURN_HACK(8);
|
||||
_NS_RETURN_HACK(9); _NS_RETURN_HACK(10); _NS_RETURN_HACK(11);
|
||||
_NS_RETURN_HACK(12); _NS_RETURN_HACK(13); _NS_RETURN_HACK(14);
|
||||
_NS_RETURN_HACK(15); _NS_RETURN_HACK(16); _NS_RETURN_HACK(17);
|
||||
_NS_RETURN_HACK(18); _NS_RETURN_HACK(19); _NS_RETURN_HACK(20);
|
||||
_NS_RETURN_HACK(21); _NS_RETURN_HACK(22); _NS_RETURN_HACK(23);
|
||||
_NS_RETURN_HACK(24); _NS_RETURN_HACK(25); _NS_RETURN_HACK(26);
|
||||
_NS_RETURN_HACK(27); _NS_RETURN_HACK(28); _NS_RETURN_HACK(29);
|
||||
_NS_RETURN_HACK(30); _NS_RETURN_HACK(31); _NS_RETURN_HACK(32);
|
||||
_NS_RETURN_HACK(33); _NS_RETURN_HACK(34); _NS_RETURN_HACK(35);
|
||||
_NS_RETURN_HACK(36); _NS_RETURN_HACK(37); _NS_RETURN_HACK(38);
|
||||
_NS_RETURN_HACK(39); _NS_RETURN_HACK(40); _NS_RETURN_HACK(41);
|
||||
_NS_RETURN_HACK(42); _NS_RETURN_HACK(43); _NS_RETURN_HACK(44);
|
||||
_NS_RETURN_HACK(45); _NS_RETURN_HACK(46); _NS_RETURN_HACK(47);
|
||||
_NS_RETURN_HACK(48); _NS_RETURN_HACK(49); _NS_RETURN_HACK(50);
|
||||
_NS_RETURN_HACK(51); _NS_RETURN_HACK(52); _NS_RETURN_HACK(53);
|
||||
_NS_RETURN_HACK(54); _NS_RETURN_HACK(55); _NS_RETURN_HACK(56);
|
||||
_NS_RETURN_HACK(57); _NS_RETURN_HACK(58); _NS_RETURN_HACK(59);
|
||||
_NS_RETURN_HACK(60); _NS_RETURN_HACK(61); _NS_RETURN_HACK(62);
|
||||
_NS_RETURN_HACK(63); _NS_RETURN_HACK(64); _NS_RETURN_HACK(65);
|
||||
_NS_RETURN_HACK(66); _NS_RETURN_HACK(67); _NS_RETURN_HACK(68);
|
||||
_NS_RETURN_HACK(69); _NS_RETURN_HACK(70); _NS_RETURN_HACK(71);
|
||||
_NS_RETURN_HACK(72); _NS_RETURN_HACK(73); _NS_RETURN_HACK(74);
|
||||
_NS_RETURN_HACK(75); _NS_RETURN_HACK(76); _NS_RETURN_HACK(77);
|
||||
_NS_RETURN_HACK(78); _NS_RETURN_HACK(79); _NS_RETURN_HACK(80);
|
||||
_NS_RETURN_HACK(81); _NS_RETURN_HACK(82); _NS_RETURN_HACK(83);
|
||||
_NS_RETURN_HACK(84); _NS_RETURN_HACK(85); _NS_RETURN_HACK(86);
|
||||
_NS_RETURN_HACK(87); _NS_RETURN_HACK(88); _NS_RETURN_HACK(89);
|
||||
_NS_RETURN_HACK(90); _NS_RETURN_HACK(91); _NS_RETURN_HACK(92);
|
||||
_NS_RETURN_HACK(93); _NS_RETURN_HACK(94); _NS_RETURN_HACK(95);
|
||||
_NS_RETURN_HACK(96); _NS_RETURN_HACK(97); _NS_RETURN_HACK(98);
|
||||
_NS_RETURN_HACK(99);
|
||||
default: val = 0; break;
|
||||
}
|
||||
if (val == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
[stack addObject: [NSValue valueWithPointer: val]];
|
||||
}
|
||||
signal(SIGSEGV, old);
|
||||
}
|
||||
else
|
||||
{
|
||||
env = jbuf();
|
||||
val = (void*)env;
|
||||
val += sizeof(jmp_buf);
|
||||
memcpy(&old, val, sizeof(old));
|
||||
signal(SIGSEGV, old);
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
|
||||
const char *_NSPrintForDebugger(id object)
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#import "config.h"
|
||||
#import "GSPrivate.h"
|
||||
#import "GNUstepBase/preface.h"
|
||||
#import <Foundation/NSDebug.h>
|
||||
#import <Foundation/NSBundle.h>
|
||||
|
@ -55,6 +56,7 @@ typedef struct { @defs(NSThread) } *TInfo;
|
|||
- (NSMutableArray*) frames;
|
||||
- (id) frameAt: (unsigned)index;
|
||||
- (unsigned) frameCount;
|
||||
- (id) initWithAddresses: (NSArray*)stack;
|
||||
- (NSEnumerator*) reverseEnumerator;
|
||||
|
||||
@end
|
||||
|
@ -561,17 +563,24 @@ GSListModules()
|
|||
// grab the current stack
|
||||
- (id) init
|
||||
{
|
||||
NSMutableArray *stack = GSPrivateStackAddresses();
|
||||
|
||||
return [self initWithAddresses: stack];
|
||||
}
|
||||
|
||||
- (id) initWithAddresses: (NSArray*)stack
|
||||
{
|
||||
#if defined(STACKSYMBOLS)
|
||||
int i;
|
||||
int n;
|
||||
|
||||
frames = [[NSMutableArray alloc] init];
|
||||
n = NSCountFrames();
|
||||
n = [stack count];
|
||||
frames = [[NSMutableArray alloc] initWithCapacity: n];
|
||||
|
||||
#if defined(STACKSYMBOLS)
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
GSFunctionInfo *aFrame = nil;
|
||||
void *address = NSReturnAddress(i);
|
||||
void *address = [[stack objectAtIndex: i] pointerValue];
|
||||
void *base;
|
||||
NSString *modulePath = GSPrivateBaseAddress(address, &base);
|
||||
GSBinaryFileInfo *bfi;
|
||||
|
@ -628,12 +637,7 @@ GSListModules()
|
|||
[frames addObject: aFrame];
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
void *address = NSReturnAddress(i);
|
||||
|
||||
[frames addObject: [NSValue valueWithPointer: address]];
|
||||
}
|
||||
frames = [stack copy];
|
||||
#endif
|
||||
|
||||
return self;
|
||||
|
@ -646,35 +650,6 @@ GSListModules()
|
|||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Get a stack trace and convert it to an array of return addresses.
|
||||
*/
|
||||
@interface NSThread (Frames)
|
||||
+ (NSArray*) callStackReturnAddresses;
|
||||
@end
|
||||
@implementation NSThread (Frames)
|
||||
+ (NSArray*) callStackReturnAddresses
|
||||
{
|
||||
NSMutableArray *frames = [[GSStackTrace currentStack] frames];
|
||||
#if defined(STACKSYMBOLS)
|
||||
unsigned count = [frames count];
|
||||
|
||||
while (count-- > 0)
|
||||
{
|
||||
GSFunctionInfo *info = [frames objectAtIndex: count];
|
||||
NSValue *address;
|
||||
|
||||
address = [NSValue valueWithPointer: [info address]];
|
||||
[frames replaceObjectAtIndex: count
|
||||
withObject: address];
|
||||
}
|
||||
#endif
|
||||
return frames;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
NSString* const NSGenericException
|
||||
= @"NSGenericException";
|
||||
|
@ -697,8 +672,6 @@ NSString* const NSCharacterConversionException
|
|||
NSString* const NSParseErrorException
|
||||
= @"NSParseErrorException";
|
||||
|
||||
#include "GSPrivate.h"
|
||||
|
||||
static void _terminate()
|
||||
{
|
||||
BOOL shouldAbort;
|
||||
|
@ -826,7 +799,7 @@ _NSFoundationUncaughtExceptionHandler (NSException *exception)
|
|||
RELEASE(_e_info);
|
||||
_e_info = m;
|
||||
}
|
||||
[m setObject: [GSStackTrace currentStack] forKey: @"GSStackTraceKey"];
|
||||
[m setObject: GSPrivateStackAddresses() forKey: @"GSStackTraceKey"];
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -948,11 +921,41 @@ _NSFoundationUncaughtExceptionHandler (NSException *exception)
|
|||
- (NSString*) description
|
||||
{
|
||||
if (_e_info)
|
||||
return [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@ INFO:%@",
|
||||
{
|
||||
/* Convert stack information from an array of addresses to a stacktrace
|
||||
* for display.
|
||||
*/
|
||||
#if defined(STACKSYMBOLS)
|
||||
id o;
|
||||
|
||||
o = [_e_info objectForKey: @"GSStackTraceKey"];
|
||||
if ([o isKindOfClass: [NSArray class]] == YES)
|
||||
{
|
||||
NSMutableDictionary *m;
|
||||
|
||||
o = [[GSStackTrace alloc] initWithAddresses: o];
|
||||
if ([_e_info isKindOfClass: [NSMutableDictionary class]] == YES)
|
||||
{
|
||||
m = (NSMutableDictionary*)_e_info;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = [_e_info mutableCopy];
|
||||
RELEASE(_e_info);
|
||||
_e_info = m;
|
||||
}
|
||||
[m setObject: o forKey: @"GSStackTraceKey"];
|
||||
RELEASE(o);
|
||||
}
|
||||
#endif
|
||||
return [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@ INFO:%@",
|
||||
[super description], _e_name, _e_reason, _e_info];
|
||||
}
|
||||
else
|
||||
return [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@",
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@",
|
||||
[super description], _e_name, _e_reason];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -484,12 +484,9 @@ gnustep_base_thread_callback(void)
|
|||
|
||||
+ (NSArray*) callStackReturnAddresses
|
||||
{
|
||||
/* NB. This method is actually implemented in a category in
|
||||
* The NSException.m file as the stack trace code there is
|
||||
* able to set up an array of stack addresses and we don't
|
||||
* want to duplicate the code.
|
||||
*/
|
||||
return [self notImplemented: _cmd];
|
||||
NSMutableArray *stack = GSPrivateStackAddresses();
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue