Swarm
is a Java/Objective-C framework for agent based modelling (ABM).
logzone is a set of Obj-C classes to facilitate monitoring of
Zone
s in Swarm applications and performing operations necessary
for leak detection. While a C leak detector library like `efence'
contains hooks for the free
/malloc
functions, logzone,
from this perspective, is a hook for Zone
, the main memory building
block in Obj-C Swarm models.
Logzone is available from the swarm.org archive or from the author's website.
The package comes as a gzipped tar archive file.
tar xvfz
command on
the package. A subdirectory named `logzone-version'
should be filled.
cd
into the created directory.
make
, the file `liblogzone.a'
should be built.
make install
. The library and header files are
copied into your Swarm installation directory.
-llogzone
into your `Makefile' APPLIBS
variable,
e.g. APPLIBS=-llogzone
One of the many delights of programming is the moment that you notice your running program takes more and more memory you cannot account for. Garbage collection works around this problem but this option is (at this time) not available for Objective-C Swarm modeling. This is where leak detectors have proved helpful.
Logzone comes into play when you suspect that in your Swarm model allocated objects are not freed -- for example because you notice your Obj-C program eats more and more memory.
First, you import the included logzone header file:
#import <logzone.h>
We assume now you have designed your model memory usage by giving
globalZone
, or one or several freshly made Zones to your
classes' creation routines; this would be standard Swarm policy.
Then you can monitor your classes of interest by substituting the
relevant Zone with a LoggingZone by giving it as argument to
create
instead of your usual Zone.
The common situation would be that you have a
ModelSwarm
or BatchSwarm
that contains all your classes,
at least indirectly: then just create it with a LoggingZone.
Here is a schema of the process:
+-------------------+ +-----------+ +---------------+ |SwarmObject::create|----->|LoggingZone|====| KISSMemLogger |--> GUI |SwarmObject::drop |.... +-----------+ +---------------+ +-------------------+ . | +----------------+ | | . | | ASCIIMemLogger |-> File +-------------------+ . +--v---+ +----------------+ | AnObject::create | .....>| Zone | +------------+ | AnObject::drop | +------+ | RMemLogger |-----> R +-------------------+ +------------+
Your objects invisibly send the create message to their superclass
until the SwarmObject
superclass gets it and invokes the Zone
functions. The LoggingZone sends all requests to the Zone
class but does bookkeeping on objects and classes. This information
can then be output in several ways.
The implemented bookkeeping mechanisms are quite simple and depend on the MemLogConsumer your LoggingZone is bound to. The default, the KISSMemLogger, is a GUI window showing the classes that take the most memory in the Zone at the top (KISS stands for `Keep It Simple stupid').
If, armed with this info, you still cannot see any objects responsible for the
leak, then the other possibility is that the leaks are created within
globalZone
or scratchZone
-- especially the latter ones,
e.g. temporary indices, are frequently overlooked.
There is one caveat with respect to scratchZone: this case can only be
safely handled if
you use an ASCIIMemLogger with your LoggingZone, see
section MemLogConsumers: Available Output Formats. The section section Examples shows how to do it.
There is no limit on the number of LoggingZones. I used logzone to debug itself, for example, by giving a second LoggingZone to a first LoggingZone as creation argument; the first would be monitoring a model. I would then get two windows with my happily running model -- one monitoring the model, the other monitoring the monitor!
Each LoggingZone refers to a protocol named MemLogConsumer for sending its output to, and you can substitute your own classes provided they conform to this protocol (protocols are for Obj-C what interfaces are for Java). The logzone library provides you with several ready-made MemLogConsumers, and the usage of them all is treated in section Examples.
# | Type | Description | Comments |
1 | number | step no. | -1 if not in activity |
2 | string | alloc/free | '+' for alloc, '-' for free |
3 | string | class name | |
4 | hex | mem location | |
5 | number | object size | in bytes |
6 | number | no. of objects | always 1 if not cumulative |
Each of the examples assumes the header file is included, and both
logzone
and logger
are temporary ids. We always use
an ObserverSwarm to monitor.
#import <logzone.h> ... id logzone, logger;
The default behaviour is to give you sorted cumulative info in a graphics window.
logzone = [LoggingZone create: globalZone]; observerSwarm = [AquariumObserverSwarm create: logzone];
You can also send alloc info (non-cumulative) to a file/`stdout'.
logzone = [LoggingZone createBegin: globalZone]; [logzone setAsciiFilename: 0]; // to stdout logzone = [logzone createEnd]; observerSwarm = [AquariumObserverSwarm create: logzone];
It is also possible to set a refined logger, e.g. log cumulative info separated by commata to a file named `testlog'
logzone = [LoggingZone createBegin: globalZone]; logger = [AsciiMemLogger createBegin: globalZone]; [logger setCumulative]; [logger setFilename: "testlog"]; [logger setRecordDelim: "\n" dataDelim: ","]; logger = [logger createEnd]; [logzone setMemLogConsumer: logger]; logzone = [logzone createEnd]; observerSwarm = [AquariumObserverSwarm create: logzone];
One can even log scratchZone/globalZone by replacing it with a LoggingZone.
logzone = [LoggingZone createBegin: globalZone]; [logzone setAsciiFilename: 0]; // to stdout // WARNING: using a different logger may result in infinite loops. scratchZone = [logzone createEnd];
The RMemLogger can be used to log single (de)allocation events or per step.
logzone = [LoggingZone createBegin: globalZone]; logger = [RMemLogger createBegin: globalZone]; [logger setCumulative]; [logger setFilename: "rlog"]; logger = [logger createEnd]; [logzone setMemLogConsumer: logger]; logzone = [logzone createEnd]; observerSwarm = [AquariumObserverSwarm create: logzone];
The resulting file `rlog' could then be read from within R with something like:
frame <- read.table("rlog",header=TRUE)
Logging in other formats (contributions!) or more graphically sophisticated would be nice to have.
KISSMemLogger
's Bookkeeping
In this section we try to justify some internal design choices.
Specifically, we want to address the points made by Marcus G. Daniels
on one of the Swarm mailing lists on the efficency of the combination of
algorithms used in the KISSMemLogger
class. The conclusion
to be drawn is that there are no complexity reasons for chosing an
algorithm based on AVL trees.
To summarize the working of KISSMemLogger
, a hashtable holds
the current number of existing objects of a class, and this information
is sorted and printed on the screen every step. Now MGD points out
that it could be faster to use an AVL tree as container since sorting then
isn't necessary anymore. Let's look at the potential savings more closely.
Let M be the average number of allocations plus deallocations during a timestep, and N the number of classes to be shown in the output of the GUI window (this can be reduced by removing entries of classes that have currently no objects allocated). Let f(M,N) be the time complexity of the current bookkeeping algorithm, and g(M,N) the one for the algorithm using AVL trees. Then f(M,N)=O(N log N)+M*O(1) whereas g(M,N)=M*O(log N).
Clearly, both functions have the same complexity. One can even say, although general Swarm usage certainly spans all situations including M<<N and M>>N, the novice and intermediate user has models where M is about or greater than N, and only comparisons of specific implementations can bring more light into the question of efficiency. We would expect time savings from AVL trees only when M<<N.
awk
Here is an example of an awk
script to cumulate alloc info from
the (default non-cumulative) ASCII output.
BEGIN { arr[0]=0 } { if ( $3 in arr ) { if ( $2 ~ /\+/ ) arr[$3] = arr[$3]+1 else arr[$3] = arr[$3]-1 } else { if ( $2 ~ /\+/ ) arr[$3] = 1 } } END { for ( i in arr ) print i,arr[i] }
Please send patches to Ralf Stephan. Thanks for your contribution.
The programs currently being distributed that relate to logzone consist of the included source code. These programs are free; this means that everyone is free to use them and free to redistribute them on a free basis furtherly detailed.
The logzone-related programs are not in the public domain; they are copyrighted and there are restrictions on their distribution, but these restrictions are designed to permit everything that a good cooperating citizen would want to do. What is not allowed is to try to prevent others from further sharing any version of these programs that they might get from you.
Specifically, we want to make sure that you have the right to give away copies of the programs that relate to logzone, that you receive source code or else can get it if you want it, that you can change these programs or use pieces of them in new free programs, and that you know you can do these things.
To make sure that everyone has such rights, we have to forbid you to deprive anyone else of these rights. For example, if you distribute copies of the Texinfo related programs, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. Also, for our own protection, we must make certain that everyone finds out that there is no warranty for the programs that relate to logzone. If these programs are modified by someone else and passed on, we want their recipients to know that what they have is not what we distributed, so that any problems introduced by others will not reflect on our reputation.
The precise conditions of the licenses for the programs currently being distributed that relate to logzone are found in the General Public Licenses that accompany them.
This document was generated on 10 August 2000 using texi2html 1.56k.