Stack tyrace generation optimisation

This commit is contained in:
Richard Frith-Macdonald 2018-03-26 14:49:13 +01:00
parent ee968e6eff
commit 2d3039694f
3 changed files with 184 additions and 59 deletions

View file

@ -1,3 +1,13 @@
2018-03-26 Richard Frith-Macdonald <rfm@gnu.org>
* Source/GSPThread.h:
* Source/GSException.m:
Optimise generation of stack traces to store them in a single simple
malloced buffer and only generate an NSArray of return addresses if
actually requested. Also adjust output of stack trace to exclude the
top few frames involved in generation of the trace, so we see only
the frames of interest.
2018-03-16 Richard Frith-Macdonald <rfm@gnu.org>
* Headers/Foundation/NSLock.h: Expose pointer to function handling

View file

@ -25,6 +25,12 @@
#include <pthread.h>
#import "Foundation/NSLock.h"
@class GSStackTrace;
@class NSArray;
@class NSMapTable;
/*
* Macro to initialize recursive mutexes in a portable way. Adopted from
* libobjc2 (lock.h).
@ -48,4 +54,28 @@ static inline void GSPThreadInitRecursiveMutex(pthread_mutex_t *x)
}
# endif // PTHREAD_RECURSIVE_MUTEX_INITIALIZER(_NP)
/* Class to obtain/encapsulate a stack trace for exception reporting and/or
* lock tracing.
*/
@interface GSStackTrace : NSObject
{
NSArray *symbols;
NSArray *addresses;
@public
NSUInteger recursion; // Recursion count for lock trace
void **returns; // The return addresses on the stack
int numReturns; // Number of return addresses
}
- (NSArray*) addresses;
- (NSArray*) symbols;
@end
/* Versions of the lock classes where the locking is never traced
*/
@interface GSUntracedLock : NSLock
@end
@interface GSUntracedRecursiveLock : NSRecursiveLock
@end
#endif // _GSPThread_h_

View file

