Improved memory usage reporting mechanisms

This commit is contained in:
Richard Frith-Macdonald 2019-08-08 17:20:25 +01:00
parent 2ca4f74af5
commit dd36855923
21 changed files with 404 additions and 230 deletions

View file

@ -1,3 +1,34 @@
2019-08-08 Richard Frith-Macdonald <rfm@gnu.org>
* configure.ac: check for malloc_usable_sizer()
* configure: regenerate
* Headers/GNUstepBase/config.h.in: regenerate
* Headers/Foundation/NSDebug.h: Add GSMemoryBytes()
* Headers/GNUstepBase/NSObject+GNUstepBase.h: Rework memory usage
reporting extensions API.
* Source/Additions/NSMutableString+GNUstepBase.m:
* Source/Additions/NSObject+GNUstepBase.m:
* Source/GSArray.m:
* Source/GSCountedSet.m:
* Source/GSDictionary.m:
* Source/GSSet.m:
* Source/GSString.m:
* Source/NSConcreteHashTable.m:
* Source/NSConcreteMapTable.m:
* Source/NSData.m:
* Source/NSDebug.m:
* Source/NSDistantObject.m:
* Source/NSObject.m:
* Source/NSProxy.m:
* Source/NSRunLoop.m:
Updates for new memory reporting extensions API. The parts of this
dealing with the size of the contents of an object are now off by
default (trying to calculate the size of an object is problematic if
it is mutated while you are doing it) but classes can implement
-sizeOfContentExcluding: to turn it on if they are thread-safe.
Implementations provided for immutable container objects and for all
strings.
2019-08-06 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSData.m: Implement

View file

