Commit 8f4d8afc authored by Pat Marion's avatar Pat Marion Committed by Code Review
Browse files

Merge topic 'qt-debug-leaks-view' into master

9182f039 Add Qt widget for displaying objects tracked by vtkDebugLeaks
75ca1bd2 Add new api to vtkDebugLeaks to track vtk object lifetime
parents ce38ab1d 9182f039
......@@ -305,6 +305,36 @@ void vtkDebugLeaks::DestructClass(const char*)
}
#endif
//----------------------------------------------------------------------------
void vtkDebugLeaks::SetDebugLeaksObserver(vtkDebugLeaksObserver* observer)
{
vtkDebugLeaks::Observer = observer;
}
//----------------------------------------------------------------------------
vtkDebugLeaksObserver* vtkDebugLeaks::GetDebugLeaksObserver()
{
return vtkDebugLeaks::Observer;
}
//----------------------------------------------------------------------------
void vtkDebugLeaks::ConstructingObject(vtkObjectBase* object)
{
if (vtkDebugLeaks::Observer)
{
vtkDebugLeaks::Observer->ConstructingObject(object);
}
}
//----------------------------------------------------------------------------
void vtkDebugLeaks::DestructingObject(vtkObjectBase* object)
{
if (vtkDebugLeaks::Observer)
{
vtkDebugLeaks::Observer->DestructingObject(object);
}
}
//----------------------------------------------------------------------------
int vtkDebugLeaks::PrintCurrentLeaks()
{
......@@ -413,10 +443,12 @@ void vtkDebugLeaks::ClassInitialize()
// Default to error when leaks occur while running tests.
vtkDebugLeaks::ExitError = 1;
vtkDebugLeaks::Observer = 0;
#else
vtkDebugLeaks::MemoryTable = 0;
vtkDebugLeaks::CriticalSection = 0;
vtkDebugLeaks::ExitError = 0;
vtkDebugLeaks::Observer = 0;
#endif
}
......@@ -458,3 +490,5 @@ vtkSimpleCriticalSection* vtkDebugLeaks::CriticalSection;
// Purposely not initialized. ClassInitialize will handle it.
int vtkDebugLeaks::ExitError;
vtkDebugLeaksObserver* vtkDebugLeaks::Observer;
......@@ -33,6 +33,7 @@
class vtkDebugLeaksHashTable;
class vtkSimpleCriticalSection;
class vtkDebugLeaksObserver;
class VTK_COMMON_EXPORT vtkDebugLeaks : public vtkObject
{
......@@ -64,6 +65,10 @@ public:
// Default is on when VTK_DEBUG_LEAKS is on and off otherwise.
static int GetExitError();
static void SetExitError(int);
//BTX
static void SetDebugLeaksObserver(vtkDebugLeaksObserver* observer);
static vtkDebugLeaksObserver* GetDebugLeaksObserver();
//ETX
protected:
vtkDebugLeaks(){};
......@@ -74,17 +79,34 @@ protected:
static void ClassInitialize();
static void ClassFinalize();
static void ConstructingObject(vtkObjectBase* object);
static void DestructingObject(vtkObjectBase* object);
//BTX
friend class vtkDebugLeaksManager;
friend class vtkObjectBase;
//ETX
private:
static vtkDebugLeaksHashTable* MemoryTable;
static vtkSimpleCriticalSection* CriticalSection;
static vtkDebugLeaksObserver* Observer;
static int ExitError;
vtkDebugLeaks(const vtkDebugLeaks&); // Not implemented.
void operator=(const vtkDebugLeaks&); // Not implemented.
};
//BTX
// This class defines callbacks for debugging tools. The callbacks are not for general use.
// The objects passed as arguments to the callbacks are in partially constructed or destructed
// state and accessing them may cause undefined behavior.
class VTK_COMMON_EXPORT vtkDebugLeaksObserver {
public:
virtual ~vtkDebugLeaksObserver() {};
virtual void ConstructingObject(vtkObjectBase*) = 0;
virtual void DestructingObject(vtkObjectBase*) = 0;
};
//ETX
#endif // __vtkDebugLeaks_h
......@@ -75,10 +75,17 @@ vtkObjectBase::vtkObjectBase()
{
this->ReferenceCount = 1;
this->WeakPointers = 0;
#ifdef VTK_DEBUG_LEAKS
vtkDebugLeaks::ConstructingObject(this);
#endif
}
vtkObjectBase::~vtkObjectBase()
{
#ifdef VTK_DEBUG_LEAKS
vtkDebugLeaks::DestructingObject(this);
#endif
// warn user if reference counting is on and the object is being referenced
// by another object
if ( this->ReferenceCount > 0)
......
......@@ -12,6 +12,8 @@ SET(QVTKLibSrcs
QVTKPaintEngine.h
vtkQtAbstractModelAdapter.cxx
vtkQtAnnotationLayersModelAdapter.cxx
vtkQtDebugLeaksModel.cxx
vtkQtDebugLeaksView.cxx
vtkQtSQLDatabase.cxx
vtkQtSQLQuery.cxx
vtkQtTableModelAdapter.cxx
......@@ -28,6 +30,8 @@ SET(QVTKMocHeaders
QFilterTreeProxyModel.h
vtkQtAbstractModelAdapter.h
vtkQtAnnotationLayersModelAdapter.h
vtkQtDebugLeaksModel.h
vtkQtDebugLeaksView.h
vtkQtTableModelAdapter.h
vtkQtTreeModelAdapter.h
)
......
......@@ -2,6 +2,7 @@
SET(KIT QVTK)
# add tests that do not require data
SET(MyTests
TestQtDebugLeaksView.cxx
TestQtInitialization.cxx
TestQtSQLDatabase.cxx
TestQtTableModelAdapter.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestQtTreeModelAdapter.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// Tests vtkQtDebugLeaksModel and vtkQtDebugLeaksView.
#include "vtkConeSource.h"
#include "vtkDebugLeaks.h"
#include "vtkSmartPointer.h"
#include "vtkQtDebugLeaksModel.h"
#include "vtkQtDebugLeaksView.h"
#include <QApplication>
#include <QStandardItemModel>
#include <QTableView>
#define fail(msg) \
std::cout << msg << std::endl; \
return EXIT_FAILURE
int TestQtDebugLeaksView(int argc, char* argv[])
{
QApplication app(argc, argv);
if (vtkDebugLeaks::GetDebugLeaksObserver())
{
fail("Expected debug leaks observer to be null at start of test.");
}
vtkQtDebugLeaksView view;
vtkQtDebugLeaksModel* model = view.model();
if (!vtkDebugLeaks::GetDebugLeaksObserver())
{
fail("Expected debug leaks observer to be initialized after constructing view.");
}
// Normally the model is updated asynchronously during the application event loop.
// Since there is no event loop running during this test we'll call processEvents()
// whenever we need the model to update.
app.processEvents();
std::cout << "Expect a warning message to be printed:" << std::endl;
QList<vtkObjectBase*> cones = model->getObjects("vtkConeSource");
if (!cones.isEmpty())
{
fail("Expected number of vtkConeSource to be 0");
}
// The rest of the test requires that VTK_DEBUG_LEAKS is enabled.
// The begining of this test is still useful to ensure that the widget
// opens without crashing when debug leaks is disabled.
#ifdef VTK_DEBUG_LEAKS
vtkSmartPointer<vtkConeSource> cone = vtkSmartPointer<vtkConeSource>::New();
app.processEvents();
cones = model->getObjects("vtkConeSource");
if (cones.size() != 1 || cones[0] != cone)
{
fail("Debug leaks model failed to locate the cone");
}
view.setFilterEnabled(true);
view.setFilterText("vtkCone");
QTableView* classTable = view.findChild<QTableView*>("ClassTable");
if (classTable->model()->rowCount() != 1)
{
fail("Expected exactly 1 row in debug leaks view.");
}
classTable->selectRow(0);
QStandardItemModel* referenceModel = model->referenceCountModel("vtkConeSource");
QTableView* referenceTable = view.findChild<QTableView*>("ReferenceTable");
if (referenceTable->model() != referenceModel)
{
fail("Reference table has incorrect model");
}
view.setFilterEnabled(false);
if (classTable->model()->rowCount() <= 1)
{
fail("Expected more than 1 row in the debug leaks view");
}
if (view.filterText() != "vtkCone")
{
fail("Expected filter text to be 'vtkCone'");
}
int baseReferenceCount = cone->GetReferenceCount();
if (referenceModel->rowCount() != 1)
{
fail("Expected reference model to have exactly 1 row");
}
if (referenceModel->data(referenceModel->index(0, 1)) != baseReferenceCount)
{
fail("Incorrect reference count");
}
vtkSmartPointer<vtkConeSource> newReference = cone;
int newReferenceCount = cone->GetReferenceCount();
if (newReferenceCount <= baseReferenceCount)
{
fail("Expected reference count to increase after constructing smart pointer");
}
// Normally the reference count model is updated periodically during the application event loop.
// Since there is no event loop running in this test we'll directly invoke the update routine.
QMetaObject::invokeMethod(referenceModel, "updateReferenceCounts", Qt::DirectConnection);
if (referenceModel->data(referenceModel->index(0, 1)) != newReferenceCount)
{
fail("Incorrect reference count");
}
newReference = NULL;
QMetaObject::invokeMethod(referenceModel, "updateReferenceCounts", Qt::DirectConnection);
if (referenceModel->data(referenceModel->index(0, 1)) != baseReferenceCount)
{
fail("Incorrect reference count");
}
newReference = vtkSmartPointer<vtkConeSource>::New();
app.processEvents();
if (referenceModel->rowCount() != 2)
{
fail("Expected reference model to have exactly 2 rows");
}
newReference = NULL;
cone = NULL;
app.processEvents();
view.setFilterEnabled(true);
if (classTable->model()->rowCount() != 0)
{
fail("Expected 0 rows in the debug leaks view");
}
#endif
//uncomment to keep the widget open for interaction
//view.show();
//view.setAttribute(Qt::WA_QuitOnClose, true);
//app.exec();
return EXIT_SUCCESS;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkQtDebugLeaksModel.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkQtDebugLeaksModel.h"
#include "vtkDebugLeaks.h"
#include <QCoreApplication>
#include <QDebug>
#include <QList>
#include <QPointer>
#include <QTextStream>
#include <QTimer>
//-----------------------------------------------------------------------------
class vtkQtDebugLeaksModel::qObserver : public vtkDebugLeaksObserver {
public:
qObserver(vtkQtDebugLeaksModel& model) : Model(model)
{
vtkDebugLeaks::SetDebugLeaksObserver(this);
}
virtual ~qObserver()
{
vtkDebugLeaks::SetDebugLeaksObserver(0);
}
virtual void ConstructingObject(vtkObjectBase* object)
{
this->Model.addObject(object);
}
virtual void DestructingObject(vtkObjectBase* object)
{
this->Model.removeObject(object);
}
vtkQtDebugLeaksModel& Model;
};
//-----------------------------------------------------------------------------
class VTKClassInfo {
public:
VTKClassInfo(const QString& className) : Count(0), Name(className)
{
}
int Count;
QString Name;
QList<vtkObjectBase*> Objects;
};
//-----------------------------------------------------------------------------
class vtkQtDebugLeaksModel::qInternal
{
public:
qInternal() : ProcessPending(false) { }
bool ProcessPending;
QList<QString> Classes;
QList<VTKClassInfo> ClassInfo;
QList<vtkObjectBase*> ObjectsToProcess;
QHash<vtkObjectBase*, VTKClassInfo*> ObjectMap;
QHash<QString, QPointer<ReferenceCountModel> > ReferenceModels;
};
//-----------------------------------------------------------------------------
vtkQtDebugLeaksModel::vtkQtDebugLeaksModel(QObject* parent)
: QStandardItemModel(0, 2, parent)
{
this->Internal = new qInternal;
this->Observer = new qObserver(*this);
this->setHeaderData(0, Qt::Horizontal, QObject::tr("Class Name"));
this->setHeaderData(1, Qt::Horizontal, QObject::tr("Class Count"));
this->connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
SLOT(onAboutToQuit()));
}
//-----------------------------------------------------------------------------
vtkQtDebugLeaksModel::~vtkQtDebugLeaksModel()
{
delete this->Observer;
delete this->Internal;
}
//-----------------------------------------------------------------------------
void vtkQtDebugLeaksModel::onAboutToQuit()
{
delete this->Observer;
this->Observer = 0;
}
//----------------------------------------------------------------------------
void vtkQtDebugLeaksModel::addObject(vtkObjectBase* object)
{
this->Internal->ObjectsToProcess.append(object);
if (!this->Internal->ProcessPending)
{
this->Internal->ProcessPending = true;
QTimer::singleShot(0, this, SLOT(processPendingObjects()));
}
}
//----------------------------------------------------------------------------
void vtkQtDebugLeaksModel::processPendingObjects()
{
this->Internal->ProcessPending = false;
foreach(vtkObjectBase* object, this->Internal->ObjectsToProcess)
{
this->registerObject(object);
}
this->Internal->ObjectsToProcess.clear();
}
//----------------------------------------------------------------------------
void vtkQtDebugLeaksModel::registerObject(vtkObjectBase* object)
{
QString className = object->GetClassName();
int indexOf = this->Internal->Classes.indexOf(className);
if (indexOf < 0)
{
this->Internal->Classes.append(className);
this->Internal->ClassInfo.append(VTKClassInfo(className));
indexOf = this->rowCount();
this->insertRow(indexOf);
this->setData(this->index(indexOf, 0), className);
this->setData(this->index(indexOf, 1), 0);
}
VTKClassInfo& classInfo = this->Internal->ClassInfo[indexOf];
classInfo.Count += 1;
classInfo.Objects.append(object);
this->Internal->ObjectMap[object] = &classInfo;
this->setData(this->index(indexOf, 1), classInfo.Count);
ReferenceCountModel* model = this->Internal->ReferenceModels.value(className, 0);
if (model)
{
model->addObject(object);
}
}
//----------------------------------------------------------------------------
void vtkQtDebugLeaksModel::removeObject(vtkObjectBase* object)
{
VTKClassInfo* classInfo = this->Internal->ObjectMap.value(object, 0);
if (classInfo)
{
QString className = classInfo->Name;
int row = this->Internal->Classes.indexOf(className);
classInfo->Count -= 1;
classInfo->Objects.removeOne(object);
this->Internal->ObjectMap.remove(object);
if (classInfo->Count <= 0)
{
this->Internal->Classes.removeAt(row);
this->Internal->ClassInfo.removeAt(row);
this->removeRow(row);
}
else
{
this->setData(this->index(row, 1), classInfo->Count);
}
ReferenceCountModel* model = this->Internal->ReferenceModels.value(className, 0);
if (model)
{
model->removeObject(object);
}
}
else
{
this->Internal->ObjectsToProcess.removeOne(object);
}
}
//-----------------------------------------------------------------------------
QList<vtkObjectBase*> vtkQtDebugLeaksModel::getObjects(const QString& className)
{
int indexOf = this->Internal->Classes.indexOf(className);
if (indexOf < 0)
{
qWarning() << "vtkQtDebugLeaksModel::getObjects: bad class name:" << className;
return QList<vtkObjectBase*>();
}
VTKClassInfo &classInfo = this->Internal->ClassInfo[indexOf];
return classInfo.Objects;
}
//----------------------------------------------------------------------------
QStandardItemModel* vtkQtDebugLeaksModel::referenceCountModel(const QString& className)
{
ReferenceCountModel* model = this->Internal->ReferenceModels.value(className, 0);
if (!model)
{
model = new ReferenceCountModel(this);
this->Internal->ReferenceModels[className] = model;
foreach (vtkObjectBase* obj, this->getObjects(className))
{
model->addObject(obj);
}
}
return model;
}
//-----------------------------------------------------------------------------
Qt::ItemFlags vtkQtDebugLeaksModel::flags(const QModelIndex &index) const
{
Q_UNUSED(index);
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
//-----------------------------------------------------------------------------
Q_DECLARE_METATYPE(vtkObjectBase*);
//-----------------------------------------------------------------------------
ReferenceCountModel::ReferenceCountModel(QObject* parent)
: QStandardItemModel(0, 2, parent)
{
this->setHeaderData(0, Qt::Horizontal, QObject::tr("Pointer"));
this->setHeaderData(1, Qt::Horizontal, QObject::tr("Reference Count"));
QTimer::singleShot(100, this, SLOT(updateReferenceCounts()));
}
ReferenceCountModel::~ReferenceCountModel()
{
}
//-----------------------------------------------------------------------------
QString ReferenceCountModel::pointerAsString(void* ptr)
{
QString ptrStr;
QTextStream stream(&ptrStr);
stream << ptr;
return ptrStr;
}
//-----------------------------------------------------------------------------
void ReferenceCountModel::addObject(vtkObjectBase* obj)
{
int row = this->rowCount();
this->insertRow(row);
this->setData(this->index(row, 0), this->pointerAsString(obj));
this->setData(this->index(row, 0), qVariantFromValue(obj), Qt::UserRole);
this->setData(this->index(row, 1), obj->GetReferenceCount());
}
//-----------------------------------------------------------------------------
void ReferenceCountModel::removeObject(vtkObjectBase* obj)
{
QString pointerString = this->pointerAsString(obj);
for (int i = 0; i < this->rowCount(); ++i)
{
if (this->data(this->index(i, 0)) == pointerString)
{
this->removeRow(i);
return;
}
}
}
//-----------------------------------------------------------------------------
void ReferenceCountModel::updateReferenceCounts()
{
for (int row = 0; row < this->rowCount(); ++row)
{
QVariant pointerVariant = this->data(this->index(row, 0), Qt::UserRole);
vtkObjectBase* obj = pointerVariant.value<vtkObjectBase*>();