Implement an interface to export memory usage statistics by loading

a bundle. Controlled using the `MemoryLoggerBundle' defaults key.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/ec/trunk@38798 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Niels Grewe 2015-07-15 08:41:14 +00:00
parent dc9b555202
commit 7acbd5f539
4 changed files with 160 additions and 1 deletions

View file

@ -1,3 +1,9 @@
2015-07-15 Niels Grewe <niels.grewe@halbordnung.de>
* EcMemoryLogger.h
* EcProcess.m:
Add the ability to load a bundle to export memory logs to. Configured
using the 'MemoryLoggerBundle' default key.
2015-07-13 Richard Frith-Macdonald <rfm@gnu.org>
* EcProcess.h:

48
EcMemoryLogger.h Normal file
View file

@ -0,0 +1,48 @@
/** Enterprise Control Configuration and Logging
-- memory logger protocol
Copyright (C) 2015 Free Software Foundation, Inc.
Written by: Niels Grewe <niels.grewe@halbordnung.de>
Date: July 2015
This file is part of the GNUstep project.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#import <Foundation/NSObject.h>
@class EcProcess;
/**
* This protocol should be implemented by classes that want to receive
* callbacks about memory usage in the process. This feature is enabled
* by setting the MemoryLoggerBundle user default to a bundle whose
* principal class implements this protocol.
*/
@protocol EcMemoryLogger <NSObject>
/**
* This callback is issued once per minute, with totalUsage representing
* the memory usage of the process and notLeaked the amount of memory
* accounted for as active by -ecNotLeaked. All values are in bytes.
*/
- (void)process: (EcProcess*)process
didUseMemory: (uint64_t)totalUsage
notLeaked: (uint64_t)notLeaked;
@end

View file

@ -59,6 +59,7 @@
#import "EcHost.h"
#import "EcUserDefaults.h"
#import "EcBroadcastProxy.h"
#import "EcMemoryLogger.h"
#include "config.h"
@ -130,6 +131,7 @@ static NSString *cmdUser = nil;
static NSUserDefaults *cmdDefs = nil;
static NSString *cmdDebugName = nil;
static NSMutableDictionary *cmdLogMap = nil;
static id<EcMemoryLogger> cmdMemoryLogger = nil;
static NSDate *started = nil; /* Time object was created. */
static NSDate *memStats = nil; /* Time stats were started. */
@ -1055,9 +1057,52 @@ findMode(NSDictionary* d, NSString* s)
DESTROY(started);
DESTROY(userDir);
DESTROY(warningLogger);
DESTROY(cmdMemoryLogger);
}
}
- (Class)_memoryLoggerClassFromBundle: (NSString*)bundleName
{
NSString *path = nil;
Class c = Nil;
NSBundle *bundle = nil;
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSAllDomainsMask,
YES);
NSEnumerator *e = [paths objectEnumerator];
while (nil != (path = [e nextObject]))
{
path = [path stringByAppendingPathComponent: @"Bundles"];
path = [path stringByAppendingPathComponent: bundleName];
path = [path stringByAppendingPathExtension: @"bundle"];
bundle = [NSBundle bundleWithPath: path];
if (bundle != nil)
{
break;
}
}
if (nil == bundle)
{
[self cmdWarn: @"Could not load bundle '%@'", bundleName];
}
else if (Nil == (c = [bundle principalClass]))
{
[self cmdWarn: @"Could not load principal class from %@ at %@.",
bundleName, path];
}
else if (NO == [c conformsToProtocol: @protocol(EcMemoryLogger)])
{
[self cmdWarn:
@"%@ does not implement the EcMemoryLogger protocol",
NSStringFromClass(c)];
c = Nil;
}
return c;
}
+ (NSMutableDictionary*) ecInitialDefaults
{
NSProcessInfo *pi;
@ -4294,6 +4339,48 @@ With two parameters ('maximum' and a number),\n\
}
}
- (void)_ensureMemLogger
{
NSString *bundle = [cmdDefs stringForKey: @"MemoryLoggerBundle"];
Class cls = Nil;
if (nil == bundle)
{
DESTROY(cmdMemoryLogger);
return;
}
// This is a reasonable fast path if we have already loaded the bundle
cls = NSClassFromString(bundle);
if ((Nil == cls)
|| (NO == [cls conformsToProtocol: @protocol(EcMemoryLogger)]))
{
cls = [self _memoryLoggerClassFromBundle: bundle];
}
if (Nil == cls)
{
// No usable logger class, destroy any we might have
DESTROY(cmdMemoryLogger);
return;
}
if (NO == [cmdMemoryLogger isKindOfClass: cls])
{
// If it's no longer the right class, destroy it
DESTROY(cmdMemoryLogger);
}
if (nil == cmdMemoryLogger)
{
NS_DURING
{
cmdMemoryLogger = [cls new];
}
NS_HANDLER
{
[self cmdWarn: @"Exception creating memory logger: %@",
localException];
}
NS_ENDHANDLER
}
}
- (void) _memCheck
{
BOOL memDebug = [cmdDefs boolForKey: @"Memory"];
@ -4320,6 +4407,24 @@ With two parameters ('maximum' and a number),\n\
}
excLast = (uint64_t)[self ecNotLeaked];
[self _ensureMemLogger];
if (nil != cmdMemoryLogger)
{
NS_DURING
{
[cmdMemoryLogger process: self
didUseMemory: memLast
notLeaked: excLast];
}
NS_HANDLER
{
[self cmdWarn:
@"Exception logging memory usage to bundle: %@",
localException];
}
NS_ENDHANDLER
}
/* Do initial population so we can work immediately.
*/
if (0 == memSlot)

View file

@ -60,7 +60,7 @@ ECCL_HEADER_FILES = \
EcProcess.h \
EcTest.h \
EcUserDefaults.h \
EcMemoryLogger.h
TOOL_NAME = \
Command \