@ -53,6 +53,7 @@ extern "C" {
*
* Public functions:
* GSDebugAllocationActive()
* GSDebugAllocationBytes()
* GSDebugAllocationCount()
* GSDebugAllocationTotal()
* GSDebugAllocationPeak()
@ -100,6 +101,16 @@ GS_EXPORT void GSDebugAllocationRemove(Class c, id o);
*/
GS_EXPORT BOOL GSDebugAllocationActive(BOOL active);
/**
* This function activates or deactivates byte counting for allocation.<br />
* Returns the previous state.<br />
* You may call this function to activate additional checks to see how
* much memory is allocated to hold each object allocated. When this is
* enabled, listing the allocated objects will also list the number of bytes
* of heap memory allocated to hold the objects.<br />
*/
GS_EXPORT BOOL GSDebugAllocationBytes(BOOL active);
/**
* <p>
* Returns the number

View file

@ -118,20 +118,15 @@ extern "C" {
* determine how much heap memory an object (and its content) occupies.
*/
@interface NSObject(MemoryFootprint)
/** This method returns the size of the memory used by the instance variables
* of obj which were declared in the supplied class (excluding those declared
* by its superclasses or subclasses).<br />
* This is not the memory occupied by obj itself. Rather, it should be the
* memory referenced by any pointers (including objects) in obj.<br />
* Subclasses which do not implement this method will have the memory of their
* object ivars included in the total but not memory pointed to by non-object
* pointers (generic code cannot readily determine the size of blocks of
* memory pointed to by a pointer).<br />
* When an implementation (other than the NSObject implementation) of this
* method is called, cls should be the class in which the implementation
* was defined. However, as a convenience, the implementation may call the
* base implementation to get the size of object ivars, and then add in the
* size of other memory referenced by pointers the instance is using:
/** This method returns the size of the memory used by the object instance
* variables of the target object (excluding any in the specified set).<br />
* This is not the memory occupied by instance variable pointers.
* It is the memory referenced by any objects inside the target.<br />
* This method is not intended to be overridden, rather it is provided for
* use as a helper for the -sizeOfContentExcluding: method.<br />
* This method must not be called for a mutable object unless it is protected
* by a locking mechanism to prevent mutation while it is examining the
* instance variables of the object.
* <example>
* @interface foo : bar
* {
@ -142,25 +137,21 @@ extern "C" {
* }
* @end
* @implementation foo
* + (NSUInteger) contentSizeOf: (NSObject*)obj
* declaredIn: (Class)cls
* excluding: (NSHashTable*)exclude
* - (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
*{
* NSUInteger size;
*
* // get the size of the objects (a and b)
* size = [NSObject contentSizeOf: obj
* declaredIn: self
* size = [NSObject contentSizeOf: self
* excluding: exclude];
* // add the memory pointed to by p
* size += obj->capacity * sizeof(char);
* size += capacity * sizeof(char);
* return size;
*}
*@end
* </example>
*/
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude;
/** This method returns the memory usage of the receiver, excluding any
@ -171,12 +162,14 @@ extern "C" {
* The NSObject implementation returns zero if the receiver is in the
* table, but otherwise adds itself to the table and returns its memory
* footprint (the sum of all of its instance variables, plus the result
* of calling +contentSizeOf:declaredIn:excluding: for the class of the
* instance and all its superclasses).<br />
* of calling -sizeOfContentExcluding: for the instance).<br />
* Classes should not override this method, instead they should implement
* +contentSizeOf:declaredIn:excluding: to return the extra memory usage
* -sizeOfContentExcluding: to return the extra memory usage
* of the pointer/object instance variables (heap memory) they add to
* their superclass.
* their superclass.<br />
* NB. mutable objects must either prevent mutation while calculating
* their content size, or must override -sizeOfContentExcluding: to refrain
* from dealing with content which might change.
*/
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude;
@ -185,6 +178,22 @@ extern "C" {
*/
- (NSUInteger) sizeInBytes;
/** This method is called by -sizeInBytesExcluding: to calculate the size of
* any objects or heap memory contained by the receiver.<br />
* The base class implementation simply returns zero (as it is not possible
* to safely calculate content sizes of mutable objects), but subclasses should
* override it to provide correct information where possible (eg if the object
* is immutable or if locking is used to prevent mutation while calculating
* content size).<br />
* Subclasses may use the +contentSizeOf:excluding: method as a convenience
* to provide the sizes of object instnce variables.
*/
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude;
/** Helper method called by -sizeInBytesExcluding: to return the size of
* the instance excluding any contents (things referenced by pointers).
*/
- (NSUInteger) sizeOfInstance;
@end
/** This is an informal protocol ... classes may implement the method and

View file

@ -396,6 +396,9 @@
/* Define to 1 if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define to 1 if you have the `malloc_usable_size' function. */
#undef HAVE_MALLOC_USABLE_SIZE
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H

View file

@ -31,7 +31,7 @@
/* Test for ASCII whitespace which is safe for unicode characters */
#define space(C) ((C) > 127 ? NO : isspace(C))
/* This private cass is used for the -immutableProxy method in the category
/* This private class is used for the -immutableProxy method in the category
* on NSMutableString.
* It is needed for [NSAttributedString-string] and [NSTextStorage-string]
*/

View file

@ -31,6 +31,8 @@
#import "GNUstepBase/NSDebug+GNUstepBase.h"
#import "GNUstepBase/NSThread+GNUstepBase.h"
#include <malloc.h>
/* This file contains methods which nominally return an id but in fact
* always rainse an exception and never return.
* We need to suppress the compiler warning about that.
@ -325,31 +327,36 @@ handleExit()
@implementation NSObject (MemoryFootprint)
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
{
unsigned count;
Ivar *vars;
Class cls = object_getClass(obj);
NSUInteger size = 0;
if (0 != (vars = class_copyIvarList(cls, &count)))
while (cls != Nil)
{
while (count-- > 0)
{
const char *type = ivar_getTypeEncoding(vars[count]);
unsigned count;
Ivar *vars;
type = GSSkipTypeQualifierAndLayoutInfo(type);
if ('@' == *type)
if (0 != (vars = class_copyIvarList(cls, &count)))
{
while (count-- > 0)
{
NSObject *content = object_getIvar(obj, vars[count]);
if (content != nil)
const char *type = ivar_getTypeEncoding(vars[count]);
type = GSSkipTypeQualifierAndLayoutInfo(type);
if ('@' == *type)
{
size += [content sizeInBytesExcluding: exclude];
NSObject *content = object_getIvar(obj, vars[count]);
if (content != nil)
{
size += [content sizeInBytesExcluding: exclude];
}
}
}
free(vars);
}
free(vars);
cls = class_getSuperclass(cls);
}
return size;
}
@ -361,6 +368,14 @@ handleExit()
{
return 0;
}
+ (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0;
}
+ (NSUInteger) sizeOfInstance
{
return 0;
}
- (NSUInteger) sizeInBytes
{
NSUInteger bytes;
@ -375,24 +390,41 @@ handleExit()
{
if (0 == NSHashGet(exclude, self))
{
Class c = object_getClass(self);
NSUInteger size = class_getInstanceSize(c);
NSUInteger size = [self sizeOfInstance];
NSHashInsert(exclude, self);
if (size > 0)
{
while (c != Nil)
{
size += [c contentSizeOf: self
declaredIn: c
excluding: exclude];
c = class_getSuperclass(c);
}
size += [self sizeOfContentExcluding: exclude];
}
return size;
}
return 0;
}
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0;
}
- (NSUInteger) sizeOfInstance
{
NSUInteger size;
#if GS_SIZEOF_VOIDP > 4
if ((((NSUInteger)void*)self) & 0x07)
{
return 0; // Small object has no size
}
#endif
#if HAVE_MALLOC_USABLE_SIZE
size = malloc_usable_size((void*)self - sizeof(intptr_t));
#else
size = class_getInstanceSize(object_getClass(self));
#endif
return size;
}
@end
/* Dummy implementation

View file

@ -73,19 +73,16 @@ static Class GSInlineArrayClass;
@implementation GSArray
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
GSArray *a = (GSArray*)obj;
NSUInteger size = a->_count * sizeof(id);
NSUInteger index = a->_count;
NSUInteger size = _count * sizeof(id);
NSUInteger index = _count;
while (index-- > 0)
{
size += [a->_contents_array[index] sizeInBytesExcluding: exclude];
size += [_contents_array[index] sizeInBytesExcluding: exclude];
}
return size;
return size + [super sizeOfContentExcluding: exclude];
}
- (void) _raiseRangeExceptionWithIndex: (NSUInteger)index from: (SEL)sel
@ -436,19 +433,11 @@ static Class GSInlineArrayClass;
@implementation GSMutableArray
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
GSMutableArray *a = (GSMutableArray*)obj;
NSUInteger size = a->_capacity * sizeof(id);
NSUInteger index = a->_count;
while (index-- > 0)
{
size += [a->_contents_array[index] sizeInBytesExcluding: exclude];
}
return size;
/* Can't safely calculate for mutable object; just buffer size
*/
return _capacity * sizeof(void*);
}
+ (void) initialize

View file

@ -90,22 +90,11 @@
@implementation GSCountedSet
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
GSIMapTable map = &((GSCountedSet*)obj)->map;
NSUInteger size = GSIMapSize(map) - sizeof(GSIMapTable);
GSIMapEnumerator_t enumerator = GSIMapEnumeratorForMap(map);
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
while (node != 0)
{
size += [node->key.obj sizeInBytesExcluding: exclude];
node = GSIMapEnumeratorNextNode(&enumerator);
}
GSIMapEndEnumerator(&enumerator);
return size;
/* Can't safely calculate for mutable object; just buffer size
*/
return map.nodeCount * sizeof(GSIMapNode);
}
+ (void) initialize

