mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
Stack tyrace generation optimisation
This commit is contained in:
parent
ee968e6eff
commit
2d3039694f
3 changed files with 184 additions and 59 deletions
10
ChangeLog
10
ChangeLog
|
@ -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
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue