New feature: getting a stack trace on leaked objects with VTK_DEBUG_LEAKS

I just merged a new feature to VTK that augments vtkDebugLeaks. This feature lets you record a stack trace where leaked VTK objects were allocated. This can be a very useful when debugging VTK object leaks.

Previously, if you configured VTK with VTK_DEBUG_LEAKS set to ON, VTK would report the types of VTK objects that were never deleted and how many there were when the application exited. This was a useful feature to let you know there was a leak, but it didn’t provide much insight into where the leaked objects were coming from. Here’s an example:

Class "vtkDoubleArray" has 1 instance still around.
Class "vtkImageData" has 1 instance still around.

With the new feature, you can define an environment variable named VTK_DEBUG_LEAKS_TRACE_CLASSES that contains a comma-separated list of VTK classes for which you would like vtkDebugLeaks to record a stack trace when they are allocated. For example, if my program reports that objects of type vtkDoubleArray and vtkImageData are leaking, set this environment variable like so:

export VTK_DEBUG_LEAKS_TRACE_CLASSES=vtkDoubleArray,vtkImageData

Now, when the program exits, vtkDebugLeaks will report where each leaked object specified in the environment variable was allocated, helping you identify how to rectify the leaks. Here is an example of what the reporting looks like on macOS

Class "vtkDoubleArray" has 1 instance still around.
Class "vtkImageData" has 1 instance still around.

Remaining instance of object 'vtkDoubleArray' was allocated at:
0x11aca3d23 : vtkDoubleArray::New() [(/Users/cory.quammen/bin/vtk-debug/lib/libvtkCommonCore-8.90.1.dylib) ???:-1]
0x10c4c5c96 : TestPlaybackWidget(int, char**) [(/Users/cory.quammen/bin/vtk-debug/bin/vtkInteractionWidgetsCxxTests) ???:-1]
0x10c41aef5 : main [(/Users/cory.quammen/bin/vtk-debug/bin/vtkInteractionWidgetsCxxTests) ???:-1]
0x7fff8f5385ad : start [(/usr/lib/system/libdyld.dylib) ???:-1]

Remaining instance of object 'vtkImageData' was allocated at:
0x118affcd3 : vtkImageData::New() [(/Users/cory.quammen/bin/vtk-debug/lib/libvtkCommonDataModel-8.90.1.dylib) ???:-1]
0x10c4c5c8a : TestPlaybackWidget(int, char**) [(/Users/cory.quammen/bin/vtk-debug/bin/vtkInteractionWidgetsCxxTests) ???:-1]
0x10c41aef5 : main [(/Users/cory.quammen/bin/vtk-debug/bin/vtkInteractionWidgetsCxxTests) ???:-1]
0x7fff8f5385ad : start [(/usr/lib/system/libdyld.dylib) ???:-1]
9 Likes

Thank you, this is extremely useful. Does it work an all platforms?

It relies on kwsys’s SystemInformation::GetProgramStack(int, int) function, which looks like it does not provide a stack trace on Windows systems. The code to provide a stack trace on Windows exists in a couple places in VTK, including vtkWindowsTestUltities.h and vtkOpenGLState.cxx. That should be promoted to kwsys. I may do that if I have time, but if someone beats me to it, that would be fine, too.

2 Likes

Thanks, this looks like a great feature @cory.quammen!

that is great !

Would it be possible to get the line numbers somehow ?

It is possible it seems, but not particularly straightforward:

To follow up, @ken-martin has added Windows stack trace support to kwsys, made available in VTK master as of a short while ago. So now this feature is available for use on all major platforms.

2 Likes

Finally had a need to use this, it works great and saved me a ton of debugging time! I agree with @mwestphal, adding line numbers would surely be a cherry on the top!

3 Likes

This is sadly not compatible with vtkCommand and other classes that uses vtkDebugLeaks::ConstructClass(const char* className) instead of vtkDebugLeaks::ConstructClass(vtkObjectBase* object)

Well it’s not all that sad :slight_smile:

I’m not sure why vtkCommand and subclasses use that other version of vtkDebugLeaks::ConstructClass. It should work just as well to use vtkDebugLeaks::ConstructClass(vtkObjectBase* object).

I suppose it’s the same reason that IsA() uses a class name rather than a class type (typeid).

In fact, looking at my commits again, all classes were using the vtkDebugLeaks::ConstructClass(const char* name) function prior to this feature. It seems I just overlooked vtkCommand and descendents. Patch coming.

Now I remember why vtkCommand is not treated the same way as other VTK objects. vtkCommand and descendants do not follow the same discipline as most other VTK classes. Specifically, they do not use vtkStandardNewMacro nor vtkTypeMacro. The first defines the static New() member function that fully constructs a VTK object and then registers it with the leak manager. If the object is not fully constructed when GetClassName() is called, then GetClassName() returns the name of the superclass (I think). The second macro makes the VTK class aware of its name. Even if the name is known, however, it is not available at the time the object is registered with the leak manager. We cannot register the actual vtkCommand objects based on their names because of this. If you try to register vtkCommands with vtkDebugLeaks::ConstructClass(vtkObjectBase* object), you end up with incorrect reports that certain object types are not known.

If vtkCommand and subclasses were cleaned up in VTK, that would help a lot. However, there are bound to be a lot of applications that define custom subclasses of vtkCommand that won’t be updated and could lead to spurious warning messages from vtkDebugLeaks.

For now, I will not make the stack tracing mechanism work with vtkCommand and children.

1 Like

Indeed !

Also, I made a quick modification ov vtkDebugLeaks that changes ConstructClass(const char* name) into ConstructClass(const char* name, vtkObject* key) and store them into a std::map<std::pair<name, key> > in order to be able to track them the same way you track other classes.

Well, ParaView took 30 minutes to just open, I was not able to test what I was trying to test and gave up.

I’m going to need to find another way to track this leak.

@cory.quammen: Do you think adding a env var to call DebugOn() on instances of specific classes would make sense ?

I think putting this behind the VTK_DEBUG_LEAKS feature, while not necessary, is a good way to make sure we do not hinder anyone performance.

Something like VTK_DEBUG_LEAKS_DEBUG_ON_CLASSES=vtkDoubleArray,vtkImageData

Hmm, hiding it behind VTK_DEBUG_LEAKS might remove some utility since you couldn’t debug applications, say release builds, that don’t have VTK_DEBUG_LEAKS enabled. I wonder if it might be better to use the vtkLogger mechanism in conjunction with an environment variable to enable debug messages for certain classes? @utkarshayachit any thoughts on that?

Is using Valgrind not suitable here? It provides a stack trace.

Not sure to see how it is related to my suggestion.

In any case, valgrind do not catch vtk leaks has the memory is released when closing.