2001-12-17 14:31:42 +00:00
|
|
|
/** Debugging utilities for GNUStep and OpenStep
|
2001-04-19 16:10:23 +00:00
|
|
|
Copyright (C) 1997,1999,2000,2001 Free Software Foundation, Inc.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
Date: August 1997
|
2001-04-19 16:10:23 +00:00
|
|
|
Extended by: Nicola Pero <n.pero@mi.flashnet.it>
|
|
|
|
Date: December 2000, April 2001
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-09-14 11:36:11 +00:00
|
|
|
modify it under the terms of the GNU Lesser General Public
|
1997-09-01 21:59:51 +00:00
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-08 10:38:33 +00:00
|
|
|
version 2 of the License, or (at your option) any later version.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
2007-09-14 11:36:11 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
1997-09-01 21:59:51 +00:00
|
|
|
License along with this library; if not, write to the Free
|
2006-10-20 10:56:27 +00:00
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02111 USA.
|
2001-12-18 16:54:15 +00:00
|
|
|
|
2002-10-09 06:07:38 +00:00
|
|
|
<title>NSDebug utilities reference</title>
|
2001-12-18 16:54:15 +00:00
|
|
|
$Date$ $Revision$
|
1997-09-01 21:59:51 +00:00
|
|
|
*/
|
|
|
|
|
2010-02-19 08:12:46 +00:00
|
|
|
#import "common.h"
|
2001-05-29 02:38:22 +00:00
|
|
|
#include <stdio.h>
|
2010-02-17 11:47:06 +00:00
|
|
|
#import "GSPrivate.h"
|
|
|
|
#import "GNUstepBase/GSLock.h"
|
|
|
|
#import "Foundation/NSArray.h"
|
|
|
|
#import "Foundation/NSData.h"
|
2011-02-23 11:52:17 +00:00
|
|
|
#import "Foundation/NSDictionary.h"
|
2010-02-17 11:47:06 +00:00
|
|
|
#import "Foundation/NSLock.h"
|
|
|
|
#import "Foundation/NSNotification.h"
|
|
|
|
#import "Foundation/NSNotificationQueue.h"
|
|
|
|
#import "Foundation/NSThread.h"
|
|
|
|
#import "Foundation/NSValue.h"
|
1997-09-01 21:59:51 +00:00
|
|
|
|
2009-02-20 17:44:58 +00:00
|
|
|
#if HAVE_EXECINFO_H
|
|
|
|
#include <execinfo.h>
|
|
|
|
#endif
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
typedef struct {
|
1999-03-11 11:07:21 +00:00
|
|
|
Class class;
|
2001-04-19 16:10:23 +00:00
|
|
|
/* The following are used for statistical info */
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int count;
|
|
|
|
unsigned int lastc;
|
|
|
|
unsigned int total;
|
|
|
|
unsigned int peak;
|
2001-04-19 16:10:23 +00:00
|
|
|
/* The following are used to record actual objects */
|
|
|
|
BOOL is_recording;
|
|
|
|
id *recorded_objects;
|
2003-10-08 15:03:58 +00:00
|
|
|
id *recorded_tags;
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int num_recorded_objects;
|
|
|
|
unsigned int stack_size;
|
1997-09-01 21:59:51 +00:00
|
|
|
} table_entry;
|
|
|
|
|
2003-01-03 20:14:47 +00:00
|
|
|
static unsigned int num_classes = 0;
|
|
|
|
static unsigned int table_size = 0;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
static table_entry* the_table = 0;
|
|
|
|
|
|
|
|
static BOOL debug_allocation = NO;
|
|
|
|
|
2011-07-24 13:09:22 +00:00
|
|
|
static GSLazyRecursiveLock *uniqueLock = nil;
|
2000-02-19 00:40:47 +00:00
|
|
|
|
|
|
|
static const char* _GSDebugAllocationList(BOOL difference);
|
2002-11-09 06:45:31 +00:00
|
|
|
static const char* _GSDebugAllocationListAll(void);
|
2000-02-19 00:40:47 +00:00
|
|
|
|
2006-10-19 13:51:19 +00:00
|
|
|
static void _GSDebugAllocationAdd(Class c, id o);
|
|
|
|
static void _GSDebugAllocationRemove(Class c, id o);
|
2003-10-08 16:26:59 +00:00
|
|
|
|
2006-10-19 13:51:19 +00:00
|
|
|
static void (*_GSDebugAllocationAddFunc)(Class c, id o)
|
|
|
|
= _GSDebugAllocationAdd;
|
|
|
|
static void (*_GSDebugAllocationRemoveFunc)(Class c, id o)
|
|
|
|
= _GSDebugAllocationRemove;
|
2003-10-08 16:26:59 +00:00
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
@interface GSDebugAlloc : NSObject
|
|
|
|
+ (void) initialize;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSDebugAlloc
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
2004-02-08 09:42:38 +00:00
|
|
|
uniqueLock = [GSLazyRecursiveLock new];
|
2000-02-19 00:40:47 +00:00
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2003-10-08 16:26:59 +00:00
|
|
|
/**
|
2005-11-06 13:53:40 +00:00
|
|
|
* This functions allows to set own function callbacks for debugging allocation
|
|
|
|
* of objects. Useful if you intend to write your own object allocation code.
|
2003-10-08 16:26:59 +00:00
|
|
|
*/
|
2005-02-22 11:22:44 +00:00
|
|
|
void
|
2003-10-08 16:26:59 +00:00
|
|
|
GSSetDebugAllocationFunctions(void (*newAddObjectFunc)(Class c, id o),
|
|
|
|
void (*newRemoveObjectFunc)(Class c, id o))
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
2003-10-08 16:26:59 +00:00
|
|
|
|
|
|
|
if (newAddObjectFunc && newRemoveObjectFunc)
|
|
|
|
{
|
|
|
|
_GSDebugAllocationAddFunc = newAddObjectFunc;
|
|
|
|
_GSDebugAllocationRemoveFunc = newRemoveObjectFunc;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Back to default
|
|
|
|
_GSDebugAllocationAddFunc = _GSDebugAllocationAdd;
|
|
|
|
_GSDebugAllocationRemoveFunc = _GSDebugAllocationRemove;
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2003-10-08 16:26:59 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
2002-10-15 13:37:21 +00:00
|
|
|
* This function activates or deactivates object allocation debugging.<br />
|
|
|
|
* Returns the previous state.<br />
|
|
|
|
* You should call this function to activate
|
|
|
|
* allocation debugging before using any of the other allocation
|
|
|
|
* debugging functions such as GSDebugAllocationList() or
|
|
|
|
* GSDebugAllocationTotal().<br />
|
|
|
|
* Object allocation debugging
|
2002-08-20 10:22:05 +00:00
|
|
|
* should not affect performance too much, and is very useful
|
|
|
|
* as it allows you to monitor how many objects of each class
|
2002-10-15 13:37:21 +00:00
|
|
|
* your application has allocated.
|
2002-08-20 10:22:05 +00:00
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
BOOL
|
|
|
|
GSDebugAllocationActive(BOOL active)
|
|
|
|
{
|
1999-03-11 11:07:21 +00:00
|
|
|
BOOL old = debug_allocation;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
[GSDebugAlloc class]; /* Ensure thread support is working */
|
2004-09-27 06:58:23 +00:00
|
|
|
debug_allocation = active ? YES : NO;
|
1999-03-11 11:07:21 +00:00
|
|
|
return old;
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
2002-10-15 13:37:21 +00:00
|
|
|
* This function activates tracking all allocated instances of
|
|
|
|
* the specified class c.<br />
|
|
|
|
* This tracking can slow your
|
2002-08-20 10:22:05 +00:00
|
|
|
* application down, so you should use it only when you are
|
|
|
|
* into serious debugging. Usually, you will monitor your
|
2002-10-15 13:37:21 +00:00
|
|
|
* application by using the functions GSDebugAllocationList()
|
|
|
|
* and similar, which do not slow things down much and return
|
|
|
|
* the number of allocated instances; when
|
2002-08-20 10:22:05 +00:00
|
|
|
* (if) by studying the reports generated by these functions
|
|
|
|
* you have found a leak of objects of a certain class, and
|
|
|
|
* if you can't figure out how to fix it by looking at the
|
|
|
|
* code, you can use this function to start tracking
|
|
|
|
* allocated instances of that class, and the following one
|
|
|
|
* can sometime allow you to list the leaked objects directly.
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
void
|
2001-04-19 16:10:23 +00:00
|
|
|
GSDebugAllocationActiveRecordingObjects(Class c)
|
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2001-04-19 16:10:23 +00:00
|
|
|
GSDebugAllocationActive(YES);
|
|
|
|
|
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
if (the_table[i].class == c)
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
2001-04-19 16:10:23 +00:00
|
|
|
the_table[i].is_recording = YES;
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2001-04-19 16:10:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
2001-04-19 16:10:23 +00:00
|
|
|
if (num_classes >= table_size)
|
|
|
|
{
|
|
|
|
int more = table_size + 128;
|
|
|
|
table_entry *tmp;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2001-04-19 16:10:23 +00:00
|
|
|
tmp = NSZoneMalloc(NSDefaultMallocZone(), more * sizeof(table_entry));
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2001-04-19 16:10:23 +00:00
|
|
|
if (tmp == 0)
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2001-04-19 16:10:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (the_table)
|
|
|
|
{
|
|
|
|
memcpy(tmp, the_table, num_classes * sizeof(table_entry));
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), the_table);
|
|
|
|
}
|
|
|
|
the_table = tmp;
|
|
|
|
table_size = more;
|
|
|
|
}
|
|
|
|
the_table[num_classes].class = c;
|
|
|
|
the_table[num_classes].count = 0;
|
|
|
|
the_table[num_classes].lastc = 0;
|
|
|
|
the_table[num_classes].total = 0;
|
|
|
|
the_table[num_classes].peak = 0;
|
|
|
|
the_table[num_classes].is_recording = YES;
|
|
|
|
the_table[num_classes].recorded_objects = NULL;
|
2003-10-08 15:03:58 +00:00
|
|
|
the_table[num_classes].recorded_tags = NULL;
|
2001-04-19 16:10:23 +00:00
|
|
|
the_table[num_classes].num_recorded_objects = 0;
|
|
|
|
the_table[num_classes].stack_size = 0;
|
|
|
|
num_classes++;
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2001-04-19 16:10:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
GSDebugAllocationAdd(Class c, id o)
|
2003-10-08 16:26:59 +00:00
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
(*_GSDebugAllocationAddFunc)(c,o);
|
2003-10-08 16:26:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_GSDebugAllocationAdd(Class c, id o)
|
1997-09-01 21:59:51 +00:00
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
if (debug_allocation == YES)
|
1999-03-11 11:07:21 +00:00
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
1999-03-11 11:07:21 +00:00
|
|
|
|
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
if (the_table[i].class == c)
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
1999-03-11 11:07:21 +00:00
|
|
|
the_table[i].count++;
|
1999-04-19 14:29:52 +00:00
|
|
|
the_table[i].total++;
|
2000-12-11 23:28:08 +00:00
|
|
|
if (the_table[i].count > the_table[i].peak)
|
|
|
|
{
|
|
|
|
the_table[i].peak = the_table[i].count;
|
|
|
|
}
|
2001-04-19 16:10:23 +00:00
|
|
|
if (the_table[i].is_recording == YES)
|
|
|
|
{
|
2003-10-08 15:03:58 +00:00
|
|
|
if (the_table[i].num_recorded_objects
|
|
|
|
>= the_table[i].stack_size)
|
2001-04-19 16:10:23 +00:00
|
|
|
{
|
2003-10-08 15:03:58 +00:00
|
|
|
int more = the_table[i].stack_size + 128;
|
2001-04-19 16:10:23 +00:00
|
|
|
id *tmp;
|
2003-10-08 15:03:58 +00:00
|
|
|
id *tmp1;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
|
|
tmp = NSZoneMalloc(NSDefaultMallocZone(),
|
2001-04-19 16:10:23 +00:00
|
|
|
more * sizeof(id));
|
|
|
|
if (tmp == 0)
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2001-04-19 16:10:23 +00:00
|
|
|
return;
|
|
|
|
}
|
2003-10-08 15:03:58 +00:00
|
|
|
|
2005-02-22 11:22:44 +00:00
|
|
|
tmp1 = NSZoneMalloc(NSDefaultMallocZone(),
|
2003-10-08 15:03:58 +00:00
|
|
|
more * sizeof(id));
|
|
|
|
if (tmp1 == 0)
|
|
|
|
{
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), tmp);
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2003-10-08 15:03:58 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-04-19 16:10:23 +00:00
|
|
|
if (the_table[i].recorded_objects != NULL)
|
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
memcpy(tmp, the_table[i].recorded_objects,
|
|
|
|
the_table[i].num_recorded_objects
|
2001-04-19 16:10:23 +00:00
|
|
|
* sizeof(id));
|
2005-02-22 11:22:44 +00:00
|
|
|
NSZoneFree(NSDefaultMallocZone(),
|
2001-04-19 16:10:23 +00:00
|
|
|
the_table[i].recorded_objects);
|
2005-02-22 11:22:44 +00:00
|
|
|
memcpy(tmp1, the_table[i].recorded_tags,
|
|
|
|
the_table[i].num_recorded_objects
|
2003-10-08 15:03:58 +00:00
|
|
|
* sizeof(id));
|
2005-02-22 11:22:44 +00:00
|
|
|
NSZoneFree(NSDefaultMallocZone(),
|
2003-10-08 15:03:58 +00:00
|
|
|
the_table[i].recorded_tags);
|
2001-04-19 16:10:23 +00:00
|
|
|
}
|
|
|
|
the_table[i].recorded_objects = tmp;
|
2003-10-08 15:03:58 +00:00
|
|
|
the_table[i].recorded_tags = tmp1;
|
2001-04-19 16:10:23 +00:00
|
|
|
the_table[i].stack_size = more;
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2001-04-19 16:10:23 +00:00
|
|
|
(the_table[i].recorded_objects)
|
|
|
|
[the_table[i].num_recorded_objects] = o;
|
2003-10-08 15:03:58 +00:00
|
|
|
(the_table[i].recorded_tags)
|
|
|
|
[the_table[i].num_recorded_objects] = nil;
|
2001-04-19 16:10:23 +00:00
|
|
|
the_table[i].num_recorded_objects++;
|
|
|
|
}
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
1999-03-11 11:07:21 +00:00
|
|
|
return;
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
}
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
1999-03-11 11:07:21 +00:00
|
|
|
if (num_classes >= table_size)
|
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int more = table_size + 128;
|
1999-09-28 11:10:34 +00:00
|
|
|
table_entry *tmp;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
1999-09-28 11:10:34 +00:00
|
|
|
tmp = NSZoneMalloc(NSDefaultMallocZone(), more * sizeof(table_entry));
|
2005-02-22 11:22:44 +00:00
|
|
|
|
1999-03-11 11:07:21 +00:00
|
|
|
if (tmp == 0)
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
1999-03-11 11:07:21 +00:00
|
|
|
return; /* Argh */
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
if (the_table)
|
|
|
|
{
|
|
|
|
memcpy(tmp, the_table, num_classes * sizeof(table_entry));
|
1999-09-28 11:10:34 +00:00
|
|
|
NSZoneFree(NSDefaultMallocZone(), the_table);
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
the_table = tmp;
|
2000-08-16 11:57:04 +00:00
|
|
|
table_size = more;
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
the_table[num_classes].class = c;
|
|
|
|
the_table[num_classes].count = 1;
|
|
|
|
the_table[num_classes].lastc = 0;
|
1999-04-19 14:29:52 +00:00
|
|
|
the_table[num_classes].total = 1;
|
2000-12-11 23:28:08 +00:00
|
|
|
the_table[num_classes].peak = 1;
|
2001-04-19 16:10:23 +00:00
|
|
|
the_table[num_classes].is_recording = NO;
|
|
|
|
the_table[num_classes].recorded_objects = NULL;
|
2003-10-08 15:03:58 +00:00
|
|
|
the_table[num_classes].recorded_tags = NULL;
|
2001-04-19 16:10:23 +00:00
|
|
|
the_table[num_classes].num_recorded_objects = 0;
|
|
|
|
the_table[num_classes].stack_size = 0;
|
1999-03-11 11:07:21 +00:00
|
|
|
num_classes++;
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* <p>
|
2002-10-15 13:37:21 +00:00
|
|
|
* Returns the number
|
2002-08-20 10:22:05 +00:00
|
|
|
* of instances of the specified class which are currently
|
|
|
|
* allocated. This number is very important to detect memory
|
|
|
|
* leaks. If you notice that this number is constantly
|
|
|
|
* increasing without apparent reason, it is very likely a
|
|
|
|
* memory leak - you need to check that you are correctly
|
|
|
|
* releasing objects of this class, otherwise when your
|
|
|
|
* application runs for a long time, it will eventually
|
|
|
|
* allocate so many objects as to eat up all your system's
|
|
|
|
* memory ...
|
|
|
|
* </p>
|
2005-02-22 11:22:44 +00:00
|
|
|
* <p>
|
2002-08-20 10:22:05 +00:00
|
|
|
* This function, like the ones below, returns the number of
|
|
|
|
* objects allocated/released from the time when
|
2002-10-15 13:37:21 +00:00
|
|
|
* GSDebugAllocationActive() was first called. A negative
|
2002-08-20 10:22:05 +00:00
|
|
|
* number means that in total, there are less objects of this
|
|
|
|
* class allocated now than there were when you called
|
2002-10-15 13:37:21 +00:00
|
|
|
* GSDebugAllocationActive(); a positive one means there are
|
2002-08-20 10:22:05 +00:00
|
|
|
* more.
|
|
|
|
* </p>
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
int
|
|
|
|
GSDebugAllocationCount(Class c)
|
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
1999-03-11 11:07:21 +00:00
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
if (the_table[i].class == c)
|
|
|
|
{
|
|
|
|
return the_table[i].count;
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
return 0;
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
2002-10-15 13:37:21 +00:00
|
|
|
* Returns the total
|
|
|
|
* number of instances of the specified class c which have been
|
2002-08-20 10:22:05 +00:00
|
|
|
* allocated - basically the number of times you have
|
|
|
|
* allocated an object of this class. If this number is very
|
|
|
|
* high, it means you are creating a lot of objects of this
|
|
|
|
* class; even if you are releasing them correctly, you must
|
|
|
|
* not forget that allocating and deallocating objects is
|
|
|
|
* usually one of the slowest things you can do, so you might
|
|
|
|
* want to consider whether you can reduce the number of
|
|
|
|
* allocations and deallocations that you are doing - for
|
|
|
|
* example, by recycling objects of this class, uniquing
|
|
|
|
* them, and/or using some sort of flyweight pattern. It
|
|
|
|
* might also be possible that you are unnecessarily creating
|
|
|
|
* too many objects of this class. Well - of course some times
|
|
|
|
* there is nothing you can do about it.
|
|
|
|
*/
|
2000-12-11 23:28:08 +00:00
|
|
|
int
|
|
|
|
GSDebugAllocationTotal(Class c)
|
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
2000-12-11 23:28:08 +00:00
|
|
|
|
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
if (the_table[i].class == c)
|
|
|
|
{
|
|
|
|
return the_table[i].total;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
2002-10-15 13:37:21 +00:00
|
|
|
* Returns the peak
|
2002-08-20 10:22:05 +00:00
|
|
|
* number of instances of the specified class which have been
|
|
|
|
* concurrently allocated. If this number is very high, it
|
|
|
|
* means at some point in time you had a situation with a
|
|
|
|
* huge number of objects of this class allocated - this is
|
|
|
|
* an indicator that probably at some point in time your
|
|
|
|
* application was using a lot of memory - so you might want
|
|
|
|
* to investigate whether you can prevent this problem by
|
|
|
|
* inserting autorelease pools in your application's
|
|
|
|
* processing loops.
|
|
|
|
*/
|
2000-12-11 23:28:08 +00:00
|
|
|
int
|
|
|
|
GSDebugAllocationPeak(Class c)
|
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
2000-12-11 23:28:08 +00:00
|
|
|
|
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
if (the_table[i].class == c)
|
|
|
|
{
|
|
|
|
return the_table[i].peak;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
2002-10-15 13:37:21 +00:00
|
|
|
* This function returns a NULL
|
2002-08-20 10:22:05 +00:00
|
|
|
* terminated array listing all the classes for which
|
|
|
|
* statistical information has been collected. Usually, you
|
|
|
|
* call this function, and then loop on all the classes returned,
|
|
|
|
* and for each one you get current, peak and total count by
|
2002-10-15 13:37:21 +00:00
|
|
|
* using GSDebugAllocationCount(), GSDebugAllocationPeak() and
|
|
|
|
* GSDebugAllocationTotal().
|
2002-08-20 10:22:05 +00:00
|
|
|
*/
|
2000-12-11 23:28:08 +00:00
|
|
|
Class *
|
|
|
|
GSDebugAllocationClassList()
|
|
|
|
{
|
|
|
|
Class *ans;
|
|
|
|
size_t siz;
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
2000-12-11 23:28:08 +00:00
|
|
|
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2000-12-11 23:28:08 +00:00
|
|
|
siz = sizeof(Class) * (num_classes + 1);
|
|
|
|
ans = NSZoneMalloc(NSDefaultMallocZone(), siz);
|
|
|
|
|
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
ans[i] = the_table[i].class;
|
|
|
|
}
|
|
|
|
ans[num_classes] = NULL;
|
|
|
|
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2000-12-11 23:28:08 +00:00
|
|
|
|
|
|
|
return ans;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
2002-10-15 13:37:21 +00:00
|
|
|
* This function returns a newline
|
2002-08-20 10:22:05 +00:00
|
|
|
* separated list of the classes which have instances
|
|
|
|
* allocated, and the instance counts. If the 'changeFlag'
|
|
|
|
* argument is YES then the list gives the number of
|
|
|
|
* instances allocated/deallocated since the function was
|
|
|
|
* last called. This function only returns the current count
|
|
|
|
* of instances (not the peak or total count), but its output
|
|
|
|
* is ready to be displayed or logged.
|
1997-09-01 21:59:51 +00:00
|
|
|
*/
|
|
|
|
const char*
|
2002-02-13 22:25:38 +00:00
|
|
|
GSDebugAllocationList(BOOL changeFlag)
|
2000-02-19 00:40:47 +00:00
|
|
|
{
|
2003-07-25 05:31:52 +00:00
|
|
|
const char *ans;
|
|
|
|
NSData *d;
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
if (debug_allocation == NO)
|
|
|
|
{
|
|
|
|
return "Debug allocation system is not active!\n";
|
|
|
|
}
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
2002-02-13 22:25:38 +00:00
|
|
|
ans = _GSDebugAllocationList(changeFlag);
|
2003-07-25 05:31:52 +00:00
|
|
|
d = [NSData dataWithBytes: ans length: strlen(ans) + 1];
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2003-07-25 05:31:52 +00:00
|
|
|
return (const char*)[d bytes];
|
2000-02-19 00:40:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char*
|
|
|
|
_GSDebugAllocationList(BOOL difference)
|
1997-09-01 21:59:51 +00:00
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int pos = 0;
|
|
|
|
unsigned int i;
|
|
|
|
static unsigned int siz = 0;
|
1999-03-11 11:07:21 +00:00
|
|
|
static char *buf = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
int val = the_table[i].count;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
1999-03-11 11:07:21 +00:00
|
|
|
if (difference)
|
|
|
|
{
|
|
|
|
val -= the_table[i].lastc;
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
if (val != 0)
|
|
|
|
{
|
2011-03-07 15:34:06 +00:00
|
|
|
pos += 22 + strlen(class_getName(the_table[i].class));
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
if (pos == 0)
|
|
|
|
{
|
|
|
|
if (difference)
|
|
|
|
{
|
|
|
|
return "There are NO newly allocated or deallocated object!\n";
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return "I can find NO allocated object!\n";
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
}
|
2000-02-19 00:40:47 +00:00
|
|
|
|
1999-03-11 11:07:21 +00:00
|
|
|
pos++;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
1999-03-11 11:07:21 +00:00
|
|
|
if (pos > siz)
|
|
|
|
{
|
|
|
|
if (pos & 0xff)
|
|
|
|
{
|
|
|
|
pos = ((pos >> 8) + 1) << 8;
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
siz = pos;
|
|
|
|
if (buf)
|
|
|
|
{
|
1999-09-28 11:10:34 +00:00
|
|
|
NSZoneFree(NSDefaultMallocZone(), buf);
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-09-28 11:10:34 +00:00
|
|
|
buf = NSZoneMalloc(NSDefaultMallocZone(), siz);
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
|
1999-03-11 11:07:21 +00:00
|
|
|
if (buf)
|
|
|
|
{
|
|
|
|
pos = 0;
|
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
int val = the_table[i].count;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
1999-03-11 11:07:21 +00:00
|
|
|
if (difference)
|
|
|
|
{
|
|
|
|
val -= the_table[i].lastc;
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
the_table[i].lastc = the_table[i].count;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
1999-03-11 11:07:21 +00:00
|
|
|
if (val != 0)
|
|
|
|
{
|
2011-03-07 15:34:06 +00:00
|
|
|
snprintf(&buf[pos], siz - pos, "%d\t%s\n",
|
|
|
|
val, class_getName(the_table[i].class));
|
1999-03-11 11:07:21 +00:00
|
|
|
pos += strlen(&buf[pos]);
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-03-11 11:07:21 +00:00
|
|
|
return buf;
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
2002-10-15 13:37:21 +00:00
|
|
|
* This function returns a newline
|
2002-08-20 10:22:05 +00:00
|
|
|
* separated list of the classes which have had instances
|
|
|
|
* allocated at any point, and the total count of the number
|
|
|
|
* of instances allocated for each class. The difference with
|
2002-10-15 13:37:21 +00:00
|
|
|
* GSDebugAllocationList() is that this function returns also
|
2002-08-20 10:22:05 +00:00
|
|
|
* classes which have no objects allocated at the moment, but
|
|
|
|
* which had in the past.
|
|
|
|
*/
|
1999-04-19 14:29:52 +00:00
|
|
|
const char*
|
|
|
|
GSDebugAllocationListAll()
|
2000-02-19 00:40:47 +00:00
|
|
|
{
|
2003-07-25 05:31:52 +00:00
|
|
|
const char *ans;
|
|
|
|
NSData *d;
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
if (debug_allocation == NO)
|
|
|
|
{
|
|
|
|
return "Debug allocation system is not active!\n";
|
|
|
|
}
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
2000-02-19 00:40:47 +00:00
|
|
|
ans = _GSDebugAllocationListAll();
|
2003-07-25 05:31:52 +00:00
|
|
|
d = [NSData dataWithBytes: ans length: strlen(ans)+1];
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2003-07-25 05:31:52 +00:00
|
|
|
return (const char*)[d bytes];
|
2000-02-19 00:40:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char*
|
2002-11-09 06:45:31 +00:00
|
|
|
_GSDebugAllocationListAll(void)
|
1999-04-19 14:29:52 +00:00
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int pos = 0;
|
|
|
|
unsigned int i;
|
|
|
|
static unsigned int siz = 0;
|
1999-04-19 14:29:52 +00:00
|
|
|
static char *buf = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
int val = the_table[i].total;
|
|
|
|
|
|
|
|
if (val != 0)
|
|
|
|
{
|
2011-03-07 15:34:06 +00:00
|
|
|
pos += 22 + strlen(class_getName(the_table[i].class));
|
1999-04-19 14:29:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pos == 0)
|
|
|
|
{
|
|
|
|
return "I can find NO allocated object!\n";
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
|
|
|
|
if (pos > siz)
|
|
|
|
{
|
|
|
|
if (pos & 0xff)
|
|
|
|
{
|
|
|
|
pos = ((pos >> 8) + 1) << 8;
|
|
|
|
}
|
|
|
|
siz = pos;
|
|
|
|
if (buf)
|
|
|
|
{
|
1999-09-28 11:10:34 +00:00
|
|
|
NSZoneFree(NSDefaultMallocZone(), buf);
|
1999-04-19 14:29:52 +00:00
|
|
|
}
|
1999-09-28 11:10:34 +00:00
|
|
|
buf = NSZoneMalloc(NSDefaultMallocZone(), siz);
|
1999-04-19 14:29:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (buf)
|
|
|
|
{
|
|
|
|
pos = 0;
|
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
int val = the_table[i].total;
|
|
|
|
|
|
|
|
if (val != 0)
|
|
|
|
{
|
2011-03-07 15:34:06 +00:00
|
|
|
snprintf(&buf[pos], siz - pos, "%d\t%s\n",
|
|
|
|
val, class_getName(the_table[i].class));
|
1999-04-19 14:29:52 +00:00
|
|
|
pos += strlen(&buf[pos]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
void
|
2001-04-19 16:10:23 +00:00
|
|
|
GSDebugAllocationRemove(Class c, id o)
|
2003-10-08 16:26:59 +00:00
|
|
|
{
|
2005-02-22 14:06:28 +00:00
|
|
|
(*_GSDebugAllocationRemoveFunc)(c,o);
|
2003-10-08 16:26:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_GSDebugAllocationRemove(Class c, id o)
|
1997-09-01 21:59:51 +00:00
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
if (debug_allocation == YES)
|
1999-03-11 11:07:21 +00:00
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
1999-03-11 11:07:21 +00:00
|
|
|
|
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
if (the_table[i].class == c)
|
|
|
|
{
|
2003-10-08 15:03:58 +00:00
|
|
|
id tag = nil;
|
|
|
|
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
1999-03-11 11:07:21 +00:00
|
|
|
the_table[i].count--;
|
2001-04-19 16:10:23 +00:00
|
|
|
if (the_table[i].is_recording)
|
|
|
|
{
|
|
|
|
unsigned j, k;
|
|
|
|
|
|
|
|
for (j = 0; j < the_table[i].num_recorded_objects; j++)
|
|
|
|
{
|
|
|
|
if ((the_table[i].recorded_objects)[j] == o)
|
|
|
|
{
|
2003-10-08 15:03:58 +00:00
|
|
|
tag = (the_table[i].recorded_tags)[j];
|
2001-04-19 16:10:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (j < the_table[i].num_recorded_objects)
|
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
for (k = j;
|
|
|
|
k + 1 < the_table[i].num_recorded_objects;
|
2001-04-19 16:10:23 +00:00
|
|
|
k++)
|
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
(the_table[i].recorded_objects)[k] =
|
2001-04-19 16:10:23 +00:00
|
|
|
(the_table[i].recorded_objects)[k + 1];
|
2005-02-22 11:22:44 +00:00
|
|
|
(the_table[i].recorded_tags)[k] =
|
2003-10-08 15:03:58 +00:00
|
|
|
(the_table[i].recorded_tags)[k + 1];
|
2001-04-19 16:10:23 +00:00
|
|
|
}
|
|
|
|
the_table[i].num_recorded_objects--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Not found - no problem - this happens if the
|
|
|
|
object was allocated before we started
|
|
|
|
recording */
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2011-02-28 19:49:57 +00:00
|
|
|
[tag release];
|
1999-03-11 11:07:21 +00:00
|
|
|
return;
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-10-08 15:03:58 +00:00
|
|
|
/**
|
|
|
|
* This function associates the supplied tag with a recorded
|
|
|
|
* object and returns the tag which was previously associated
|
|
|
|
* with it (if any).<br />
|
|
|
|
* If the object was not recorded, the method returns nil<br />
|
|
|
|
* The tag is retained while it is associated with the object.
|
|
|
|
*/
|
|
|
|
id
|
|
|
|
GSDebugAllocationTagRecordedObject(id object, id tag)
|
|
|
|
{
|
|
|
|
Class c = [object class];
|
|
|
|
id o = nil;
|
|
|
|
int i;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
if (debug_allocation == NO)
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2003-10-08 15:03:58 +00:00
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
if (the_table[i].class == c)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2003-10-08 15:03:58 +00:00
|
|
|
if (i == num_classes
|
|
|
|
|| the_table[i].is_recording == NO
|
|
|
|
|| the_table[i].num_recorded_objects == 0)
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2003-10-08 15:03:58 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < the_table[i].num_recorded_objects; j++)
|
|
|
|
{
|
|
|
|
if (the_table[i].recorded_objects[j] == object)
|
|
|
|
{
|
|
|
|
o = the_table[i].recorded_tags[j];
|
|
|
|
the_table[i].recorded_tags[j] = RETAIN(tag);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2003-10-08 15:03:58 +00:00
|
|
|
return AUTORELEASE(o);
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
2002-10-15 13:37:21 +00:00
|
|
|
* This function returns an array
|
2002-08-20 10:22:05 +00:00
|
|
|
* containing all the allocated objects of a certain class
|
2002-10-15 13:37:21 +00:00
|
|
|
* which have been recorded ... to start the recording, you need
|
2002-11-04 08:06:48 +00:00
|
|
|
* to invoke GSDebugAllocationActiveRecordingObjects().
|
2003-07-22 08:52:37 +00:00
|
|
|
* Presumably, you will immediately call [NSObject-description] on them
|
2002-08-20 10:22:05 +00:00
|
|
|
* to find out the objects you are leaking. The objects are
|
|
|
|
* returned in an array, so until the array is autoreleased,
|
|
|
|
* the objects are not released.
|
|
|
|
*/
|
2001-04-19 16:10:23 +00:00
|
|
|
NSArray *
|
|
|
|
GSDebugAllocationListRecordedObjects(Class c)
|
|
|
|
{
|
|
|
|
NSArray *answer;
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i, k;
|
2001-04-19 16:10:23 +00:00
|
|
|
id *tmp;
|
|
|
|
|
|
|
|
if (debug_allocation == NO)
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock lock];
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2001-04-19 16:10:23 +00:00
|
|
|
for (i = 0; i < num_classes; i++)
|
|
|
|
{
|
|
|
|
if (the_table[i].class == c)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2001-04-19 16:10:23 +00:00
|
|
|
if (i == num_classes)
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2001-04-19 16:10:23 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (the_table[i].is_recording == NO)
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2001-04-19 16:10:23 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (the_table[i].num_recorded_objects == 0)
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2001-04-19 16:10:23 +00:00
|
|
|
return [NSArray array];
|
|
|
|
}
|
|
|
|
|
2005-02-22 11:22:44 +00:00
|
|
|
tmp = NSZoneMalloc(NSDefaultMallocZone(),
|
2001-04-19 16:10:23 +00:00
|
|
|
the_table[i].num_recorded_objects * sizeof(id));
|
|
|
|
if (tmp == 0)
|
|
|
|
{
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2001-04-19 16:10:23 +00:00
|
|
|
return nil;
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2001-04-19 16:10:23 +00:00
|
|
|
/* First, we copy the objects into a temporary buffer */
|
2005-02-22 11:22:44 +00:00
|
|
|
memcpy(tmp, the_table[i].recorded_objects,
|
2001-04-19 16:10:23 +00:00
|
|
|
the_table[i].num_recorded_objects * sizeof(id));
|
|
|
|
|
|
|
|
/* Retain all the objects - NB: if retaining one of the objects as a
|
2009-01-12 12:48:46 +00:00
|
|
|
side effect eleases another one of them , we are broken ... */
|
|
|
|
#if !GS_WITH_GC
|
2001-04-19 16:10:23 +00:00
|
|
|
for (k = 0; k < the_table[i].num_recorded_objects; k++)
|
|
|
|
{
|
2009-01-12 12:48:46 +00:00
|
|
|
[tmp[k] retain];
|
2001-04-19 16:10:23 +00:00
|
|
|
}
|
2009-01-12 12:48:46 +00:00
|
|
|
#endif
|
2001-04-19 16:10:23 +00:00
|
|
|
|
2003-10-08 15:03:58 +00:00
|
|
|
/* Then, we bravely unlock the lock */
|
2004-09-27 06:58:23 +00:00
|
|
|
[uniqueLock unlock];
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2001-04-19 16:10:23 +00:00
|
|
|
/* Only then we create an array with them - this is now safe as we
|
|
|
|
have copied the objects out, unlocked, and retained them. */
|
2005-02-22 11:22:44 +00:00
|
|
|
answer = [NSArray arrayWithObjects: tmp
|
2001-04-19 16:10:23 +00:00
|
|
|
count: the_table[i].num_recorded_objects];
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2001-04-19 16:10:23 +00:00
|
|
|
/* Now we release all the objects to balance the retain */
|
|
|
|
for (k = 0; k < the_table[i].num_recorded_objects; k++)
|
|
|
|
{
|
|
|
|
RELEASE (tmp[k]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* And free the space used by them */
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), tmp);
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
|
|
return answer;
|
2001-04-19 16:10:23 +00:00
|
|
|
}
|
|
|
|
|
2011-03-06 14:23:57 +00:00
|
|
|
#if !defined(HAVE_BUILTIN_EXTRACT_RETURN_ADDRESS)
|
|
|
|
# define __builtin_extract_return_address(X) X
|
|
|
|
#endif
|
2001-04-19 16:10:23 +00:00
|
|
|
|
2008-03-16 18:17:49 +00:00
|
|
|
#define _NS_FRAME_HACK(a) \
|
|
|
|
case a: env->addr = __builtin_frame_address(a + 1); break;
|
|
|
|
#define _NS_RETURN_HACK(a) \
|
2011-03-06 14:23:57 +00:00
|
|
|
case a: env->addr = (__builtin_frame_address(a + 1) ? \
|
|
|
|
__builtin_extract_return_address(__builtin_return_address(a + 1)) : 0); break;
|
2007-02-04 08:43:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The following horrible signal handling code is a workaround for the fact
|
|
|
|
* that the __builtin_frame_address() and __builtin_return_address()
|
|
|
|
* functions are not reliable (at least not on my EM64T based system) and
|
|
|
|
* will sometimes walk off the stack and access illegal memory locations.
|
|
|
|
* In order to prevent such an occurrance from crashing the application,
|
2009-03-21 15:31:52 +00:00
|
|
|
* we use sigsetjmp() and siglongjmp() to ensure that we can recover, and
|
2007-02-04 08:43:16 +00:00
|
|
|
* we keep the jump buffer in thread-local memory to avoid possible thread
|
|
|
|
* safety issues.
|
|
|
|
* Of course this will fail horribly if an exception occurs in one of the
|
|
|
|
* few methods we use to manage the per-thread jump buffer.
|
|
|
|
*/
|
2011-12-15 09:42:39 +00:00
|
|
|
#if defined(HAVE_SYS_SIGNAL_H)
|
|
|
|
# include <sys/signal.h>
|
|
|
|
#elif defined(HAVE_SIGNAL_H)
|
|
|
|
# include <signal.h>
|
|
|
|
#endif
|
|
|
|
|
2009-03-21 15:31:52 +00:00
|
|
|
#include <setjmp.h>
|
2007-02-04 08:43:16 +00:00
|
|
|
|
2010-03-19 12:10:11 +00:00
|
|
|
#if defined(__MINGW__)
|
2008-11-17 13:45:32 +00:00
|
|
|
#ifndef SIGBUS
|
2008-03-18 15:53:45 +00:00
|
|
|
#define SIGBUS SIGILL
|
2007-02-04 12:53:43 +00:00
|
|
|
#endif
|
2008-11-17 13:45:32 +00:00
|
|
|
#endif
|
2007-02-04 12:53:43 +00:00
|
|
|
|
2009-03-21 15:31:52 +00:00
|
|
|
/* sigsetjmp may be a function or a macro. The test for the function is
|
|
|
|
* done at configure time so we can tell here if either is available.
|
|
|
|
*/
|
|
|
|
#if !defined(HAVE_SIGSETJMP) && !defined(sigsetjmp)
|
|
|
|
#define siglongjmp(A,B) longjmp(A,B)
|
|
|
|
#define sigsetjmp(A,B) setjmp(A)
|
|
|
|
#define sigjmp_buf jmp_buf
|
|
|
|
#endif
|
|
|
|
|
2008-03-16 18:17:49 +00:00
|
|
|
typedef struct {
|
2009-03-21 15:31:52 +00:00
|
|
|
sigjmp_buf buf;
|
2008-03-16 18:17:49 +00:00
|
|
|
void *addr;
|
|
|
|
void (*bus)(int);
|
|
|
|
void (*segv)(int);
|
|
|
|
} jbuf_type;
|
|
|
|
|
|
|
|
static jbuf_type *
|
2007-02-04 08:43:16 +00:00
|
|
|
jbuf()
|
2000-08-23 18:54:22 +00:00
|
|
|
{
|
2007-02-04 08:43:16 +00:00
|
|
|
NSMutableData *d;
|
2011-02-23 11:52:17 +00:00
|
|
|
NSMutableDictionary *dict;
|
2007-02-04 08:43:16 +00:00
|
|
|
|
2011-02-23 11:52:17 +00:00
|
|
|
dict = [[NSThread currentThread] threadDictionary];
|
|
|
|
d = [dict objectForKey: @"GSjbuf"];
|
2007-02-04 08:43:16 +00:00
|
|
|
if (d == nil)
|
|
|
|
{
|
2008-03-16 18:17:49 +00:00
|
|
|
d = [[NSMutableData alloc] initWithLength: sizeof(jbuf_type)];
|
2011-02-23 11:52:17 +00:00
|
|
|
[dict setObject: d forKey: @"GSjbuf"];
|
2007-02-04 08:43:16 +00:00
|
|
|
RELEASE(d);
|
|
|
|
}
|
2008-03-16 18:17:49 +00:00
|
|
|
return (jbuf_type*)[d mutableBytes];
|
2000-08-23 18:54:22 +00:00
|
|
|
}
|
|
|
|
|
2007-02-04 08:43:16 +00:00
|
|
|
static void
|
|
|
|
recover(int sig)
|
2000-08-23 18:54:22 +00:00
|
|
|
{
|
2009-03-21 15:31:52 +00:00
|
|
|
siglongjmp(jbuf()->buf, 1);
|
2007-02-04 08:43:16 +00:00
|
|
|
}
|
2000-08-23 18:54:22 +00:00
|
|
|
|
2007-02-04 08:43:16 +00:00
|
|
|
void *
|
2009-02-23 20:42:32 +00:00
|
|
|
NSFrameAddress(NSUInteger offset)
|
2007-02-04 08:43:16 +00:00
|
|
|
{
|
2008-03-16 18:17:49 +00:00
|
|
|
jbuf_type *env;
|
2007-02-04 08:43:16 +00:00
|
|
|
|
|
|
|
env = jbuf();
|
2009-03-21 15:31:52 +00:00
|
|
|
if (sigsetjmp(env->buf, 1) == 0)
|
2007-02-04 08:43:16 +00:00
|
|
|
{
|
2008-03-16 18:17:49 +00:00
|
|
|
env->segv = signal(SIGSEGV, recover);
|
|
|
|
env->bus = signal(SIGBUS, recover);
|
2007-02-04 08:43:16 +00:00
|
|
|
switch (offset)
|
|
|
|
{
|
|
|
|
_NS_FRAME_HACK(0); _NS_FRAME_HACK(1); _NS_FRAME_HACK(2);
|
|
|
|
_NS_FRAME_HACK(3); _NS_FRAME_HACK(4); _NS_FRAME_HACK(5);
|
|
|
|
_NS_FRAME_HACK(6); _NS_FRAME_HACK(7); _NS_FRAME_HACK(8);
|
|
|
|
_NS_FRAME_HACK(9); _NS_FRAME_HACK(10); _NS_FRAME_HACK(11);
|
|
|
|
_NS_FRAME_HACK(12); _NS_FRAME_HACK(13); _NS_FRAME_HACK(14);
|
|
|
|
_NS_FRAME_HACK(15); _NS_FRAME_HACK(16); _NS_FRAME_HACK(17);
|
|
|
|
_NS_FRAME_HACK(18); _NS_FRAME_HACK(19); _NS_FRAME_HACK(20);
|
|
|
|
_NS_FRAME_HACK(21); _NS_FRAME_HACK(22); _NS_FRAME_HACK(23);
|
|
|
|
_NS_FRAME_HACK(24); _NS_FRAME_HACK(25); _NS_FRAME_HACK(26);
|
|
|
|
_NS_FRAME_HACK(27); _NS_FRAME_HACK(28); _NS_FRAME_HACK(29);
|
|
|
|
_NS_FRAME_HACK(30); _NS_FRAME_HACK(31); _NS_FRAME_HACK(32);
|
|
|
|
_NS_FRAME_HACK(33); _NS_FRAME_HACK(34); _NS_FRAME_HACK(35);
|
|
|
|
_NS_FRAME_HACK(36); _NS_FRAME_HACK(37); _NS_FRAME_HACK(38);
|
|
|
|
_NS_FRAME_HACK(39); _NS_FRAME_HACK(40); _NS_FRAME_HACK(41);
|
|
|
|
_NS_FRAME_HACK(42); _NS_FRAME_HACK(43); _NS_FRAME_HACK(44);
|
|
|
|
_NS_FRAME_HACK(45); _NS_FRAME_HACK(46); _NS_FRAME_HACK(47);
|
|
|
|
_NS_FRAME_HACK(48); _NS_FRAME_HACK(49); _NS_FRAME_HACK(50);
|
|
|
|
_NS_FRAME_HACK(51); _NS_FRAME_HACK(52); _NS_FRAME_HACK(53);
|
|
|
|
_NS_FRAME_HACK(54); _NS_FRAME_HACK(55); _NS_FRAME_HACK(56);
|
|
|
|
_NS_FRAME_HACK(57); _NS_FRAME_HACK(58); _NS_FRAME_HACK(59);
|
|
|
|
_NS_FRAME_HACK(60); _NS_FRAME_HACK(61); _NS_FRAME_HACK(62);
|
|
|
|
_NS_FRAME_HACK(63); _NS_FRAME_HACK(64); _NS_FRAME_HACK(65);
|
|
|
|
_NS_FRAME_HACK(66); _NS_FRAME_HACK(67); _NS_FRAME_HACK(68);
|
|
|
|
_NS_FRAME_HACK(69); _NS_FRAME_HACK(70); _NS_FRAME_HACK(71);
|
|
|
|
_NS_FRAME_HACK(72); _NS_FRAME_HACK(73); _NS_FRAME_HACK(74);
|
|
|
|
_NS_FRAME_HACK(75); _NS_FRAME_HACK(76); _NS_FRAME_HACK(77);
|
|
|
|
_NS_FRAME_HACK(78); _NS_FRAME_HACK(79); _NS_FRAME_HACK(80);
|
|
|
|
_NS_FRAME_HACK(81); _NS_FRAME_HACK(82); _NS_FRAME_HACK(83);
|
|
|
|
_NS_FRAME_HACK(84); _NS_FRAME_HACK(85); _NS_FRAME_HACK(86);
|
|
|
|
_NS_FRAME_HACK(87); _NS_FRAME_HACK(88); _NS_FRAME_HACK(89);
|
|
|
|
_NS_FRAME_HACK(90); _NS_FRAME_HACK(91); _NS_FRAME_HACK(92);
|
|
|
|
_NS_FRAME_HACK(93); _NS_FRAME_HACK(94); _NS_FRAME_HACK(95);
|
|
|
|
_NS_FRAME_HACK(96); _NS_FRAME_HACK(97); _NS_FRAME_HACK(98);
|
|
|
|
_NS_FRAME_HACK(99);
|
2008-03-16 18:17:49 +00:00
|
|
|
default: env->addr = NULL; break;
|
2007-02-04 08:43:16 +00:00
|
|
|
}
|
2008-03-16 18:17:49 +00:00
|
|
|
signal(SIGSEGV, env->segv);
|
|
|
|
signal(SIGBUS, env->bus);
|
2007-02-04 08:43:16 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-10-29 06:47:46 +00:00
|
|
|
env = jbuf();
|
2008-03-16 18:17:49 +00:00
|
|
|
signal(SIGSEGV, env->segv);
|
|
|
|
signal(SIGBUS, env->bus);
|
|
|
|
env->addr = NULL;
|
2007-02-04 08:43:16 +00:00
|
|
|
}
|
2008-03-16 18:17:49 +00:00
|
|
|
return env->addr;
|
2000-08-23 18:54:22 +00:00
|
|
|
}
|
|
|
|
|
2009-02-23 20:42:32 +00:00
|
|
|
NSUInteger NSCountFrames(void)
|
2007-12-03 14:13:57 +00:00
|
|
|
{
|
2008-03-16 18:17:49 +00:00
|
|
|
jbuf_type *env;
|
2007-12-03 14:13:57 +00:00
|
|
|
|
|
|
|
env = jbuf();
|
2009-03-21 15:31:52 +00:00
|
|
|
if (sigsetjmp(env->buf, 1) == 0)
|
2007-12-03 14:13:57 +00:00
|
|
|
{
|
2008-03-16 18:17:49 +00:00
|
|
|
env->segv = signal(SIGSEGV, recover);
|
|
|
|
env->bus = signal(SIGBUS, recover);
|
|
|
|
env->addr = 0;
|
2007-12-03 14:13:57 +00:00
|
|
|
|
|
|
|
#define _NS_COUNT_HACK(X) if (__builtin_frame_address(X + 1) == 0) \
|
2008-03-16 18:17:49 +00:00
|
|
|
goto done; else env->addr = (void*)(X + 1);
|
2007-12-03 14:13:57 +00:00
|
|
|
|
|
|
|
_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:
|
2008-03-16 18:17:49 +00:00
|
|
|
signal(SIGSEGV, env->segv);
|
|
|
|
signal(SIGBUS, env->bus);
|
2007-12-03 14:13:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
env = jbuf();
|
2008-03-16 18:17:49 +00:00
|
|
|
signal(SIGSEGV, env->segv);
|
|
|
|
signal(SIGBUS, env->bus);
|
2007-12-03 14:13:57 +00:00
|
|
|
}
|
|
|
|
|
2009-02-23 20:42:32 +00:00
|
|
|
return (uintptr_t)env->addr;
|
2007-12-03 14:13:57 +00:00
|
|
|
}
|
|
|
|
|
2007-02-04 08:43:16 +00:00
|
|
|
void *
|
2009-02-23 20:42:32 +00:00
|
|
|
NSReturnAddress(NSUInteger offset)
|
2000-08-23 18:54:22 +00:00
|
|
|
{
|
2008-03-16 18:17:49 +00:00
|
|
|
jbuf_type *env;
|
2007-02-04 08:43:16 +00:00
|
|
|
|
|
|
|
env = jbuf();
|
2009-03-21 15:31:52 +00:00
|
|
|
if (sigsetjmp(env->buf, 1) == 0)
|
2007-02-04 08:43:16 +00:00
|
|
|
{
|
2008-03-16 18:17:49 +00:00
|
|
|
env->segv = signal(SIGSEGV, recover);
|
|
|
|
env->bus = signal(SIGBUS, recover);
|
2007-02-04 08:43:16 +00:00
|
|
|
switch (offset)
|
|
|
|
{
|
|
|
|
_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);
|
2008-03-16 18:17:49 +00:00
|
|
|
default: env->addr = NULL; break;
|
2007-02-04 08:43:16 +00:00
|
|
|
}
|
2008-03-16 18:17:49 +00:00
|
|
|
signal(SIGSEGV, env->segv);
|
|
|
|
signal(SIGBUS, env->bus);
|
2007-02-04 08:43:16 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-03-16 18:17:49 +00:00
|
|
|
env = jbuf();
|
|
|
|
signal(SIGSEGV, env->segv);
|
|
|
|
signal(SIGBUS, env->bus);
|
|
|
|
env->addr = NULL;
|
2007-02-04 08:43:16 +00:00
|
|
|
}
|
|
|
|
|
2008-03-16 18:17:49 +00:00
|
|
|
return env->addr;
|
2000-08-23 18:54:22 +00:00
|
|
|
}
|
2001-05-26 16:02:56 +00:00
|
|
|
|
2007-12-03 14:13:57 +00:00
|
|
|
NSMutableArray *
|
|
|
|
GSPrivateStackAddresses(void)
|
|
|
|
{
|
2009-02-20 17:44:58 +00:00
|
|
|
NSMutableArray *stack;
|
2011-02-28 19:49:57 +00:00
|
|
|
NSAutoreleasePool *pool;
|
2009-02-20 17:44:58 +00:00
|
|
|
|
|
|
|
#if HAVE_BACKTRACE
|
|
|
|
void *addresses[1024];
|
|
|
|
int n = backtrace(addresses, 1024);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
stack = [NSMutableArray arrayWithCapacity: n];
|
|
|
|
pool = [NSAutoreleasePool new];
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
[stack addObject: [NSValue valueWithPointer: addresses[i]]];
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
2007-12-03 14:13:57 +00:00
|
|
|
unsigned n = NSCountFrames();
|
|
|
|
unsigned i;
|
2008-03-16 18:17:49 +00:00
|
|
|
jbuf_type *env;
|
2007-12-03 14:13:57 +00:00
|
|
|
|
2009-02-20 17:44:58 +00:00
|
|
|
stack = [NSMutableArray arrayWithCapacity: n];
|
|
|
|
pool = [NSAutoreleasePool new];
|
2007-12-06 11:40:17 +00:00
|
|
|
/* There should be more frame addresses than return addresses.
|
|
|
|
*/
|
|
|
|
if (n > 0)
|
|
|
|
{
|
|
|
|
n--;
|
|
|
|
}
|
|
|
|
if (n > 0)
|
|
|
|
{
|
|
|
|
n--;
|
|
|
|
}
|
|
|
|
|
2007-12-03 14:13:57 +00:00
|
|
|
env = jbuf();
|
2009-03-21 15:31:52 +00:00
|
|
|
if (sigsetjmp(env->buf, 1) == 0)
|
2007-12-03 14:13:57 +00:00
|
|
|
{
|
2008-03-16 18:17:49 +00:00
|
|
|
env->segv = signal(SIGSEGV, recover);
|
|
|
|
env->bus = signal(SIGBUS, recover);
|
2007-12-03 14:13:57 +00:00
|
|
|
|
|
|
|
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);
|
2008-03-16 18:17:49 +00:00
|
|
|
default: env->addr = 0; break;
|
2007-12-03 14:13:57 +00:00
|
|
|
}
|
2008-03-16 18:17:49 +00:00
|
|
|
if (env->addr == 0)
|
2007-12-03 14:13:57 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2008-03-16 18:17:49 +00:00
|
|
|
[stack addObject: [NSValue valueWithPointer: env->addr]];
|
2007-12-03 14:13:57 +00:00
|
|
|
}
|
2008-03-16 18:17:49 +00:00
|
|
|
signal(SIGSEGV, env->segv);
|
|
|
|
signal(SIGBUS, env->bus);
|
2007-12-03 14:13:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-10-29 06:47:46 +00:00
|
|
|
env = jbuf();
|
2008-03-16 18:17:49 +00:00
|
|
|
signal(SIGSEGV, env->segv);
|
|
|
|
signal(SIGBUS, env->bus);
|
2007-12-03 14:13:57 +00:00
|
|
|
}
|
2009-02-20 17:44:58 +00:00
|
|
|
#endif
|
2011-05-27 11:48:44 +00:00
|
|
|
[pool drain];
|
2007-12-03 14:13:57 +00:00
|
|
|
return stack;
|
|
|
|
}
|
|
|
|
|
2007-02-04 08:43:16 +00:00
|
|
|
|
2001-05-26 16:02:56 +00:00
|
|
|
const char *_NSPrintForDebugger(id object)
|
|
|
|
{
|
|
|
|
if (object && [object respondsToSelector: @selector(description)])
|
|
|
|
return [[object description] cString];
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-07-25 16:00:52 +00:00
|
|
|
|
|
|
|
NSString *_NSNewStringFromCString(const char *cstring)
|
|
|
|
{
|
2006-10-09 14:00:01 +00:00
|
|
|
return [NSString stringWithCString: cstring
|
|
|
|
encoding: [NSString defaultCStringEncoding]];
|
2005-07-25 16:00:52 +00:00
|
|
|
}
|
|
|
|
|