View file

@ -80,13 +80,10 @@
static SEL nxtSel;
static SEL objSel;
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
GSIMapTable map = &((GSDictionary*)obj)->map;
NSUInteger size = GSIMapSize(map) - sizeof(GSIMapTable);
GSIMapEnumerator_t enumerator = GSIMapEnumeratorForMap(map);
NSUInteger size = GSIMapSize(&map) - sizeof(GSIMapTable);
GSIMapEnumerator_t enumerator = GSIMapEnumeratorForMap(&map);
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
while (node != 0)
@ -96,7 +93,7 @@ static SEL objSel;
node = GSIMapEnumeratorNextNode(&enumerator);
}
GSIMapEndEnumerator(&enumerator);
return size;
return size + [super sizeOfContentExcluding: exclude];
}
+ (void) initialize
@ -379,23 +376,11 @@ static SEL objSel;
@implementation GSMutableDictionary
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
GSIMapTable map = &((GSDictionary*)obj)->map;
NSUInteger size = GSIMapSize(map) - sizeof(GSIMapTable);
GSIMapEnumerator_t enumerator = GSIMapEnumeratorForMap(map);
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
while (node != 0)
{
size += [node->key.obj sizeInBytesExcluding: exclude];
size += [node->value.obj sizeInBytesExcluding: exclude];
node = GSIMapEnumeratorNextNode(&enumerator);
}
GSIMapEndEnumerator(&enumerator);
return size;
/* Can't safely calculate for mutable object; just buffer size
*/
return map.nodeCount * sizeof(GSIMapNode);
}
+ (void) initialize

