next up previous contents
Next: Dependency Up: The MINOS Off-line Software Previous: Ntuples   Contents

Subsections


Leak Checker

Last significant change: 2001/09/17

Introduction

OO programs are dynamic; the objects they consist of are created and destroyed throughout the course of program execution. Objects require memory to hold their data members. The memory for objects created at execution time comes from the stack or the heap. When objects are destroyed it is essential that the memory that they occupied is recovered. This is automatic for stack based objects, but is left to the application code for heap based objects. Failing to destroy objects correctly by using the delete operator results in memory clogged with dead objects, a phenomenon referred to as a memory leak

The LeakChecker package is a very simple leak checker facility developed as part of the framework to provide the first level of defense against memory leaks. It can be used to allow basic leak checking to be built into package validation.

LeakChecker is not a substitute for a full function leak checker which should still be applied to complete programs.

To use the checker, probes (C++ macro calls) are added to the code at each object creation and destruction. By default these macros generate nothing and so can safely be a permanent addition to the code. Activation is controlled by a preprocessor option which then generates calls to record object creation and destruction with a LeaLeakChecker object. This object can be used by validation code to query the number of objects created and the number currently active. The information can be supplied globally or for a specific class.

Inserting Leak Checker probes into code

The best place to insert the probes are into the class constructors and destructors, but this is not possible for external code such as ROOT. In this case the probes have to be added where the new and delete operators are used on them.

Adding probes for MINOS classes

The leak checker macros are defined in:-

#include "LeakChecker/Lea.h"

The macro LEA_CTOR should be inserted into every constructor and LEA_DTOR into the destructor. These macros deduce the class name from the file name so file names must follow the standard naming convention i.e. the class name followed by .cxx or .h, See below if this is not the case e.g. defining multiple classes in a single file.

It is essential that the compiler not be allowed to generate default and copy constructors as calls to these will not be recorded.

The code for a dummy class supplying both default and copy constructors would look like this:-

#include "LeakChecker/Lea.h"

class MyClass {
public:
  MyClass() { LEA_CTOR; }
  MyClass(const MyClass& that) { LEA_CTOR; *this = that }
 ~MyClass() { LEA_DTOR;  }
...

}

If you don't want a copy constructor then the trick is simply to declare, but not define, a private one e.g:-

private:
  MyClass(const MyClass& that);
}

being private nobody else can call it so you don't have to define it.

If your class is not defined in a file with the standard naming convention then you will have to use extended forms LEA_CTOR_NM and LEA_DTOR_NM (where NM = named). These macros take two arguments: the name of the class and the address of the object. Using these macros the above example becomes:-

#include "LeakChecker/Lea.h"

class MyClass {
public:
  MyClass() { LEA_CTOR_NM("MyClass",this); }
  MyClass(const MyClass& that)
            { LEA_CTOR_NM("MyClass",this); *this = that }
 ~MyClass() { LEA_DTOR_NM("MyClass",this);  }
...

}

Adding probes for foreign classes

The extended macro forms LEA_CTOR_NM and LEA_DTOR_NM can be used with each new and delete operation to record construction and destruction of objects for classes where the modification of the source is not an option. Of course you should not do this for MINOS class objects that have probes built into their constructors and destructors, although if you do, this only means double counting; it won't mask a leak or spuriously indicate one.

Here is sample code recording object creation and destruction:-

#include "LeakChecker/Lea.h"

  TheirClass* myObj = new TheirClass(...);
  LEA_CTOR_NM("TheirClass",myObj); 

  ...

  delete myObj;
  LEA_DTOR_NM("TheirClass",myObj); 
  myObj = 0;

This records heap objects. In principle the same technique could also be used for stack based objects:-

#include "LeakChecker/Lea.h"

  {
    TheirClass myObj(...);
    LEA_CTOR_NM("TheirClass",&myObj); 

    ...

    LEA_DTOR_NM("TheirClass",&myObj); 
  }

