|
|
(2 intermediate revisions by one other user not shown) |
Line 1: |
Line 1: |
− | This page has some minor changes to VTK that have helped in Slicer3 debugging. It would make sense to improve these techniques and make them conditionally compilable in VTK so people can better understand their code.
| + | <big>'''Note:''' We are migrating this content to the slicer.org domain - <font color="orange">The newer page is [https://www.slicer.org/wiki/Slicer3:VTK_Observer_Debugging here]</font></big> |
− | | |
− | It would also be good if there were signatures for AddObserver that allowed additional information to be associated with the particular observer so that introspections of the vtkCommand instances could be more informative. Extra information could include the class name of or a pointer to the 'owner' of the observer and/or a text string comment added by the programmer.
| |
− | | |
− | See also [[Slicer3:VTK Leak Debugging]]
| |
− | | |
− | == Removing Observers ==
| |
− | | |
− | Below is a modified method for use in Common/vtkObject.cxx that removes observers before calling 'delete' on the instance.
| |
− | | |
− | //----------------------------------------------------------------------------
| |
− | void vtkObject::UnRegisterInternal(vtkObjectBase* o, int check)
| |
− | {
| |
− | // Print debugging messages.
| |
− | if(o)
| |
− | {
| |
− | vtkDebugMacro(<< "UnRegistered by "
| |
− | << o->GetClassName() << " (" << o << "), ReferenceCount = "
| |
− | << (this->ReferenceCount-1));
| |
− | }
| |
− | else
| |
− | {
| |
− | vtkDebugMacro(<< "UnRegistered by NULL, ReferenceCount = "
| |
− | << (this->ReferenceCount-1));
| |
− | }
| |
− | | |
− | if(this->ReferenceCount == 1)
| |
− | {
| |
− | // The reference count is 1, so the object is about to be deleted.
| |
− | // Invoke the delete event.
| |
− | this->InvokeEvent(vtkCommand::DeleteEvent, 0);
| |
− | | |
− | // Make sure we don't invoke any more events during destructor
| |
− | if ( this->SubjectHelper )
| |
− | {
| |
− | vtkObserver *elem = this->SubjectHelper->Start;
| |
− | vtkObserver *next;
| |
− | while (elem)
| |
− | {
| |
− | next = elem->Next;
| |
− | delete elem;
| |
− | elem = next;
| |
− | }
| |
− | this->SubjectHelper->Start = NULL;
| |
− | }
| |
− | }
| |
− | | |
− | // Decrement the reference count.
| |
− | this->Superclass::UnRegisterInternal(o, check);
| |
− | }
| |
− | | |
− | == Debugging Observers ==
| |
− | | |
− | The code below, also in Common/vtkObject.cxx adds debug printouts to help figure out which observers are being invoked in response to events. See [[Slicer3:VTK_Observer_Debugging:ExampleOutput|example output]] for this modification.
| |
− | | |
− | int vtkSubjectHelper::InvokeEvent(unsigned long event, void *callData,
| |
− | vtkObject *self)
| |
− | {
| |
− | #define INDENT(__indent) {for (int __ii = 0; __ii < (__indent); __ii++) {cerr << " ";}}
| |
− | | |
− | static int indent = 0;
| |
− | this->ListModified = 0;
| |
− |
| |
− | vtkObserver *elem = this->Start;
| |
− | int commandCount = 0;
| |
− | while (elem)
| |
− | {
| |
− | elem->Visited = 0;
| |
− | elem=elem->Next;
| |
− | commandCount++;
| |
− | }
| |
− |
| |
− | INDENT(indent);
| |
− | cerr << "{ "<< commandCount << " commands for " << self->GetClassName() << " event is " << vtkCommand::GetStringFromEventId(event) << "\n";
| |
− | indent++;
| |
− | | |
− | commandCount = 0;
| |
− | elem = this->Start;
| |
− | vtkObserver *next;
| |
− | while (elem)
| |
− | {
| |
− | // store the next pointer because elem could disappear due to Command
| |
− | commandCount++;
| |
− | next = elem->Next;
| |
− | if (!elem->Visited &&
| |
− | elem->Event == event || elem->Event == vtkCommand::AnyEvent)
| |
− | {
| |
− | elem->Visited = 1;
| |
− | vtkCommand* command = elem->Command;
| |
− | command->Register(command);
| |
− | command->SetAbortFlag(0);
| |
− | | |
− | INDENT(indent);
| |
− | cerr << "{ " << commandCount << " Invoking "<< vtkCommand::GetStringFromEventId(event)
| |
− | << " on " << self->GetClassName() << "\n";
| |
− | | |
− | elem->Command->Execute(self,event,callData);
| |
− | | |
− | INDENT(indent);
| |
− | cerr << "} " << commandCount << " Returned from "<< vtkCommand::GetStringFromEventId(event)
| |
− | << " on " << self->GetClassName() << "\n";
| |
− | | |
− | // if the command set the abort flag, then stop firing events
| |
− | // and return
| |
− | if(command->GetAbortFlag())
| |
− | {
| |
− | command->UnRegister();
| |
− | indent--;
| |
− | INDENT(indent);
| |
− | cerr << "} aborted " << self->GetClassName() << "\n";
| |
− | return 1;
| |
− | }
| |
− | command->UnRegister();
| |
− | }
| |
− | else
| |
− | {
| |
− | INDENT(indent);
| |
− | cerr << " -- " << commandCount << " Skipping "<< vtkCommand::GetStringFromEventId(elem->Event) << "\n";
| |
− | }
| |
− | if (this->ListModified)
| |
− | {
| |
− | elem = this->Start;
| |
− | this->ListModified = 0;
| |
− | }
| |
− | else
| |
− | {
| |
− | elem = next;
| |
− | }
| |
− | }
| |
− | | |
− | indent--;
| |
− | INDENT(indent);
| |
− | cerr << "} finished " << self->GetClassName() << "\n";
| |
− | | |
− | return 0;
| |
− | }
| |
− | | |
− | == Debug All ==
| |
− | | |
− | The following changes to Common/vtkSetGet.h turns on debugging for all objects by default. (Lots of output!). I usually redirect the output to a file and then grep for the class name, and then for the string 'egisterd' like
| |
− | grep /tmp/slicerout vtkSlicerApplication | grep egistered | less
| |
− | | |
− | | |
− | <nowiki>Index: Common/vtkSetGet.h
| |
− | ===================================================================
| |
− | RCS file: /cvsroot/VTK/VTK/Common/vtkSetGet.h,v
| |
− | retrieving revision 1.128.4.3
| |
− | diff -r1.128.4.3 vtkSetGet.h
| |
− | 616a617
| |
− | > ret->DebugOn(); \
| |
− | 619c620,622
| |
− | < return new thisClass; \ | |
− | ---
| |
− | > thisClass* retClass = new thisClass; \
| |
− | > retClass->DebugOn(); \
| |
− | > return (retClass); \
| |
− | 630c633,635
| |
− | < return thisClass::New(); \
| |
− | ---
| |
− | > thisClass* retClass = thisClass::New(); \
| |
− | > retClass->DebugOn(); \
| |
− | > return (retClass); \
| |
− | </nowiki>
| |
− | | |
− | The modified macros should look as follows:
| |
− | | |
− | <pre>
| |
− | // Macro to implement the standard form of the New() method.
| |
− | #define vtkStandardNewMacro(thisClass) \
| |
− | thisClass* thisClass::New() \
| |
− | { \
| |
− | vtkObject* ret = vtkObjectFactory::CreateInstance(#thisClass); \
| |
− | ret->DebugOn(); \
| |
− | if(ret) \
| |
− | { \
| |
− | return static_cast<thisClass*>(ret); \
| |
− | } \
| |
− | thisClass *retClass = new thisClass; \
| |
− | retClass->DebugOn(); \
| |
− | return (retClass); \
| |
− | } \
| |
− | vtkInstantiatorNewMacro(thisClass)
| |
− | | |
− | // Macro to implement the instantiator's wrapper around the New() | |
− | // method. Use this macro if and only if vtkStandardNewMacro is not
| |
− | // used by the class.
| |
− | #define vtkInstantiatorNewMacro(thisClass) \
| |
− | extern vtkObject* vtkInstantiator##thisClass##New(); \
| |
− | vtkObject* vtkInstantiator##thisClass##New() \
| |
− | { \
| |
− | thisClass *retClass = thisClass::New(); \
| |
− | retClass->DebugOn(); \
| |
− | return (retClass); \
| |
− | }
| |
− | </pre> | |