There has been substantial interest in the difficult task of finding and fixing leaks in EOF, OpenStep and WOF applications. As the EOF team was preparing for the final release of WebObjects 3.5, they wrote a small test app to help them find leaks in the framework. This example will help you learn how to write an application that should be able to run indefinitely with running out of memory. Also, if you think you have encountered a serious leak in the framework, you can use this app as a minimal test case for illuminating the problem.
The app takes a few input arguments:
-batchsize m [number, default 1]
-runs n [number, default 1]
-trials t [number, default 1]
-hang yn [YES/NO, default NO]
For each trial, at runtime the app will, using the Movies EOmodel:
- Empty the tables
- Insert movies (insert m * n movies)
- Add directors and actors (m*n objects fetched, m*n to-one
faults fired, m*n to-many faults fired, m*n+m^2*n objects inserted)
- Modify objects (m*n objects fetched, m*n to-one faults fired, m*n
to-many faults fired, m*n+m^2*n objects faulted in, m*n objects
deleted, m*n objects inserted, m*n+m^2*n objects mutated)
The -hang option makes the app pause before quitting. This allows you to use pview or ps after the app finishes, rather than monitoring the app during its entire run.
The following results were obtained on Windows NT, using pview.exe to examine the image at the end of the run. This app has only been tested using EOF 2.2, the EOF version that ships with WebObjects 3.5. You might notice that the results aren't quite as deterministic as one might hope, but this is the raw untouched data we got.
void usage(void)
{
NSLog(@"Usage: LeakTester -batchsize <batchsize> -runs <number of
runs> -trials <number of trials> -hang <BOOL>
WARNING!!!! THIS PROGRAM
REMOVES ALL EXISTING DATA IN THE TABLES USED");
}
void logReport(NSString *modelPath, int batchsize, int runs, int trials)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
EOModel *model;
model = [[[EOModel alloc] initWithContentsOfFile:modelPath]
autorelease];
expression = [[adaptor expressionClass] expressionForString:@"delete
from LKMOVIE"];
[channel evaluateExpression:expression];
expression = [[adaptor expressionClass] expressionForString:@"delete
from LKACTOR"];
[channel evaluateExpression:expression];
expression = [[adaptor expressionClass] expressionForString:@"delete
from LKDIRECTOR"];
[channel evaluateExpression:expression];
// Informix complains if we do a COMMIT here; OK for Sybase and
Oracle.
// expression = [[adaptor expressionClass]
expressionForString:@"commit"];
// [channel evaluateExpression:expression];
[channel closeChannel];
[pool release];
}
void doit(NSString *modelPath, int batchsize, int runs)
{
NSAutoreleasePool *pool, *pool0, *pool1, *pool2;
EOEditingContext *editingContext = [EOEditingContext new]; // context
with defaultCoordinator
Movie *movie;
Movie *lastMovie;
NSArray *movies;
NSArray *actors;
NSArray *lastActors;
Actor *actor;
Director *director;
int i;
int j;
int priKey;
int m;
// For apps with models stored in the main bundle, the following is
// unnecessary, since the defaultGroup will be automatically built
from
// the contents of the main bundle and and any referenced frameworks.
pool = [[NSAutoreleasePool alloc] init];
[EOModelGroup setDefaultGroup:[[EOModelGroup new] autorelease]];
[[EOModelGroup defaultGroup] addModelWithFile:modelPath];