mirror of
https://github.com/gnustep/libs-ec.git
synced 2025-02-22 19:31:53 +00:00
memory management tweaks
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/ec/trunk@38766 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
71c4c118c4
commit
2439001507
3 changed files with 353 additions and 224 deletions
|
@ -1,3 +1,11 @@
|
||||||
|
2015-07-08 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
* EcProcess.h:
|
||||||
|
* EcProcess.m:
|
||||||
|
Allow the memory monitoring configuration to be set from the Console
|
||||||
|
via the 'memory' command. Improve the help text. Improve the info
|
||||||
|
provided in the debug/error reports.
|
||||||
|
|
||||||
2015-07-06 Richard Frith-Macdonald <rfm@gnu.org>
|
2015-07-06 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
* EcProcess.h:
|
* EcProcess.h:
|
||||||
|
|
43
EcProcess.h
43
EcProcess.h
|
@ -269,34 +269,41 @@ extern NSString* cmdVersion(NSString *ver);
|
||||||
* <desc>
|
* <desc>
|
||||||
* This boolean value determines whether statistics on creation
|
* This boolean value determines whether statistics on creation
|
||||||
* and destruction of objects are maintained.<br />
|
* and destruction of objects are maintained.<br />
|
||||||
* This may be set on the command line or in Control.plist, but
|
* This may be set in the NSUserDefaults system or in Control.plist,
|
||||||
* may be overridden by using the 'Memory' command in the
|
* but may be overridden by using the 'memory' command in the
|
||||||
* Console program.
|
* Console program.
|
||||||
* </desc>
|
* </desc>
|
||||||
* <term>EcMemoryAllowed</term>
|
* <term>EcMemoryAllowed</term>
|
||||||
* <desc>
|
* <desc>
|
||||||
* This may be used to specify the total process memory usage
|
* This may be used to specify the total process memory usage
|
||||||
* (in megabytes) before memory usage alerting may begin.<br />
|
* (in megabytes) before memory usage alerting may begin.<br />
|
||||||
* If this is not specified (or a negative or excessive value
|
* Memory usage 'error' reports are then generated every time
|
||||||
|
* the average (over ten minutes) memory usage exceeds a warning
|
||||||
|
* threshold (the threshold is then increased).<br />
|
||||||
|
* If this setting is not specified (or a negative or excessive value
|
||||||
* is specified) then memory is monitored for ten minutes and
|
* is specified) then memory is monitored for ten minutes and
|
||||||
* the threshold is set at the peak during that period plus a
|
* the threshold is set at the peak during that period plus a
|
||||||
* margin to allow further memory growth before warning.<br />
|
* margin to allow further memory growth before warning.<br />
|
||||||
* The margin is at least 50KB on a test system, 5000KB on
|
* The minimum margin is determined by the EcMemoryIncrement and
|
||||||
* a non-test system.
|
* EcMemoryPercentage settings.<br />
|
||||||
|
* This may be set in the NSUserDefaults system or in Control.plist,
|
||||||
|
* but may be overridden by using the 'memory' command in the
|
||||||
|
* Console program.
|
||||||
* </desc>
|
* </desc>
|
||||||
* <term>EcMemoryIncrement</term>
|
* <term>EcMemoryIncrement</term>
|
||||||
* <desc>
|
* <desc>
|
||||||
* This integer value controls the (KBytes) increment (from
|
* This integer value controls the (KBytes) increment (from
|
||||||
* current peak value) in process memory usage after which
|
* current peak value) in process memory usage after which
|
||||||
* an alert is generated.<br />
|
* an alert is generated.<br />
|
||||||
* If this is not set (or is set to a value less than ten or
|
* If this is not set (or is set to a value less than 10KB or
|
||||||
* greater than a million) then a value of five thousand is used
|
* greater than 1000000KB) then a value of 5000KB is used.<br />
|
||||||
* unless EcMemory is set (in which case fifty is used).<br />
|
|
||||||
* Setting a higher value makes memory leak detection less
|
* Setting a higher value makes memory leak detection less
|
||||||
* sensitive (but reduces unnecessary alerts).<br />
|
* sensitive (but reduces unnecessary alerts).<br />
|
||||||
* If used in conjunction with EcMemoryPercentage, the greater
|
* If used in conjunction with EcMemoryPercentage, the greater
|
||||||
* of the two allowed memory values is used.<br />
|
* of the two allowed memory values is used.<br />
|
||||||
* This may be set on the command line or in Control.plist
|
* This may be set in the NSUserDefaults system or in Control.plist,
|
||||||
|
* but may be overridden by using the 'memory' command in the
|
||||||
|
* Console program.
|
||||||
* </desc>
|
* </desc>
|
||||||
* <term>EcMemoryMaximum</term>
|
* <term>EcMemoryMaximum</term>
|
||||||
* <desc>
|
* <desc>
|
||||||
|
@ -306,7 +313,10 @@ extern NSString* cmdVersion(NSString *ver);
|
||||||
* If the total memory usage of the process reaches this threshold,
|
* If the total memory usage of the process reaches this threshold,
|
||||||
* the -cmdQuit: method will be called with an argument of -1.<br />
|
* the -cmdQuit: method will be called with an argument of -1.<br />
|
||||||
* If this is not specified (or a negative value is specified)
|
* If this is not specified (or a negative value is specified)
|
||||||
* the process will never shut down due to excessive memory usage.
|
* the process will never shut down due to excessive memory usage.<br />
|
||||||
|
* This may be set in the NSUserDefaults system or in Control.plist,
|
||||||
|
* but may be overridden by using the 'memory' command in the
|
||||||
|
* Console program.
|
||||||
* </desc>
|
* </desc>
|
||||||
* <term>EcMemoryPercentage</term>
|
* <term>EcMemoryPercentage</term>
|
||||||
* <desc>
|
* <desc>
|
||||||
|
@ -314,14 +324,15 @@ extern NSString* cmdVersion(NSString *ver);
|
||||||
* threshold after which a memory usage alert is generated.<br />
|
* threshold after which a memory usage alert is generated.<br />
|
||||||
* The increase is calculated as a percentage of the current
|
* The increase is calculated as a percentage of the current
|
||||||
* peak memory usage value when an alert is generated.<br />
|
* peak memory usage value when an alert is generated.<br />
|
||||||
* If this is not set (or is set to a value less than one or
|
* If this is not set (or is set to a value less than 1 or
|
||||||
* greater than a thousand) then a value of ten is used unless
|
* greater than 1000) then a value of 10 is used.<br />
|
||||||
* EcMemory is set (in which case a value of one is used).<br />
|
|
||||||
* Setting a higher value make memory leak detection less
|
* Setting a higher value make memory leak detection less
|
||||||
* sensitive (but reduces unnecessary alerts).<br />
|
* sensitive (but reduces unnecessary alerts).<br />
|
||||||
* If used in conjunction with EcMemoryIncrement, the greater
|
* If used in conjunction with EcMemoryIncrement, the greater
|
||||||
* of the two allowed memory values is used.<br />
|
* of the two allowed memory values is used.<br />
|
||||||
* This may be set on the command line or in Control.plist
|
* This may be set in the NSUserDefaults system or in Control.plist,
|
||||||
|
* but may be overridden by using the 'memory' command in the
|
||||||
|
* Console program.
|
||||||
* </desc>
|
* </desc>
|
||||||
* <term>EcRelease</term>
|
* <term>EcRelease</term>
|
||||||
* <desc>
|
* <desc>
|
||||||
|
@ -330,8 +341,8 @@ extern NSString* cmdVersion(NSString *ver);
|
||||||
* this on has a big impact on program performance and is not
|
* this on has a big impact on program performance and is not
|
||||||
* recommended except for debugging crashes and other memory
|
* recommended except for debugging crashes and other memory
|
||||||
* issues.<br />
|
* issues.<br />
|
||||||
* This may be set on the command line or in Control.plist, but
|
* This may be set in the NSUserDefaults system or in Control.plist,
|
||||||
* may be overridden by using the 'release' command in the
|
* but may be overridden by using the 'release' command in the
|
||||||
* Console program.
|
* Console program.
|
||||||
* </desc>
|
* </desc>
|
||||||
* <term>EcTesting</term>
|
* <term>EcTesting</term>
|
||||||
|
|
486
EcProcess.m
486
EcProcess.m
|
@ -1004,6 +1004,7 @@ findMode(NSDictionary* d, NSString* s)
|
||||||
@interface EcProcess (Private)
|
@interface EcProcess (Private)
|
||||||
- (void) cmdMesgrelease: (NSArray*)msg;
|
- (void) cmdMesgrelease: (NSArray*)msg;
|
||||||
- (void) cmdMesgtesting: (NSArray*)msg;
|
- (void) cmdMesgtesting: (NSArray*)msg;
|
||||||
|
- (void) _memCheck;
|
||||||
- (NSString*) _moveLog: (NSString*)name to: (NSString*)sub;
|
- (NSString*) _moveLog: (NSString*)name to: (NSString*)sub;
|
||||||
- (void) _timedOut: (NSTimer*)timer;
|
- (void) _timedOut: (NSTimer*)timer;
|
||||||
- (void) _update: (NSMutableDictionary*)info;
|
- (void) _update: (NSMutableDictionary*)info;
|
||||||
|
@ -1261,16 +1262,20 @@ static NSString *noFiles = @"No log files to archive";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memAllowed = (uint64_t)[cmdDefs integerForKey: @"MemoryAllowed"];
|
memAllowed = (uint64_t)[cmdDefs integerForKey: @"MemoryAllowed"];
|
||||||
|
#if SIZEOF_VOIDP == 4
|
||||||
if (memAllowed > 200000)
|
if (memAllowed > 200000)
|
||||||
{
|
{
|
||||||
memAllowed = 0;
|
memAllowed = 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
memMaximum = (uint64_t)[cmdDefs integerForKey: @"MemoryMaximum"];
|
memMaximum = (uint64_t)[cmdDefs integerForKey: @"MemoryMaximum"];
|
||||||
|
#if SIZEOF_VOIDP == 4
|
||||||
if (memMaximum > 200000)
|
if (memMaximum > 200000)
|
||||||
{
|
{
|
||||||
memMaximum = 0; // Disabled
|
memMaximum = 0; // Disabled
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
str = [cmdDefs stringForKey: @"CoreSize"];
|
str = [cmdDefs stringForKey: @"CoreSize"];
|
||||||
if (nil == str)
|
if (nil == str)
|
||||||
|
@ -2226,10 +2231,6 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
||||||
|
|
||||||
- (void) ecNewMinute: (NSCalendarDate*)when
|
- (void) ecNewMinute: (NSCalendarDate*)when
|
||||||
{
|
{
|
||||||
BOOL memDebug = [cmdDefs boolForKey: @"Memory"];
|
|
||||||
FILE *fptr;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
#ifndef __MINGW__
|
#ifndef __MINGW__
|
||||||
if (NO == cmdIsQuitting)
|
if (NO == cmdIsQuitting)
|
||||||
{
|
{
|
||||||
|
@ -2284,156 +2285,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
||||||
*/
|
*/
|
||||||
[NSHost flushHostCache];
|
[NSHost flushHostCache];
|
||||||
|
|
||||||
/* /proc/pid/statm reports the process memory size in 4KB pages
|
[self _memCheck];
|
||||||
*/
|
|
||||||
fptr = fopen([[NSString stringWithFormat: @"/proc/%d/statm",
|
|
||||||
[[NSProcessInfo processInfo] processIdentifier]] UTF8String], "r");
|
|
||||||
memLast = 1;
|
|
||||||
if (NULL != fptr)
|
|
||||||
{
|
|
||||||
if (fscanf(fptr, "%"PRIu64, &memLast) != 1)
|
|
||||||
{
|
|
||||||
memLast = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memLast *= (4 * 1024);
|
|
||||||
if (memLast <= 0) memLast = 1;
|
|
||||||
}
|
|
||||||
fclose(fptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do initial population so we can work immediately.
|
|
||||||
*/
|
|
||||||
if (0 == memSlot)
|
|
||||||
{
|
|
||||||
for (i = 1; i < MEMCOUNT; i++)
|
|
||||||
{
|
|
||||||
memRoll[i] = memLast;
|
|
||||||
}
|
|
||||||
memPrev = memStrt = memLast;
|
|
||||||
ASSIGN(memTime, [NSDate date]);
|
|
||||||
}
|
|
||||||
memRoll[memSlot % MEMCOUNT] = memLast;
|
|
||||||
memSlot++;
|
|
||||||
|
|
||||||
/* Find the average usage over the last set of samples.
|
|
||||||
* Round up to a block size.
|
|
||||||
*/
|
|
||||||
memAvge = 0;
|
|
||||||
for (i = 0; i < MEMCOUNT; i++)
|
|
||||||
{
|
|
||||||
memAvge += memRoll[i];
|
|
||||||
}
|
|
||||||
memAvge /= MEMCOUNT;
|
|
||||||
|
|
||||||
/* Convert to 1KB blocks.
|
|
||||||
*/
|
|
||||||
if (memAvge % 1024)
|
|
||||||
{
|
|
||||||
memAvge = ((memAvge / 1024) + 1) * 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update peak memory usage if necessary.
|
|
||||||
*/
|
|
||||||
if (memLast > memPeak)
|
|
||||||
{
|
|
||||||
memPeak = memLast;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we have a defined maximum memory usage for the process,
|
|
||||||
* we should shut down with a non-zero status to get a restart.
|
|
||||||
*/
|
|
||||||
if (memMaximum > 0 && memPeak > (memMaximum * 1024 * 1024))
|
|
||||||
{
|
|
||||||
if (NO == cmdIsQuitting)
|
|
||||||
{
|
|
||||||
cmdIsQuitting = YES;
|
|
||||||
[self cmdQuit: -1];
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the average memory usage is above the warning threshold,
|
|
||||||
* we should alert and reset the threshold.
|
|
||||||
* During the first ten minutes
|
|
||||||
*/
|
|
||||||
if (memAvge > memWarn || memSlot < MEMCOUNT)
|
|
||||||
{
|
|
||||||
NSInteger inc;
|
|
||||||
NSInteger pct;
|
|
||||||
NSInteger iMax = 0;
|
|
||||||
NSInteger pMax = 0;
|
|
||||||
|
|
||||||
/* We increase the threshold for the next alert by a percentage
|
|
||||||
* of the existing usage or by a fixed increment, whichever is
|
|
||||||
* the larger.
|
|
||||||
*/
|
|
||||||
pct = [cmdDefs integerForKey: @"MemoryPercentage"];
|
|
||||||
if (pct < 1 || pct > 1000) pct = 0;
|
|
||||||
inc = [cmdDefs integerForKey: @"MemoryIncrement"];
|
|
||||||
if (inc < 10 || inc > 1000000) inc = 0;
|
|
||||||
if (0 == inc && 0 == pct)
|
|
||||||
{
|
|
||||||
if (YES == memDebug)
|
|
||||||
{
|
|
||||||
/* We want detailed memory information, so we set the next
|
|
||||||
* alerting threshold 50 KB above the current peak usage.
|
|
||||||
*/
|
|
||||||
inc = 50;
|
|
||||||
pct = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* We do not want detailed memory information,
|
|
||||||
* so we set the next alerting threshold from
|
|
||||||
* 5000 KB above the current peak usage,
|
|
||||||
* ensuring that only serious increases
|
|
||||||
* in usage will generate an alert.
|
|
||||||
*/
|
|
||||||
inc = 5000;
|
|
||||||
pct = 10; // Use ten percent if more than fixed increment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inc > 0)
|
|
||||||
{
|
|
||||||
iMax = memPeak + (inc * 1024);
|
|
||||||
}
|
|
||||||
if (pct > 0)
|
|
||||||
{
|
|
||||||
pMax = (memPeak * (100 + pct)) / 100;
|
|
||||||
}
|
|
||||||
memWarn = (iMax > pMax) ? iMax : pMax;
|
|
||||||
if (memWarn % 1024)
|
|
||||||
{
|
|
||||||
memWarn = (memWarn/1024 + 1) * 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If not in the initial period, we need to generate an alert
|
|
||||||
* because the average has risen above the allowed size.
|
|
||||||
*/
|
|
||||||
if (memSlot >= MEMCOUNT)
|
|
||||||
{
|
|
||||||
uint64_t prev;
|
|
||||||
NSDate *when;
|
|
||||||
|
|
||||||
prev = memPrev;
|
|
||||||
when = AUTORELEASE(memTime);
|
|
||||||
memPrev = memAvge;
|
|
||||||
memTime = [NSDate new];
|
|
||||||
[self cmdError: @"Average memory usage grown from %"
|
|
||||||
PRIu64"KB to %"PRIu64"KB since %@",
|
|
||||||
prev/1024, memAvge/1024, when];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (YES == memDebug)
|
|
||||||
{
|
|
||||||
[self cmdDbg: cmdDetailDbg
|
|
||||||
msg: @"Memory usage %"PRIu64"KB", memLast/1024];
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) ecHadIP: (NSDate*)when
|
- (void) ecHadIP: (NSDate*)when
|
||||||
|
@ -3237,39 +3089,47 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
||||||
|
|
||||||
- (void) cmdMesgmemory: (NSArray*)msg
|
- (void) cmdMesgmemory: (NSArray*)msg
|
||||||
{
|
{
|
||||||
if ([msg count] == 0
|
if ([msg count] == 0)
|
||||||
|| [[msg objectAtIndex: 0] caseInsensitiveCompare: @"help"]
|
|
||||||
== NSOrderedSame)
|
|
||||||
{
|
{
|
||||||
[self cmdPrintf: @"controls recording of memory management statistics"];
|
[self cmdPrintf: @"controls recording of memory management statistics"];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ([[msg objectAtIndex: 0] caseInsensitiveCompare: @"help"]
|
if ([[msg objectAtIndex: 0] caseInsensitiveCompare: @"help"]
|
||||||
== NSOrderedSame)
|
== NSOrderedSame || ([msg count] > 1
|
||||||
|
&& [[msg objectAtIndex: 1] caseInsensitiveCompare: @"help"]
|
||||||
|
== NSOrderedSame))
|
||||||
{
|
{
|
||||||
[self cmdPrintf: @"\n"];
|
[self cmdPrintf: @"\n\
|
||||||
[self cmdPrintf: @"Without parameters, the memory command is "];
|
Without parameters,\n\
|
||||||
[self cmdPrintf: @"used to list the changes in the numbers of "];
|
the memory command is used to list the changes in the numbers of objects\n\
|
||||||
[self cmdPrintf: @"objects allocated since the command was "];
|
allocated since the command was last issued.\n\
|
||||||
[self cmdPrintf: @"last issued.\n"];
|
With the single parameter 'all',\n\
|
||||||
[self cmdPrintf: @"With the single parameter 'all', the memory "];
|
the memory command is used to list the cumulative totals of objects\n\
|
||||||
[self cmdPrintf: @"command is used to list the cumulative totals "];
|
allocated since the first time a memory command was issued.\n\
|
||||||
[self cmdPrintf: @"of objects allocated since the first time a "];
|
With the single parameter 'yes',\n\
|
||||||
[self cmdPrintf: @"memory command was issued.\n"];
|
the memory command is used to turn on gathering of memory usage statistics.\n\
|
||||||
[self cmdPrintf: @"With the single parameter 'yes', the memory "];
|
With the single parameter 'no',\n\
|
||||||
[self cmdPrintf: @"command is used to turn on gathering of "];
|
the memory command is used to turn off gathering of memory usage statistics.\n\
|
||||||
[self cmdPrintf: @"memory usage statistics.\n"];
|
With the single parameter 'default',\n\
|
||||||
[self cmdPrintf: @"With the single parameter 'no', the memory "];
|
the gathering of memory usage statistics reverts to the default setting.\n\
|
||||||
[self cmdPrintf: @"command is used to turn off gathering of "];
|
With two parameters ('class' and a class name),\n\
|
||||||
[self cmdPrintf: @"memory usage statistics.\n"];
|
new instances of the class are recorded.\n\
|
||||||
[self cmdPrintf: @"With the single parameter 'default', the "];
|
With two parameters ('list' and a class),\n\
|
||||||
[self cmdPrintf: @"gathering of memory usage statistics reverts "];
|
recorded instances of the class are reported.\n\
|
||||||
[self cmdPrintf: @"to the default setting.\n"];
|
With two parameters ('allowed' and a number),\n\
|
||||||
[self cmdPrintf: @"With two parameters ('class' and a class name), "];
|
the threshold for warnings about process size is set (in MB).\n\
|
||||||
[self cmdPrintf: @"new instances of the class are recorded.\n"];
|
Set to 'default' to revert to the default.\n\
|
||||||
[self cmdPrintf: @"With two parameters ('list' and a class), "];
|
With two parameters ('increment' and a number),\n\
|
||||||
[self cmdPrintf: @"recorded instances of the class are reported.\n"];
|
the size increment between warnings about process size is set (in KB\n\
|
||||||
|
from 10 to 1000000). Set to 'default' to revert to the default.\n\
|
||||||
|
With two parameters ('percentage' and a number),\n\
|
||||||
|
the percentage increment between warnings about process memory size is\n\
|
||||||
|
set (from 1 to 1000). Set to 'default' to revert to the default.\n\
|
||||||
|
With two parameters ('maximum' and a number),\n\
|
||||||
|
the maximum process size (in MB) is set. On reaching the limit, the\n\
|
||||||
|
process restarts unless the limit is zero (meaning no maximum).\n\
|
||||||
|
Set to 'default' to revert to the default."];
|
||||||
[self cmdPrintf: @"\n"];
|
[self cmdPrintf: @"\n"];
|
||||||
}
|
}
|
||||||
else if ([msg count] == 2)
|
else if ([msg count] == 2)
|
||||||
|
@ -3327,21 +3187,98 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
||||||
}
|
}
|
||||||
else if ([msg count] == 3)
|
else if ([msg count] == 3)
|
||||||
{
|
{
|
||||||
NSString *name = [msg objectAtIndex: 2];
|
NSString *op = [msg objectAtIndex: 1];
|
||||||
Class c = NSClassFromString(name);
|
NSString *arg = [msg objectAtIndex: 2];
|
||||||
|
NSInteger val = [arg integerValue];
|
||||||
|
|
||||||
if (Nil == c)
|
if ([op caseInsensitiveCompare: @"allowed"] == NSOrderedSame)
|
||||||
{
|
{
|
||||||
[self cmdPrintf: @"Unable to find class '%@'.\n", name];
|
if (val <= 0)
|
||||||
|
{
|
||||||
|
[cmdDefs setCommand: nil forKey: @"MemoryAllowed"];
|
||||||
|
if (0 == memAllowed)
|
||||||
|
{
|
||||||
|
/* The threshold was set back to zero ... to be
|
||||||
|
* calculated from a ten minute baseline.
|
||||||
|
*/
|
||||||
|
memSlot = 0;
|
||||||
|
}
|
||||||
|
[self cmdPrintf: @"MemoryAllowed using default value.\n"];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NSString *op = [msg objectAtIndex: 1];
|
arg = [NSString stringWithFormat: @"%"PRIu64, (uint64_t)val];
|
||||||
|
[cmdDefs setCommand: arg forKey: @"MemoryAllowed"];
|
||||||
|
[self cmdPrintf: @"MemoryAllowed set to %@MB.\n", arg];
|
||||||
|
}
|
||||||
|
memWarn = memAllowed * 1024 * 1024;
|
||||||
|
DESTROY(memTime);
|
||||||
|
[self _memCheck];
|
||||||
|
}
|
||||||
|
else if ([op caseInsensitiveCompare: @"increment"] == NSOrderedSame)
|
||||||
|
{
|
||||||
|
if (val <= 10 || val > 1000000)
|
||||||
|
{
|
||||||
|
[cmdDefs setCommand: nil forKey: @"MemoryIncrement"];
|
||||||
|
[self cmdPrintf: @"MemoryIncrement using default value.\n"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arg = [NSString stringWithFormat: @"%"PRIu64, (uint64_t)val];
|
||||||
|
[cmdDefs setCommand: arg forKey: @"MemoryIncrement"];
|
||||||
|
[self cmdPrintf: @"MemoryIncrement set to %@KB.\n", arg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ([op caseInsensitiveCompare: @"percentage"] == NSOrderedSame)
|
||||||
|
{
|
||||||
|
if (val <= 0 || val > 1000)
|
||||||
|
{
|
||||||
|
[cmdDefs setCommand: nil forKey: @"MemoryPercentage"];
|
||||||
|
[self cmdPrintf: @"MemoryPercentage using default value.\n"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arg = [NSString stringWithFormat: @"%"PRIu64, (uint64_t)val];
|
||||||
|
[cmdDefs setCommand: arg forKey: @"MemoryPercentage"];
|
||||||
|
[self cmdPrintf: @"MemoryPercentage set to %@.\n", arg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ([op caseInsensitiveCompare: @"maximum"] == NSOrderedSame)
|
||||||
|
{
|
||||||
|
if (val <= 0)
|
||||||
|
{
|
||||||
|
if ([arg caseInsensitiveCompare: @"default"] == NSOrderedSame)
|
||||||
|
{
|
||||||
|
[cmdDefs setCommand: nil forKey: @"MemoryMaximum"];
|
||||||
|
[self cmdPrintf: @"MemoryMaximum using default value.\n"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[cmdDefs setCommand: @"0" forKey: @"MemoryMaximum"];
|
||||||
|
[self cmdPrintf: @"MemoryMaximum restart turned off.\n"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arg = [NSString stringWithFormat: @"%"PRIu64, (uint64_t)val];
|
||||||
|
[cmdDefs setCommand: arg forKey: @"MemoryMaximum"];
|
||||||
|
[self cmdPrintf: @"MemoryMaximum set to %@MB.\n", arg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Class c = NSClassFromString(arg);
|
||||||
|
|
||||||
|
if (Nil == c)
|
||||||
|
{
|
||||||
|
[self cmdPrintf: @"Unable to find class '%@'.\n", arg];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if ([op caseInsensitiveCompare: @"class"] == NSOrderedSame)
|
if ([op caseInsensitiveCompare: @"class"] == NSOrderedSame)
|
||||||
{
|
{
|
||||||
GSDebugAllocationActiveRecordingObjects(c);
|
GSDebugAllocationActiveRecordingObjects(c);
|
||||||
[self cmdPrintf: @"Recording instances of '%@'.\n", name];
|
[self cmdPrintf: @"Recording instances of '%@'.\n", arg];
|
||||||
}
|
}
|
||||||
else if ([op caseInsensitiveCompare: @"list"] == NSOrderedSame)
|
else if ([op caseInsensitiveCompare: @"list"] == NSOrderedSame)
|
||||||
{
|
{
|
||||||
|
@ -3350,7 +3287,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
||||||
NSUInteger index;
|
NSUInteger index;
|
||||||
|
|
||||||
array = GSDebugAllocationListRecordedObjects(c);
|
array = GSDebugAllocationListRecordedObjects(c);
|
||||||
[self cmdPrintf: @"Current instances of '%@':\n", name];
|
[self cmdPrintf: @"Current instances of '%@':\n", arg];
|
||||||
count = [array count];
|
count = [array count];
|
||||||
for (index = 0; index < count; index++)
|
for (index = 0; index < count; index++)
|
||||||
{
|
{
|
||||||
|
@ -3364,6 +3301,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[self cmdPrintf: @"\n%@ on %@ running since %@\n\n",
|
[self cmdPrintf: @"\n%@ on %@ running since %@\n\n",
|
||||||
|
@ -3420,7 +3358,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
||||||
[self cmdPrintf: @" %"PRIu64"KB (average),"
|
[self cmdPrintf: @" %"PRIu64"KB (average),"
|
||||||
@" %"PRIu64"KB (start)\n",
|
@" %"PRIu64"KB (start)\n",
|
||||||
memAvge/1024, memStrt/1024];
|
memAvge/1024, memStrt/1024];
|
||||||
[self cmdPrintf: @" %"PRIu64"KB (exempt)\n",
|
[self cmdPrintf: @" %"PRIu64"KB (reserved)\n",
|
||||||
[self ecNotLeaked]/1024];
|
[self ecNotLeaked]/1024];
|
||||||
[self cmdPrintf:
|
[self cmdPrintf:
|
||||||
@"Memory error reporting after average usage: %"PRIu64"KB\n",
|
@"Memory error reporting after average usage: %"PRIu64"KB\n",
|
||||||
|
@ -4306,6 +4244,178 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) _memCheck
|
||||||
|
{
|
||||||
|
BOOL memDebug = [cmdDefs boolForKey: @"Memory"];
|
||||||
|
FILE *fptr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* /proc/pid/statm reports the process memory size in 4KB pages
|
||||||
|
*/
|
||||||
|
fptr = fopen([[NSString stringWithFormat: @"/proc/%d/statm",
|
||||||
|
[[NSProcessInfo processInfo] processIdentifier]] UTF8String], "r");
|
||||||
|
memLast = 1;
|
||||||
|
if (NULL != fptr)
|
||||||
|
{
|
||||||
|
if (fscanf(fptr, "%"PRIu64, &memLast) != 1)
|
||||||
|
{
|
||||||
|
memLast = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memLast *= (4 * 1024);
|
||||||
|
if (memLast <= 0) memLast = 1;
|
||||||
|
}
|
||||||
|
fclose(fptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do initial population so we can work immediately.
|
||||||
|
*/
|
||||||
|
if (0 == memSlot)
|
||||||
|
{
|
||||||
|
for (i = 1; i < MEMCOUNT; i++)
|
||||||
|
{
|
||||||
|
memRoll[i] = memLast;
|
||||||
|
}
|
||||||
|
memPrev = memStrt = memLast;
|
||||||
|
}
|
||||||
|
memRoll[memSlot % MEMCOUNT] = memLast;
|
||||||
|
memSlot++;
|
||||||
|
|
||||||
|
/* Find the average usage over the last set of samples.
|
||||||
|
* Round up to a block size.
|
||||||
|
*/
|
||||||
|
memAvge = 0;
|
||||||
|
for (i = 0; i < MEMCOUNT; i++)
|
||||||
|
{
|
||||||
|
memAvge += memRoll[i];
|
||||||
|
}
|
||||||
|
memAvge /= MEMCOUNT;
|
||||||
|
|
||||||
|
/* Convert to 1KB blocks.
|
||||||
|
*/
|
||||||
|
if (memAvge % 1024)
|
||||||
|
{
|
||||||
|
memAvge = ((memAvge / 1024) + 1) * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update peak memory usage if necessary.
|
||||||
|
*/
|
||||||
|
if (memLast > memPeak)
|
||||||
|
{
|
||||||
|
memPeak = memLast;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have a defined maximum memory usage for the process,
|
||||||
|
* we should shut down with a non-zero status to get a restart.
|
||||||
|
*/
|
||||||
|
if (memMaximum > 0 && memPeak > (memMaximum * 1024 * 1024))
|
||||||
|
{
|
||||||
|
if (NO == cmdIsQuitting)
|
||||||
|
{
|
||||||
|
cmdIsQuitting = YES;
|
||||||
|
[self cmdQuit: -1];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the average memory usage is above the threshold, we alert and reset
|
||||||
|
* the threshold.
|
||||||
|
* During the first ten minutes though, we always adjust the threshold and
|
||||||
|
* we suppress any warnings. This gives us a more stable starting point.
|
||||||
|
*/
|
||||||
|
if (memAvge > memWarn || memSlot < MEMCOUNT)
|
||||||
|
{
|
||||||
|
NSInteger inc;
|
||||||
|
NSInteger pct;
|
||||||
|
NSInteger iMax = 0;
|
||||||
|
NSInteger pMax = 0;
|
||||||
|
|
||||||
|
/* We increase the threshold for the next alert by a percentage
|
||||||
|
* of the existing usage or by a fixed increment, whichever is
|
||||||
|
* the larger.
|
||||||
|
*/
|
||||||
|
pct = [cmdDefs integerForKey: @"MemoryPercentage"];
|
||||||
|
if (pct < 1 || pct > 1000) pct = 0;
|
||||||
|
inc = [cmdDefs integerForKey: @"MemoryIncrement"];
|
||||||
|
if (inc < 10 || inc > 1000000) inc = 0;
|
||||||
|
if (0 == inc && 0 == pct)
|
||||||
|
{
|
||||||
|
if (YES == memDebug)
|
||||||
|
{
|
||||||
|
/* We want detailed memory information, so we set the next
|
||||||
|
* alerting threshold 50 KB above the current peak usage.
|
||||||
|
*/
|
||||||
|
inc = 50;
|
||||||
|
pct = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We do not want detailed memory information,
|
||||||
|
* so we set the next alerting threshold from
|
||||||
|
* 5000 KB above the current peak usage,
|
||||||
|
* ensuring that only serious increases
|
||||||
|
* in usage will generate an alert.
|
||||||
|
*/
|
||||||
|
inc = 5000;
|
||||||
|
pct = 10; // Use ten percent if more than fixed increment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inc > 0)
|
||||||
|
{
|
||||||
|
iMax = memPeak + (inc * 1024);
|
||||||
|
}
|
||||||
|
if (pct > 0)
|
||||||
|
{
|
||||||
|
pMax = (memPeak * (100 + pct)) / 100;
|
||||||
|
}
|
||||||
|
memWarn = (iMax > pMax) ? iMax : pMax;
|
||||||
|
if (memWarn % 1024)
|
||||||
|
{
|
||||||
|
memWarn = (memWarn/1024 + 1) * 1024;
|
||||||
|
}
|
||||||
|
if (memWarn < memAllowed * 1024 * 1024)
|
||||||
|
{
|
||||||
|
/* Never warn at less than the allowed memory.
|
||||||
|
*/
|
||||||
|
memWarn = memAllowed * 1024 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If not in the initial period, we need to generate an alert
|
||||||
|
* because the average has risen above the allowed size.
|
||||||
|
*/
|
||||||
|
if (memSlot >= MEMCOUNT)
|
||||||
|
{
|
||||||
|
uint64_t prev;
|
||||||
|
NSDate *when;
|
||||||
|
|
||||||
|
prev = memPrev;
|
||||||
|
when = AUTORELEASE(memTime);
|
||||||
|
memPrev = memAvge;
|
||||||
|
memTime = [NSDate new];
|
||||||
|
if (nil == when)
|
||||||
|
{
|
||||||
|
[self cmdError: @"Average memory usage grown from %"
|
||||||
|
PRIu64"KB to %"PRIu64"KB (reserved: %"PRIu64"KB)",
|
||||||
|
prev/1024, memAvge/1024, [self ecNotLeaked]/1024];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[self cmdError: @"Average memory usage grown from %"
|
||||||
|
PRIu64"KB to %"PRIu64"KB (reserved: %"PRIu64"KB) since %@",
|
||||||
|
prev/1024, memAvge/1024, [self ecNotLeaked]/1024, when];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (YES == memDebug)
|
||||||
|
{
|
||||||
|
[self cmdDbg: cmdDetailDbg
|
||||||
|
msg: @"Memory usage %"PRIu64"KB (reserved: %"PRIu64"KB)",
|
||||||
|
memLast/1024, [self ecNotLeaked]/1024];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (NSString*) _moveLog: (NSString*)name to: (NSString*)sub
|
- (NSString*) _moveLog: (NSString*)name to: (NSString*)sub
|
||||||
{
|
{
|
||||||
NSString *status;
|
NSString *status;
|
||||||
|
|
Loading…
Reference in a new issue