View file

@ -106,13 +106,10 @@ static Class arrayClass;
static Class setClass;
static Class mutableSetClass;
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
GSIMapTable map = &((GSSet*)obj)->map;
NSUInteger size = GSIMapSize(map) - sizeof(GSIMapTable);
GSIMapEnumerator_t enumerator = GSIMapEnumeratorForMap(map);
NSUInteger size = GSIMapSize(&map) - sizeof(GSIMapTable);
GSIMapEnumerator_t enumerator = GSIMapEnumeratorForMap(&map);
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
while (node != 0)
@ -551,6 +548,13 @@ static Class mutableSetClass;
@implementation GSMutableSet
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
/* Can't safely calculate for mutable object; just buffer size
*/
return map.nodeCount * sizeof(GSIMapNode);
}
+ (void) initialize
{
if (self == [GSMutableSet class])

View file

@ -1082,6 +1082,16 @@ tsbytes(uintptr_t s, char *buf)
return NSMakeRange(anIndex, 1);
}
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0; // Tiny string uses no heap
}
- (NSUInteger) sizeOfInstance
{
return 0; // Tiny string uses no heap
}
- (const char*) UTF8String
{
char *buf = GSAutoreleasedBuffer(9);
@ -4107,6 +4117,22 @@ agree, create a new GSCInlineString otherwise.
@implementation GSCInlineString
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0; // Inline string content uses no heap
}
- (NSUInteger) sizeOfInstance
{
NSUInteger size;
#if HAVE_MALLOC_USABLE_SIZE
size = malloc_usable_size((void*)self - sizeof(intptr_t));
#else
size = class_getInstanceSize(GSCInlineStringClass);
size += _count;
#endif
return size;
}
@end
@ -4518,6 +4544,22 @@ agree, create a new GSUInlineString otherwise.
@implementation GSUInlineString
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0; // Inline string content uses no heap
}
- (NSUInteger) sizeOfInstance
{
NSUInteger size;
#if HAVE_MALLOC_USABLE_SIZE
size = malloc_usable_size((void*)self - sizeof(intptr_t));
#else
size = class_getInstanceSize(GSUInlineStringClass);
size += _count * sizeof(unichar);
#endif
return size;
}
@end
@ -6320,6 +6362,16 @@ literalIsEqual(NXConstantString *self, id anObject)
#endif
}
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0; // Constant string uses no heap
}
- (NSUInteger) sizeOfInstance
{
return 0; // Constant string uses no heap
}
@end

View file

@ -814,26 +814,11 @@ const NSHashTableCallBacks NSPointerToStructHashCallBacks =
@implementation NSConcreteHashTable
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
GSIMapTable map = (GSIMapTable)obj;
NSUInteger size = GSIMapSize(map) - sizeof(GSIMapTable);
/* If we knew that this table held objects, we could return their size...
GSIMapEnumerator_t enumerator = GSIMapEnumeratorForMap(map);
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
while (node != 0)
{
size += [node->key.obj sizeInBytesExcluding: exclude];
node = GSIMapEnumeratorNextNode(&enumerator);
}
GSIMapEndEnumerator(&enumerator);
*/
return size;
/* Can't safely calculate for mutable object; just buffer size
*/
return nodeCount * sizeof(GSIMapNode);
}
+ (void) initialize

View file

@ -1170,26 +1170,11 @@ const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks =
@implementation NSConcreteMapTable
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
GSIMapTable map = (GSIMapTable)obj;
NSUInteger size = GSIMapSize(map) - sizeof(GSIMapTable);
/* If we knew that this table held objects, we could return their size...
GSIMapEnumerator_t enumerator = GSIMapEnumeratorForMap(map);
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
while (node != 0)
{
size += [node->key.obj sizeInBytesExcluding: exclude];
size += [node->value.obj sizeInBytesExcluding: exclude];
node = GSIMapEnumeratorNextNode(&enumerator);
}
GSIMapEndEnumerator(&enumerator);
*/
return size;
/* Can't safely calculate for mutable object; just buffer size
*/
return nodeCount * sizeof(GSIMapNode);
}
+ (void) initialize

View file

@ -509,11 +509,9 @@ failure:
*/
@implementation NSData
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return [(NSData*)obj length];
return [self length];
}
+ (void) initialize
@ -2142,11 +2140,9 @@ failure:
}
}
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return [(NSMutableData*)obj capacity];
return [self capacity];
}
+ (id) data

