Difference between revisions of "Slicer3:VTK Leak Debugging"
m |
|||
Line 41: | Line 41: | ||
====Print additional object info upon object creation using breakpoints (Windows-only)==== | ====Print additional object info upon object creation using breakpoints (Windows-only)==== | ||
One approach that has worked for me is to modify the behavior of visual studio breakpoints - right clicking on a breakpoint and picking the 'When Hit..." option allow you to do things like print an expression or stack trace and continue running. This does slow down execution but can provide useful log data without modifying the code. | One approach that has worked for me is to modify the behavior of visual studio breakpoints - right clicking on a breakpoint and picking the 'When Hit..." option allow you to do things like print an expression or stack trace and continue running. This does slow down execution but can provide useful log data without modifying the code. | ||
+ | |||
+ | Note that if you are tracking leaks in a particular VTK class, you can implement dummy Register and UnRegister methods with an implementation that just calls the superclass. By placing a breakpoint in these methods and using the When Hit... option described above you can create a set of stack traces that let you see exactly why the reference count does not go back to zero. | ||
===Preventing memory leaks=== | ===Preventing memory leaks=== |
Latest revision as of 22:16, 14 November 2011
Home < Slicer3:VTK Leak DebuggingContents
In your code
Finding memory leaks
Enabling debug message output for leaked classes
If you are getting leaks for a vtkObject but can't figure out where it's coming from, you can try making a change to the constructor for that class and checking the debug output (warning, it can be verbose). What you will see from the following type of change is instances that are created but never destroyed. Then you can add other debug statements in your code and narrow down which instances aren't being deleted.
Add this at the beginning of the constructor:
this->DebugOn(); vtkDebugMacro("constructing");
and this in the destructor:
vtkDebugMacro("destructing");
For example, these changes helped me find an extra instance of vtkPoints
// Construct object with an initial data array of type float. vtkPoints::vtkPoints(int dataType) { this->DebugOn(); vtkDebugMacro("constructing"); this->Data = vtkFloatArray::New(); this->Data->Register(this); this->Data->Delete(); this->SetDataType(dataType); this->Data->SetNumberOfComponents(3); this->Bounds[0] = this->Bounds[2] = this->Bounds[4] = 0.0; this->Bounds[1] = this->Bounds[3] = this->Bounds[5] = 1.0; } vtkPoints::~vtkPoints() { vtkDebugMacro("destructing"); this->Data->UnRegister(this); }
Print additional object info upon object creation using breakpoints (Windows-only)
One approach that has worked for me is to modify the behavior of visual studio breakpoints - right clicking on a breakpoint and picking the 'When Hit..." option allow you to do things like print an expression or stack trace and continue running. This does slow down execution but can provide useful log data without modifying the code.
Note that if you are tracking leaks in a particular VTK class, you can implement dummy Register and UnRegister methods with an implementation that just calls the superclass. By placing a breakpoint in these methods and using the When Hit... option described above you can create a set of stack traces that let you see exactly why the reference count does not go back to zero.
Preventing memory leaks
There are a couple of useful tools:
vtkNew - See http://www.vtk.org/doc/nightly/html/classvtkNew.html#details vtkSmartPointer - See http://www.vtk.org/doc/nightly/html/classvtkSmartPointer.html#details and http://www.vtk.org/Wiki/VTK/Tutorials/SmartPointers Their cousin the "vtkWeakPointer" could also be useful. See http://www.vtk.org/doc/nightly/html/classvtkWeakPointer.html#details
Whenever possible, make use them. There are plenty of resources on-line talking about the reason motivating the use of SmartPointer. For example: http://ootips.org/yonat/4dev/smart-pointers.html#Why
For example, you could use them to hold the pointer returned by vtkMRMLScene::GetNodesByClass. Indeed, this function returns a NEW reference and it's the caller role to manage the lifecycle of the object. See https://github.com/Slicer/Slicer/blob/master/Libs/MRML/vtkMRMLScene.cxx#L1636
It is preferred to pass already created objects to methods for update rather than creating the object instance in a method and returning a pointer to that (this way it's easier to keep the responsibility of creating and destructing the object at the same place). For example: https://github.com/Slicer/Slicer/blob/master/Libs/MRML/vtkMRMLScene.cxx#L1608 (May be a method taking a vtkCollection as parameter could also be added in addition/instead of the one dealing std::vector. In this case, python wrapping, efficiency .. are also things to consider)
You could also use tools/widgets like "DebugLeaksView" [2]. See http://www.vtk.org/Wiki/DebugLeaksView
Tracking the leaks when they appear is the highly recommended approach. It's when you are implementing a feature that you have the best understanding of the code, things are "wired" in your brain. The more you wait, the more painful it could be ...
In VTK
Printing additional information on the console upon object creation/destruction
In VTK/Common/vtkObjectBase.cxx, modify the RegisterInternal and UnRegisterInternal methods as follows. The prints to cerr will provide a running list of all memory allocation activity. To make this even more useful, you may want to change places in your code (and VTK code) from o->Delete() to o->UnRegister(this).
//---------------------------------------------------------------------------- void vtkObjectBase::RegisterInternal(vtkObjectBase* o, int check) { std::cerr << this << " " << this->GetClassName() << " " << this->ReferenceCount << " Register by "; if ( o ) { std::cerr << o->GetClassName(); } else { std::cerr << "NULL"; } std::cerr << "\n"; // If a reference is available from the garbage collector, use it. // Otherwise create a new reference by incrementing the reference // count. if(!(check && vtkObjectBaseToGarbageCollectorFriendship::TakeReference(this))) { ++this->ReferenceCount; } } //---------------------------------------------------------------------------- void vtkObjectBase::UnRegisterInternal(vtkObjectBase* o, int check) { std::cerr << this << " " << this->GetClassName() << " " << this->ReferenceCount << " Unregister by " if ( o ) { std::cerr << o->GetClassName(); } else { std::cerr << "NULL"; } std::cerr << "\n"; // If the garbage collector accepts a reference, do not decrement // the count. if(check && this->ReferenceCount > 1 && vtkObjectBaseToGarbageCollectorFriendship::GiveReference(this)) { return; } // Decrement the reference count. if(--this->ReferenceCount <= 0) { // Count has gone to zero. Delete the object. #ifdef VTK_DEBUG_LEAKS vtkDebugLeaks::DestructClass(this->GetClassName()); #endif delete this; } else if(check) { // The garbage collector did not accept the reference, but the // object still exists and is participating in garbage collection. // This means either that delayed garbage collection is disabled // or the collector has decided it is time to do a check. vtkGarbageCollector::Collect(this); } }
Saving pointers to leaked objects
With a small VTK patch you can get the pointers to the actual leaked object instances. Then, using the pointers, you can print the contents, analyze the object with the debugger, etc. The idea is to simply add the object pointer to a std::set when in the vtkObject constructor, remove the pointer from the std::set in the destructor, and print the pointers in vtkDebugLeaks. The implementation requires minor modification of 3 files, for VTK-5.8.0 a patch/modified files are available at the NA-MIC sandbox: http://svn.na-mic.org/NAMICSandBox/trunk/SlicerDebug/DebugLeakAnalysis/VTK-5.8.0/