Implemented tracking and reporting all allocated instances of a certain

class


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@9627 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
nico 2001-04-19 16:10:23 +00:00
parent 5d228bcd91
commit c1bafe11b6
3 changed files with 258 additions and 16 deletions

View file

@ -3,6 +3,8 @@
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: August 1997
Extended by: Nicola Pero <n.pero@mi.flashnet.it>
Date: December 2000, April 2001
This file is part of the GNUstep Base Library.
@ -68,11 +70,29 @@ extern int errno;
* Returns a newline separated list of the classes which
* have had instances allocated at any point, and the total
* count of the number of instances allocated.
*/
*
* When the previous functions have allowed you to find a memory leak,
* and you know that you are leaking objects of class XXX, but you are
* hopeless about actually finding out where the leak is, the
* following functions could come handy as they allow you to find
* exactly *what* objects you are leaking (warning! these functions
* could slow down your system appreciably - use them only temporarily
* and only in debugging systems):
*
* GSDebugAllocationStartRecordingObjects()
* Starts recording all allocated objects of a certain class
*
* GSDebugAllocationListRecordedObjects()
* Returns an array containing all the allocated objects
* of a certain class which have been recorded.
* Presumably, you will immediately call -description on
* them to find out the objects you are leaking.
* Warning - the objects are put in an array, so until
* the array is autoreleased, the objects are not released. */
#ifndef NDEBUG
GS_EXPORT void GSDebugAllocationAdd(Class c);
GS_EXPORT void GSDebugAllocationRemove(Class c);
GS_EXPORT void GSDebugAllocationAdd(Class c, id o);
GS_EXPORT void GSDebugAllocationRemove(Class c, id o);
GS_EXPORT BOOL GSDebugAllocationActive(BOOL active);
GS_EXPORT int GSDebugAllocationCount(Class c);
@ -82,6 +102,9 @@ GS_EXPORT Class* GSDebugAllocationClassList();
GS_EXPORT const char* GSDebugAllocationList(BOOL changeFlag);
GS_EXPORT const char* GSDebugAllocationListAll();
GS_EXPORT void GSDebugAllocationActiveRecordingObjects(Class c);
GS_EXPORT NSArray *GSDebugAllocationListRecordedObjects(Class c);
GS_EXPORT NSString* GSDebugFunctionMsg(const char *func, const char *file,
int line, NSString *fmt);
GS_EXPORT NSString* GSDebugMethodMsg(id obj, SEL sel, const char *file,

View file

@ -1,8 +1,10 @@
/* Debugging utilities for GNUStep and OpenStep
Copyright (C) 1997,1999,2000 Free Software Foundation, Inc.
Copyright (C) 1997,1999,2000,2001 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: August 1997
Extended by: Nicola Pero <n.pero@mi.flashnet.it>
Date: December 2000, April 2001
This file is part of the GNUstep Base Library.
@ -45,10 +47,16 @@ strerror(int eno)
typedef struct {
Class class;
/* The following are used for statistical info */
int count;
int lastc;
int total;
int peak;
/* The following are used to record actual objects */
BOOL is_recording;
id *recorded_objects;
int num_recorded_objects;
int stack_size;
} table_entry;
static int num_classes = 0;
@ -104,7 +112,63 @@ GSDebugAllocationActive(BOOL active)
}
void
GSDebugAllocationAdd(Class c)
GSDebugAllocationActiveRecordingObjects(Class c)
{
unsigned i;
GSDebugAllocationActive(YES);
for (i = 0; i < num_classes; i++)
{
if (the_table[i].class == c)
{
if (uniqueLock != nil)
[uniqueLock lock];
the_table[i].is_recording = YES;
if (uniqueLock != nil)
[uniqueLock unlock];
return;
}
}
if (uniqueLock != nil)
[uniqueLock lock];
if (num_classes >= table_size)
{
int more = table_size + 128;
table_entry *tmp;
tmp = NSZoneMalloc(NSDefaultMallocZone(), more * sizeof(table_entry));
if (tmp == 0)
{
if (uniqueLock != nil)
[uniqueLock unlock];
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;
the_table[num_classes].num_recorded_objects = 0;
the_table[num_classes].stack_size = 0;
num_classes++;
if (uniqueLock != nil)
[uniqueLock unlock];
}
void
GSDebugAllocationAdd(Class c, id o)
{
if (debug_allocation)
{
@ -122,6 +186,39 @@ GSDebugAllocationAdd(Class c)
{
the_table[i].peak = the_table[i].count;
}
if (the_table[i].is_recording == YES)
{
if (the_table[i].num_recorded_objects >=
the_table[i].stack_size)
{
int more = the_table[i].stack_size + 128;
id *tmp;
tmp = NSZoneMalloc(NSDefaultMallocZone(),
more * sizeof(id));
if (tmp == 0)
{
if (uniqueLock != nil)
[uniqueLock unlock];
return;
}
if (the_table[i].recorded_objects != NULL)
{
memcpy(tmp, the_table[i].recorded_objects,
the_table[i].num_recorded_objects
* sizeof(id));
NSZoneFree(NSDefaultMallocZone(),
the_table[i].recorded_objects);
}
the_table[i].recorded_objects = tmp;
the_table[i].stack_size = more;
}
(the_table[i].recorded_objects)
[the_table[i].num_recorded_objects] = o;
the_table[i].num_recorded_objects++;
}
if (uniqueLock != nil)
[uniqueLock unlock];
return;
@ -133,9 +230,9 @@ GSDebugAllocationAdd(Class c)
{
int more = table_size + 128;
table_entry *tmp;
tmp = NSZoneMalloc(NSDefaultMallocZone(), more * sizeof(table_entry));
if (tmp == 0)
{
if (uniqueLock != nil)
@ -155,6 +252,10 @@ GSDebugAllocationAdd(Class c)
the_table[num_classes].lastc = 0;
the_table[num_classes].total = 1;
the_table[num_classes].peak = 1;
the_table[num_classes].is_recording = NO;
the_table[num_classes].recorded_objects = NULL;
the_table[num_classes].num_recorded_objects = 0;
the_table[num_classes].stack_size = 0;
num_classes++;
if (uniqueLock != nil)
[uniqueLock unlock];
@ -396,7 +497,7 @@ _GSDebugAllocationListAll()
}
void
GSDebugAllocationRemove(Class c)
GSDebugAllocationRemove(Class c, id o)
{
if (debug_allocation)
{
@ -409,6 +510,36 @@ GSDebugAllocationRemove(Class c)
if (uniqueLock != nil)
[uniqueLock lock];
the_table[i].count--;
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)
{
break;
}
}
if (j < the_table[i].num_recorded_objects)
{
for (k = j;
k + 1 < the_table[i].num_recorded_objects;
k++)
{
(the_table[i].recorded_objects)[k] =
(the_table[i].recorded_objects)[k + 1];
}
the_table[i].num_recorded_objects--;
}
else
{
/* Not found - no problem - this happens if the
object was allocated before we started
recording */
;
}
}
if (uniqueLock != nil)
[uniqueLock unlock];
return;
@ -417,6 +548,94 @@ GSDebugAllocationRemove(Class c)
}
}
NSArray *
GSDebugAllocationListRecordedObjects(Class c)
{
NSArray *answer;
unsigned i, k;
id *tmp;
if (debug_allocation == NO)
{
return nil;
}
if (uniqueLock != nil)
[uniqueLock lock];
for (i = 0; i < num_classes; i++)
{
if (the_table[i].class == c)
{
break;
}
}
if (i == num_classes)
{
if (uniqueLock != nil)
[uniqueLock unlock];
return nil;
}
if (the_table[i].is_recording == NO)
{
if (uniqueLock != nil)
[uniqueLock unlock];
return nil;
}
if (the_table[i].num_recorded_objects == 0)
{
if (uniqueLock != nil)
[uniqueLock unlock];
return [NSArray array];
}
tmp = NSZoneMalloc(NSDefaultMallocZone(),
the_table[i].num_recorded_objects * sizeof(id));
if (tmp == 0)
{
if (uniqueLock != nil)
[uniqueLock unlock];
return nil;
}
/* First, we copy the objects into a temporary buffer */
memcpy(tmp, the_table[i].recorded_objects,
the_table[i].num_recorded_objects * sizeof(id));
/* Then, we bravely unlock the lock */
if (uniqueLock != nil)
[uniqueLock unlock];
/* Retain all the objects - NB: if retaining one of the objects as a
side effect releases another one of them , we are broken ... */
for (k = 0; k < the_table[i].num_recorded_objects; k++)
{
RETAIN (tmp[k]);
}
/* Only then we create an array with them - this is now safe as we
have copied the objects out, unlocked, and retained them. */
answer = [NSArray arrayWithObjects: tmp
count: the_table[i].num_recorded_objects];
/* 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);
return answer;
}
NSString*
GSDebugFunctionMsg(const char *func, const char *file, int line, NSString *fmt)
{

View file

@ -337,7 +337,7 @@ GSFinalize(void* object, void* data)
{
[(id)object gcFinalize];
#ifndef NDEBUG
GSDebugAllocationRemove(((id)object)->class_pointer);
GSDebugAllocationRemove(((id)object)->class_pointer, (id)object);
#endif
((id)object)->class_pointer = (void*)0xdeadface;
}
@ -385,7 +385,7 @@ NSAllocateObject(Class aClass, unsigned extraBytes, NSZone *zone)
* finalised - for other objects we have no way of decrementing
* the count when the object is collected.
*/
GSDebugAllocationAdd(aClass);
GSDebugAllocationAdd(aClass, new);
#endif
GC_REGISTER_FINALIZER (new, GSFinalize, NULL, NULL, NULL);
}
@ -428,7 +428,7 @@ inline NSObject *
NSAllocateObject (Class aClass, unsigned extraBytes, NSZone *zone)
{
#ifndef NDEBUG
extern void GSDebugAllocationAdd(Class);
extern void GSDebugAllocationAdd(Class c, id o);
#endif
id new = nil;
int size = aClass->instance_size + extraBytes + sizeof(struct obj_layout);
@ -447,7 +447,7 @@ NSAllocateObject (Class aClass, unsigned extraBytes, NSZone *zone)
new = (id)&((obj)new)[1];
new->class_pointer = aClass;
#ifndef NDEBUG
GSDebugAllocationAdd(aClass);
GSDebugAllocationAdd(aClass, new);
#endif
}
return new;
@ -457,7 +457,7 @@ inline void
NSDeallocateObject(NSObject *anObject)
{
#ifndef NDEBUG
extern void GSDebugAllocationRemove(Class);
extern void GSDebugAllocationRemove(Class c, id o);
#endif
if ((anObject!=nil) && CLS_ISCLASS(((id)anObject)->class_pointer))
{
@ -465,7 +465,7 @@ NSDeallocateObject(NSObject *anObject)
NSZone *z = GSObjCZone(anObject);
#ifndef NDEBUG
GSDebugAllocationRemove(((id)anObject)->class_pointer);
GSDebugAllocationRemove(((id)anObject)->class_pointer, (id)anObject);
#endif
((id)anObject)->class_pointer = (void*) 0xdeadface;
NSZoneFree(z, o);
@ -495,7 +495,7 @@ NSAllocateObject (Class aClass, unsigned extraBytes, NSZone *zone)
memset (new, 0, size);
new->class_pointer = aClass;
#ifndef NDEBUG
GSDebugAllocationAdd(aClass);
GSDebugAllocationAdd(aClass, new);
#endif
}
return new;
@ -509,7 +509,7 @@ NSDeallocateObject(NSObject *anObject)
NSZone *z = [anObject zone];
#ifndef NDEBUG
GSDebugAllocationRemove(((id)anObject)->class_pointer);
GSDebugAllocationRemove(((id)anObject)->class_pointer, (id)anObject);
#endif
((id)anObject)->class_pointer = (void*) 0xdeadface;
NSZoneFree(z, anObject);