views:

171

answers:

5

I have a large framework consisting of many C++ classes. Is there a way using any tools at runtime, to trace all the C++ objects that are being constructed and currently exist?

For example, at a certain time t1, perhaps the application has objects A1, A2 and B3, but at time t2, it has A1, A4, C2 and so on?

This is a cross platform framework but I'm familiar with working in Linux, Solaris and (possibly) Mac OS X.

+10  A: 

You can inject code in the destructor and constructor of the objects that you want to count:

SomeObject::SomeObject() {
   ++globalSomeObjectCounter;
}

SomeObject::~SomeObject() {
   --globalSomeObjectCounter;
}

Don't forget to increase the counter in all constructors (copy constructors, etc.)

EDIT: In this situation one can use the curiously recurring template pattern:

template <typename T>
struct Counter
{
    Counter() {++counter;}
    virtual ~Counter() {--counter;}
    static int counter;
};
template <typename T> int Counter<T>::counter(0);

and then:

class SomeObject : public Counter<SomeObject> {
}

to automatically generate a counter for each class type.

Andreas Brinck
What if SomeObject is derived from SomeObjectParent? You will count 2 for SomeObject, no?
frankc
@user275455 Not as long as you don't increment `globalSomeObjectCounter` in `SomeObjectParent`'s constructor.
Andreas Brinck
But what if you construct a parent directly? There is nothing to guarantee it's abstract.
frankc
You'd have to know to take the (number of parent constructs) - (number of child constructs) to get the real number of parent instantiations.
Mark B
@user275455 Each class which you're interested in should have it's own counter. If you construct a `SomeObject` both `globalSomeObjectCounter` and `globalSomeObjectParentCounter` will increase with one, which is as it should be.
Andreas Brinck
Shouldn't the destructor be virtual as well? IIRC, it won't be run otherwise.
Sagekilla
@Sagekilla Thanks for the catch!
Andreas Brinck
I see, somehow I thought you had one global counter, not a counter per class. That means you need to know all the counter names in some other place to get the total sum of all objects.
frankc
Well, if you had to get the counter of all the objects that exist at one point in time you would just access Counter<Object_Type>::counter for each type.
Sagekilla
A: 

I have not used it myself by Massif might be the tool you are looking for. http://valgrind.org/info/tools.html

madmik3
According to the short description there, Massif only profiles the heap. Objects can also be instantiated on the stack.
Space_C0wb0y
A: 

Make a special base class equivalent to Java's Object and make every class derive from that. Then in that class put the global counter operations Andreas Brinck suggests in the constructor/destructor respectively. In addition to making sure a derived object is only counted as one object, it means you only need to instrument 1 constructor and 1 destructor. Though of course the downside is you need to slightly change the definition of every class...

frankc
This way there's no way of knowing how many objects of each class has been constructed, see my edited answer.
Andreas Brinck
That's true but I didn't think that was a requirement. My interpretation, right or wrong, was 1 count of all objects.
frankc
A: 

This is Solaris only, but if that's an option, you can use dtrace to track the number of constructor and destructor calls for each of your classes and print it out at an interval. This will require an amount of work to set up all the entry/return blocks, but I suspect the dtrace script could be auto-generated.

Mark B
+1  A: 

I assume that you count only objects on the heap. If this is the case, you can log all calls to new and delete by replacing them with some custom macros.

We have developed such a custom logging facility. The primary purpose was to track memory leaks, but it can also be used to find out how many objects at any specific time. For example,

#define MY_NEW_OBJECT(a, T) \
    a = new T;              \
    MY_LOGGING((LM_DEBUG, "[NEW OBJ ] 0x%08X(%s), %4d bytes. %-20s - %-40s - %4d\n", a, #T,  \
    sizeof(T), __FILE__, __func__, __LINE__));

MyClass* myObj;
MY_NEW_OBJECT(myObj, MyClass);

The MY_LOGGING adds timestamp automatically at the beginning of each line. The line has the class name, filename, line number, function name, and the size.

A utility parses the logging file and generates graphs showing the number of objects, total size utilized etc at any time.

Of course, you have to replace every new/delete calls with the macros. Which might be quite a bit work.

Sherwood Hu
This only counts dynamically allocated objects. What about local variables, members etc...
Space_C0wb0y