View file

@ -48,24 +48,31 @@
#include <execinfo.h>
#endif
#include <malloc.h>
typedef struct {
Class class;
Class class;
/* The following are used for statistical info */
unsigned int count;
unsigned int lastc;
unsigned int total;
unsigned int peak;
uint32_t count;
uint32_t lastc;
uint32_t totalc;
uint32_t peak;
uint64_t bytes;
uint64_t totalb;
uint64_t lastb;
uint32_t nominal_size;
/* The following are used to record actual objects */
BOOL is_recording;
id *recorded_objects;
id *recorded_tags;
unsigned int num_recorded_objects;
unsigned int stack_size;
BOOL is_recording;
id *recorded_objects;
id *recorded_tags;
uint32_t num_recorded_objects;
uint32_t stack_size;
} table_entry;
typedef struct {
const char *name;
int count;
long bytes;
} list_entry;
static NSInteger itemComp(id v0, id v1, void *ctxt)
@ -82,6 +89,7 @@ static unsigned int table_size = 0;
static table_entry* the_table = 0;
static BOOL debug_allocation = NO;
static BOOL debug_byte_size = NO;
static pthread_mutex_t uniqueLock;
@ -141,6 +149,15 @@ GSDebugAllocationActive(BOOL active)
return old;
}
BOOL
GSDebugAllocationBytes(BOOL active)
{
BOOL old = debug_byte_size;
debug_byte_size = active ? YES : NO;
return old;
}
BOOL
GSDebugAllocationRecordObjects(Class c, BOOL newState)
{
@ -205,8 +222,12 @@ GSDebugAllocationRecordObjects(Class c, BOOL newState)
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].totalc = 0;
the_table[num_classes].peak = 0;
the_table[num_classes].bytes = 0;
the_table[num_classes].lastb = 0;
the_table[num_classes].totalb = 0;
the_table[num_classes].nominal_size = class_getInstanceSize(c);
the_table[num_classes].is_recording = YES;
the_table[num_classes].recorded_objects = NULL;
the_table[num_classes].recorded_tags = NULL;
@ -236,6 +257,7 @@ _GSDebugAllocationAdd(Class c, id o)
if (debug_allocation == YES)
{
unsigned int i;
unsigned bytes;
for (i = 0; i < num_classes; i++)
{
@ -243,7 +265,17 @@ _GSDebugAllocationAdd(Class c, id o)
{
doLock();
the_table[i].count++;
the_table[i].total++;
the_table[i].totalc++;
if (YES == debug_byte_size)
{
bytes = [o sizeOfInstance];
}
else
{
bytes = the_table[i].nominal_size;
}
the_table[i].bytes += bytes;
the_table[i].totalb += bytes;
if (the_table[i].count > the_table[i].peak)
{
the_table[i].peak = the_table[i].count;
@ -326,8 +358,20 @@ _GSDebugAllocationAdd(Class c, id o)
}
the_table[num_classes].class = c;
the_table[num_classes].count = 1;
the_table[num_classes].nominal_size = class_getInstanceSize(c);
if (YES == debug_byte_size)
{
bytes = [o sizeOfInstance];
}
else
{
bytes = the_table[num_classes].nominal_size;
}
the_table[num_classes].bytes += bytes;
the_table[num_classes].totalb += bytes;
the_table[num_classes].lastb = 0;
the_table[num_classes].lastc = 0;
the_table[num_classes].total = 1;
the_table[num_classes].totalc = 1;
the_table[num_classes].peak = 1;
the_table[num_classes].is_recording = NO;
the_table[num_classes].recorded_objects = NULL;
@ -363,7 +407,7 @@ GSDebugAllocationTotal(Class c)
{
if (the_table[i].class == c)
{
return the_table[i].total;
return the_table[i].totalc;
}
}
return 0;
@ -468,7 +512,15 @@ GSDebugAllocationList(BOOL changeFlag)
{
list_entry *item = (list_entry*)order[index];
[result appendFormat: @"%d\t%s\n", item->count, item->name];
if (YES == debug_byte_size)
{
[result appendFormat: @"%d\t%-32s\t%ld\n",
item->count, item->name, item->bytes];
}
else
{
[result appendFormat: @"%d\t%s\n", item->count, item->name];
}
}
free(items);
return [result UTF8String];
@ -521,7 +573,15 @@ GSDebugAllocationListAll()
{
list_entry *item = (list_entry*)order[index];
[result appendFormat: @"%d\t%s\n", item->count, item->name];
if (YES == debug_byte_size)
{
[result appendFormat: @"%d\t%-32s\t%ld\n",
item->count, item->name, item->bytes];
}
else
{
[result appendFormat: @"%d\t%s\n", item->count, item->name];
}
}
free(items);
return [result UTF8String];
@ -536,17 +596,21 @@ _GSDebugAllocationFetch(list_entry *items, BOOL difference)
for (i = pos = 0; i < num_classes; i++)
{
int val = the_table[i].count;
int count = the_table[i].count;
long bytes = the_table[i].bytes;
if (difference)
{
val -= the_table[i].lastc;
count -= the_table[i].lastc;
bytes -= the_table[i].lastb;
the_table[i].lastc = the_table[i].count;
the_table[i].lastb = the_table[i].bytes;
}
if (val)
if (count || (bytes && debug_byte_size))
{
items[pos].name = class_getName(the_table[i].class);
items[pos].count = val;
items[pos].count = count;
items[pos].bytes = bytes;
pos++;
}
}
@ -554,6 +618,7 @@ _GSDebugAllocationFetch(list_entry *items, BOOL difference)
{
items[pos].name = 0;
items[pos].count = 0;
items[pos].bytes = 0;
pos++;
}
}
@ -566,7 +631,8 @@ _GSDebugAllocationFetchAll(list_entry *items)
for (i = 0; i < num_classes; i++)
{
items[i].name = class_getName(the_table[i].class);
items[i].count = the_table[i].total;
items[i].count = the_table[i].totalc;
items[i].bytes = the_table[i].totalb;
}
}
@ -588,9 +654,19 @@ _GSDebugAllocationRemove(Class c, id o)
if (the_table[i].class == c)
{
id tag = nil;
unsigned bytes;
doLock();
if (YES == debug_byte_size)
{
bytes = [o sizeOfInstance];
}
else
{
bytes = the_table[i].nominal_size;
}
the_table[i].count--;
the_table[i].bytes -= bytes;
if (the_table[i].is_recording)
{
unsigned j, k;

View file

@ -830,9 +830,7 @@ GS_ROOT_CLASS @interface GSDistantObjectPlaceHolder
*/
@implementation NSDistantObject(GNUstepExtensions)
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0;
}

View file

@ -53,6 +53,8 @@
#include <locale.h>
#endif
#include <malloc.h>
#import "GSPThread.h"
#if defined(HAVE_SYS_SIGNAL_H)
@ -2582,31 +2584,36 @@ GSPrivateMemorySize(NSObject *self, NSHashTable *exclude)
@implementation NSObject (MemoryFootprint)
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
{
unsigned count;
Ivar *vars;
Class cls = object_getClass(obj);
NSUInteger size = 0;
if (0 != (vars = class_copyIvarList(cls, &count)))
while (cls != Nil)
{
while (count-- > 0)
{
const char *type = ivar_getTypeEncoding(vars[count]);
unsigned count;
Ivar *vars;
type = GSSkipTypeQualifierAndLayoutInfo(type);
if ('@' == *type)
if (0 != (vars = class_copyIvarList(cls, &count)))
{
while (count-- > 0)
{
NSObject *content = object_getIvar(obj, vars[count]);
if (content != nil)
const char *type = ivar_getTypeEncoding(vars[count]);
type = GSSkipTypeQualifierAndLayoutInfo(type);
if ('@' == *type)
{
size += [content sizeInBytesExcluding: exclude];
NSObject *content = object_getIvar(obj, vars[count]);
if (content != nil)
{
size += [content sizeInBytesExcluding: exclude];
}
}
}
free(vars);
}
free(vars);
cls = class_getSuperclass(cls);
}
return size;
}
@ -2618,6 +2625,10 @@ GSPrivateMemorySize(NSObject *self, NSHashTable *exclude)
{
return 0;
}
+ (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0;
}
- (NSUInteger) sizeInBytes
{
NSUInteger bytes;
@ -2632,22 +2643,40 @@ GSPrivateMemorySize(NSObject *self, NSHashTable *exclude)
{
if (0 == NSHashGet(exclude, self))
{
Class c = object_getClass(self);
NSUInteger size = class_getInstanceSize(c);
NSUInteger size = [self sizeOfInstance];
NSHashInsert(exclude, self);
if (size > 0)
{
while (c != Nil)
{
size += [c contentSizeOf: self
declaredIn: c
excluding: exclude];
c = class_getSuperclass(c);
}
size += [self sizeOfContentExcluding: exclude];
}
return size;
}
return 0;
}
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0;
}
- (NSUInteger) sizeOfInstance
{
NSUInteger size;
#if GS_SIZEOF_VOIDP > 4
NSUInteger u = (NSUInteger)self;
if (u & 0x07)
{
return 0; // Small object has no size
}
#endif
#if HAVE_MALLOC_USABLE_SIZE
size = malloc_usable_size((void*)self - sizeof(intptr_t));
#else
size = class_getInstanceSize(object_getClass(self));
#endif
return size;
}
@end

View file

@ -85,13 +85,6 @@
return self;
}
+ (NSUInteger) contentSizeOf: (NSObject*)obj
declaredIn: (Class)cls
excluding: (NSHashTable*)exclude
{
return 0;
}
/**
* Returns the receiver.
*/
@ -561,6 +554,15 @@
{
return 0;
}
+ (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0;
}
+ (NSUInteger) sizeOfInstance
{
return 0;
}
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude
{
@ -570,20 +572,18 @@
NSUInteger size = class_getInstanceSize(c);
NSHashInsert(exclude, self);
if (size > 0)
{
while (c != Nil)
{
size += [c contentSizeOf: (NSObject*)self
declaredIn: c
excluding: exclude];
c = class_getSuperclass(c);
}
}
return size;
}
return 0;
}
- (NSUInteger) sizeOfContentExcluding: (NSHashTable*)exclude
{
return 0;
}
- (NSUInteger) sizeOfInstance
{
return class_getInstanceSize(object_getClass(self));
}
/**
* Returns the zone in which the receiver was allocated.

View file

@ -410,9 +410,9 @@ static inline BOOL timerInvalidated(NSTimer *t)
}
- (void) receivedEvent: (void*)data
type: (RunLoopEventType)type
extra: (void*)extra
forMode: (NSString*)mode
type: (RunLoopEventType)type
extra: (void*)extra
forMode: (NSString*)mode
{
#if HAVE_DISPATCH_MAIN_QUEUE_DRAIN_NP
dispatch_main_queue_drain_np();
@ -527,7 +527,7 @@ static inline BOOL timerInvalidated(NSTimer *t)
}
NSEndMapTableEnumeration(&enumerator);
/* Finally, fire the requests ands release them.
/* Finally, fire the requests and release them.
*/
for (i = 0; i < count; i++)
{
@ -955,7 +955,7 @@ updateTimer(NSTimer *t, NSDate *d, NSTimeInterval now)
{
NSTimeInterval add;
/* Just incrementing the date was insufficieint to bring it to
/* Just incrementing the date was insufficient to bring it to
* the current time, so we must have missed one or more fire
* opportunities, or the fire date has been set on the timer.
* If a fire date long ago has been set and the increment value

2
configure vendored
View file

@ -8707,7 +8707,7 @@ _ACEOF
fi
for ac_func in statvfs link symlink readlink geteuid getlogin getpwnam getpwnam_r getpwuid getpwuid_r getgrgid getgrgid_r getgrnam getgrnam_r rint getopt
for ac_func in statvfs link symlink readlink geteuid getlogin getpwnam getpwnam_r getpwuid getpwuid_r getgrgid getgrgid_r getgrnam getgrnam_r rint getopt malloc_usable_size
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"

View file

@ -2240,7 +2240,7 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
AC_CHECK_HEADERS(sys/types.h windows.h locale.h langinfo.h)
saved_LIBS="$LIBS"
AC_CHECK_LIB(m, main)
AC_CHECK_FUNCS(statvfs link symlink readlink geteuid getlogin getpwnam getpwnam_r getpwuid getpwuid_r getgrgid getgrgid_r getgrnam getgrnam_r rint getopt)
AC_CHECK_FUNCS(statvfs link symlink readlink geteuid getlogin getpwnam getpwnam_r getpwuid getpwuid_r getgrgid getgrgid_r getgrnam getgrnam_r rint getopt malloc_usable_size)
LIBS="$saved_LIBS"
AC_CACHE_CHECK([for pw_gecos field in struct passwd],