1. adrian
  2. oc

Source

oc /

Filename Size Date modified Message
4.6 KB
5.6 KB

C++ Object Counter and Leak Detector

Introduction

It is straightforward nowadays to write C++ code that is “free” of memory leaks without having to give up the use of pointers, by using smart pointer classes such as std::auto_ptr or any of the boost family of pointers such as boost::shared_ptr.

However, complex software, or legacy code may still contain memory allocation bugs, which are traditionally quite hard to detect and fix. Memory debugging libraries or programs can be used to debug such issues, but they usually provide low level information which needs to be further interpreted in the context of the application to be useful.

The utility classes presented here can be used to do object level allocation tracking, thus offering a higher level view of the object creation and destruction behavior. They require minimal code changes and have very little memory usage or performance impact. Furthermore, they are disabled in release mode.

Detecting object leaks

Here is a typical usage example:

Without object counter:

class C
{
                // members
};

With object counter:

class C
{
                OBJ_COUNTER( C )

                // members
};

Once added to a class, the object counter will identify each new instance of that class by an integer value, or id (unique for all objects of a class) that will be stored in a per class static container for tracking. When an instance is destructed, the corresponding id will be removed from the container.

If all instances of a class have been destructed upon application exit, a success message is logged.

If there are still objects that haven’t been destructed, an object leak message is logged, indicating the class name and some extra information about the current status: the number of instances still in existence, the maximum number of instances at any one time, as well as a list of ids of leaked objects.

An assert is triggered if the application attempts to destruct an object with an id that is not found, usually indicating that an object is destructed multiple times.

Debugging object leaks

The object counter provides code to help with debugging object creation/destruction issues as well.

If object leaks have been detected it is useful to run the application several times and determine if there is a pattern in the number and indexes of the leaked objects.

If an object with the same id between application run is leaked, a second object counter macro can be used to break when this specific instance is created. For example, if the leaked object id is 5, the usage is as follows:

class C
{
                OBJ_COUNTER_BREAK_ON_INDEX( C , 5 )
                // members
};

When the class C instance index 5 is created, the execution breaks into the debugger by using the __asm int 3; (or DebugBreak() ) statement. It is then possible to examine the context, call stack etc to find the reason for the leak.

Character type

The object counter classes are character type neutral, meaning they work as well with ASCII, UTF8 or UTF16 (Unicode) character types, by using the character macros (_T, TCHAR) defined in the compiler header files.

Instead of using std::string or std::wstring directly, we defined a template based string type that takes the current character type TCHAR as argument.

Multi-threaded execution

The current implementation works well in a single-threaded environment (the application can be multithreaded, but the objects themselves should be created/destructed within the same thread). If the objects being tracked are created/destructed from multiple threads the object counter classes will require thread synchronization to work properly.

The object counter classes make use of a placeholder Lockable class, which in a real multi-threaded application will have to implement the synchronization methods.

Static instances

The object counter utility classes rely on the fact that static variables are destructed when the process exits, which ensures the object counting works well with heap (allocated using new) and local variables. If however the instances being tracked are themselves static, the object counter utility may not work anymore, as C++ doesn’t guarantee the order in which static variables are allocated or destructed. It is therefore possible that the tracking static variable be destructed before the tracked instances, thus detecting false leaks.

String << operator

The STL std::ostringstream class is very useful when assembling strings from variables of different types, without having to do any explicit conversions, by using the << operator. Using this class however requires several extra steps, for example:

      int n = 10;
      ostringstream_t os;
      os << _T( "variable int n is " ) << n;
      OutputDebugString( os.str().c_str() );

We have defined a template based << operator that is applied to directly to strings and has the same effect as the std::ostringstream << operator. This makes it possible to create strings in place from values of various types, thus making the code and clearer (for those who like compact code). The same example using the string_t << operator:

      int n = 10;
      OutputDebugString( (string_t() << _T( "variable int n is " ) << n).c_str() );

We are using this where logging strings are created from string and integer variables.