The DTOR call has to be placed at the point where the object goes out of scope. Not only would this be easy to overlook but will fail if control does not fall through to the end of the compound statement. Further, stack based objects are not a source of leaks and all that is lost by not recording them is that they will not be included in the total creation statistics. For this reason you are advised:-

Do not use the macros to record creation and destruction of stack objects.

Activating the probes

As explained in the introduction, by default the macro probes are disabled; producing no code at all. Consequently they can be permanent additions to the source. To activate them the code has to be compiled with the preprocessor option -DLEAK_CHECKER. This option can be selected when using the standard Makefiles by defining the environmental variable ENV_CXXFLAGS. The following shows how to activate the probes in the MyPackage package:-

cd MyPackage
setenv ENV_CXXFLAGS  -DLEAK_CHECKER
gmake clean lib
unsetenv ENV_CXXFLAGS

if not using csh, substitute the equivalent commands for setenv and unsetenv.

In this way leak checking can be activated package by package, or even class by class. Note however, probes used for foreign classes can give spurious results if not all new and delete probes are activated. If an activated package gives objects to another unactivated package to delete this will appear as a memory leak. Conversely the inverse will appear as a negative leak. These problems can be avoided by using the leak checker as part of a validation suite as described in the next section.

Accessing Leak Checker statistics

Activating the probes as described above causes statistics to be collected, class by class on object creation and destruction. To access this information you require the services of the LeaLeakChecker object which can be accessed using it static Instance() method:-

#include "LeakChecker/LeaLeakChecker.h"

...

LeaLeakChecker* lea = LeaLeakChecker::Instance();

LeaLeakChecker has methods to count the total number of objects created and the number currently active ( = created - destroyed):-

 UInt_t GetNumCreated(const Char_t* name = 0) const;
 UInt_t GetNumActive(const Char_t* name = 0) const;

they take as their argument the name of the required class. If omitted, the sum for all classes is returned. You can reset the counts for any class using:-

 void Reset(const Char_t* name = 0);

again, omitting the argument means all classes.

A simple summary of the current state of the leak checker system can be sent to the MessageService as follows:-

 MSG("Nav",Msg::kInfo) << lea;

The leak checker is primarily a first line of defense, to be incorporated into stand-alone package validation. Suppose you have an XxxPackage which has a validation suite contained in the XxxValidate class and executed using the method:-

Bool_t RunAllTests();

A test function to run it and also perform leak checking could be as follows:-

Bool_t TestXxx() {


//  Perform full validation with leak checking and 
//  return kTRUE if successful.
  
#include "LeakChecker/LeaLeakChecker.h"
#include "MessageService/MsgService.h"
#include "XxxPackage/XxxValidate.h"

  Bool_t  ok = kTRUE;

//  Clear leak checker.
  LeaLeakChecker* lea = LeaLeakChecker::Instance();
  lea.Reset();

//  Run validation suite within an inner scope so that
//  all stack based objects are destroyed before checking
//  for leaks.

  {
    XxxValidate v;
    ok = v.RunAllTests();
  }

//  See if leak checker is enabled by checking total created.

  if ( lea->GetNumCreated() == 0 ) {
    MSG("Xxx",Msg::kInfo) << "Warning: Leak checking disabled!!"
			  << endl;
  }

//  If it has make sure that there are no objects left.

  else if ( lea->GetNumActive() ) {
    MSG("Xxx",Msg::kInfo) << "Error, leaks detected! " << lea;
    ok = kFALSE;
  }

  return ok;
}

A final word of warning

The leak checker describe here is not a substitute for a full function leak checker such as Purify for a number of reasons:-

Despite all of the above, the system should prove useful for any package that can be tested stand-alone. Adding the probes is not particularly onerous and then the package developer can use the system to check for leaks throughout package development on her/his own machine. In this way some of the basic leaks can be eliminated before moving onto integrated system testing using a more powerful checker.


next up previous contents
Next: Dependency Up: The MINOS Off-line Software Previous: Ntuples   Contents
MINOS Software 2017-11-20