mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
Improved memory usage reporting mechanisms
This commit is contained in:
parent
2ca4f74af5
commit
dd36855923
21 changed files with 404 additions and 230 deletions
31
ChangeLog
31
ChangeLog
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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]
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
118
Source/NSDebug.m
118
Source/NSDebug.m
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
2
configure
vendored
|
@ -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"
|
||||
|
|
|
@ -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],
|
||||
|
|
Loading…
Reference in a new issue