@ -32,6 +32,7 @@
#import "Foundation/NSException.h"
#import "Foundation/NSArray.h"
#import "Foundation/NSCoder.h"
#import "Foundation/NSData.h"
#import "Foundation/NSLock.h"
#import "Foundation/NSNull.h"
#import "Foundation/NSThread.h"
@ -40,6 +41,7 @@
#import "Foundation/NSValue.h"
#import "GNUstepBase/NSString+GNUstepBase.h"
#import "GSPThread.h"
#ifdef __GNUSTEP_RUNTIME__
#include <objc/hooks.h>
@ -107,17 +109,6 @@ static NSUncaughtExceptionHandler *_NSUncaughtExceptionHandler = 0;
#define _e_info (((id*)_reserved)[0])
#define _e_stack (((id*)_reserved)[1])
/* This is the GNU name for the CTOR list */
@interface GSStackTrace : NSObject
{
NSArray *symbols;
NSArray *addresses;
}
- (NSArray*) addresses;
- (NSArray*) symbols;
@end
@interface NSException (GSPrivate)
- (GSStackTrace*) _callStack;
@ -574,10 +565,6 @@ GSListModules()
#endif /* USE_BFD */
@implementation GSStackTrace : NSObject
static NSRecursiveLock *traceLock = nil;
#if defined(_WIN32) && !defined(USE_BFD)
typedef USHORT (WINAPI *CaptureStackBackTraceType)(ULONG,ULONG,PVOID*,PULONG);
typedef BOOL (WINAPI *SymInitializeType)(HANDLE,char*,BOOL);
@ -589,20 +576,47 @@ static SymInitializeType initSym = 0;
static SymSetOptionsType optSym = 0;
static SymFromAddrType fromSym = 0;
static HANDLE hProcess = 0;
static NSRecursiveLock *traceLock = nil;
#define MAXFRAMES 62 /* Limitation of windows-xp */
#else
#define MAXFRAMES 128 /* 1KB buffer on 64bit machine */
#endif
@implementation GSStackTrace : NSObject
/** Offset from the top of the stack (when we generate a trace) to the
* first frame likely to be of interest for debugging.
*/
#define FrameOffset 4
+ (void) initialize
{
#if defined(_WIN32) && !defined(USE_BFD)
if (nil == traceLock)
{
traceLock = [NSRecursiveLock new];
}
#endif
}
- (NSArray*) addresses
{
if (nil == addresses && numReturns > FrameOffset)
{
CREATE_AUTORELEASE_POOL(pool);
NSInteger count = numReturns - FrameOffset;
NSValue *objects[count];
NSUInteger index;
const void **ptrs = (const void **)returns;
for (index = 0; index < count; index++)
{
objects[index] = [NSValue valueWithPointer: ptrs[FrameOffset+index]];
}
addresses = [[NSArray alloc] initWithObjects: objects count: count];
DESTROY(pool);
}
return addresses;
}
@ -610,6 +624,11 @@ static HANDLE hProcess = 0;
{
DESTROY(addresses);
DESTROY(symbols);
if (returns != NULL)
{
free(returns);
returns = NULL;
}
[super dealloc];
}
@ -632,16 +651,25 @@ static HANDLE hProcess = 0;
return result;
}
// grab the current stack
- (id) init
{
#if defined(USE_BFD)
addresses = [GSPrivateStackAddresses() copy];
#elif defined(_WIN32)
uint16_t frames;
return [self initWithOffset: 3];
}
// grab the current stack
- (id) initWithOffset: (unsigned)o
{
#if HAVE_BACKTRACE
void *addr[MAXFRAMES*sizeof(void*)];
numReturns = backtrace(addr, MAXFRAMES*sizeof(void*));
if (numReturns > 0)
{
returns = malloc(numReturns * sizeof(void*));
memcpy(returns, addr, numReturns * sizeof(void*));
}
#elif defined(_WIN32) && !defined(USE_BFD)
NSUInteger addr[MAXFRAMES];
NSNumber *vals[MAXFRAMES];
NSUInteger i;
[traceLock lock];
if (0 == hProcess)
@ -723,34 +751,100 @@ static HANDLE hProcess = 0;
return self;
}
frames = (capture)(0, MAXFRAMES, (void**)addr, NULL);
for (i = 0; i < frames; i++)
numReturns = (capture)(0, MAXFRAMES, (void**)addr, NULL);
if (numReturns > 0)
{
vals[i] = [NSNumber numberWithUnsignedInteger: addr[i]];
returns = malloc(numReturns * sizeof(void*));
memcpy(returns, addr, numReturns * sizeof(void*));
}
[traceLock unlock];
addresses = [[NSArray alloc] initWithObjects: vals count: frames];
#elif defined(HAVE_BACKTRACE)
void **addr;
id *vals;
int count;
int i;
addr = calloc(sizeof(void*),1024);
count = backtrace(addr, 1024);
addr = realloc(addr, count * sizeof(void*));
vals = alloca(count * sizeof(id));
for (i = 0; i < count; i++)
{
vals[i] = [NSNumber numberWithUnsignedInteger:
(NSUInteger)addr[i]];
}
addresses = [[NSArray alloc] initWithObjects: vals count: count];
free(addr);
#else
addresses = [GSPrivateStackAddresses() copy];
int n;
n = NSCountFrames();
/* There should be more frame addresses than return addresses.
*/
if (n > 0)
{
n--;
}
if (n > 0)
{
n--;
}
if ((numReturns = n) > 0)
{
jbuf_type *env;
returns = malloc(numReturns * sizeof(void*));
env = jbuf();
if (sigsetjmp(env->buf, 1) == 0)
{
unsigned i;
env->segv = signal(SIGSEGV, recover);
env->bus = signal(SIGBUS, recover);
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: env->addr = 0; break;
}
if (env->addr == 0)
{
break;
}
memcpy(&returns[i], env->addr, sizeof(void*));
}
signal(SIGSEGV, env->segv);
signal(SIGBUS, env->bus);
}
else
{
env = jbuf();
signal(SIGSEGV, env->segv);
signal(SIGBUS, env->bus);
}
}
#endif
return self;
}
@ -759,7 +853,8 @@ static HANDLE hProcess = 0;
{
if (nil == symbols)
{
NSUInteger count = [addresses count];
NSInteger count = numReturns - FrameOffset;
const void **ptrs = (const void**)&returns[FrameOffset];
if (count > 0)
{
@ -772,12 +867,11 @@ static HANDLE hProcess = 0;
for (i = 0; i < count; i++)
{
GSFunctionInfo *aFrame = nil;
void *address;
void *address = (void*)*ptrs++;
void *base;
NSString *modulePath;
GSBinaryFileInfo *bfi;
address = (void*)[[addresses objectAtIndex: i] pointerValue];
modulePath = GSPrivateBaseAddress(address, &base);
if (modulePath != nil && (bfi = GSLoadModule(modulePath)) != nil)
{
@ -842,9 +936,8 @@ static HANDLE hProcess = 0;
[traceLock lock];
for (i = 0; i < count; i++)
{
NSUInteger addr;
NSUInteger addr = (NSUInteger)*ptrs++;
addr = (NSUInteger)[[addresses objectAtIndex: i] integerValue];
if ((fromSym)(hProcess, (DWORD64)addr, 0, symbol))
{
syms[i] = [NSString stringWithFormat:
@ -862,18 +955,10 @@ static HANDLE hProcess = 0;
symbols = [[NSArray alloc] initWithObjects: syms count: count];
#elif defined(HAVE_BACKTRACE)
char **strs;
void **addr;
NSString **symbolArray;
NSUInteger i;
addr = alloca(count * sizeof(void*));
for (i = 0; i < count; i++)
{
addr[i] = (void*)[[addresses objectAtIndex: i]
unsignedIntegerValue];
}
strs = backtrace_symbols(addr, count);
strs = backtrace_symbols(ptrs, count);
symbolArray = alloca(count * sizeof(NSString*));
for (i = 0; i < count; i++)
{