Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement QObject counting grouped by metaobject #30

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 161 additions & 7 deletions core/metaobjecttreemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,135 @@
#include <QDebug>
#include <QThread>

#include <assert.h>

using namespace GammaRay;

#define IF_DEBUG(x)

namespace GammaRay {

/**
* Tracks information about meta objects
*
* @sa objectAdded for explanation
*/
class MetaObjectInfoTracker
{
public:
struct MetaObjectInfo
{
MetaObjectInfo() : selfCount(0), inclusiveCount(0) {}

/// Number of objects of a particular meta object type
int selfCount;
/**
* Number of objects of the exact meta object type
* + number of objects of type that inherit from this meta type
*/
int inclusiveCount;
};

/**
* Use this whenever a new object of type @p metaObject was seen
*
* This will increase these values:
* - selfCount for that particular @p metaObject
* - inclusiveCount for @p metaObject and *all* ancestors
*
* Complexity-wise the inclusive count calculation should be okay,
* since the number of ancestors should be rather small
* (QMetaObject class hierarchy is rather a broad than a deep tree structure)
*
* If this yields some performance issues, we might need to remove the inclusive
* costs calculation altogether (a calculate-on-request pattern should be even slower)
*/
void objectAdded(const QMetaObject* metaObject)
{
// note: use plain C asserts here, infinite loops otherwise in case of assert

++m_metaObjectInfoMap[metaObject].selfCount;

// increase inclusive counts
const QMetaObject* current = metaObject;
while (current) {
++m_metaObjectInfoMap[current].inclusiveCount;
current = current->superClass();
}
}

/**
* @sa objectAdded for explanation
*/
void objectRemoved(const QMetaObject* metaObject)
{
// note: use plain C asserts here, infinite loops otherwise

assert(m_metaObjectInfoMap.contains(metaObject));
if (m_metaObjectInfoMap[metaObject].selfCount == 0) {
// something went wrong, but let's just ignore this event in case of assert
return;
}

--m_metaObjectInfoMap[metaObject].selfCount;
assert(m_metaObjectInfoMap[metaObject].selfCount >= 0);

// decrease inclusive counts
const QMetaObject* current = metaObject;
while (current) {
--m_metaObjectInfoMap[current].inclusiveCount;
assert(m_metaObjectInfoMap[current].inclusiveCount >= 0);
current = current->superClass();
}
}

inline const MetaObjectInfo& info(const QMetaObject* metaObject)
{
return m_metaObjectInfoMap[metaObject];
}

private:
QHash<const QMetaObject*, MetaObjectInfo> m_metaObjectInfoMap;
};

}

MetaObjectTreeModel::MetaObjectTreeModel(QObject *parent)
: QAbstractItemModel(parent)
, m_infoTracker(new MetaObjectInfoTracker)
{
qRegisterMetaType<const QMetaObject *>();
}

MetaObjectTreeModel::~MetaObjectTreeModel()
{
delete m_infoTracker;
}

QVariant MetaObjectTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
switch (section) {
case ObjectColumn:
return tr("Meta Object Class Hierarchy");
return tr("Meta Object Class");
case ObjectSelfCountColumn:
return tr("Self (#Objects)");
case ObjectInclusiveCountColumn:
return tr("Incl. (#Objects)");
default:
return QVariant();
}
} else if (role == Qt::ToolTipRole) {
switch (section) {
case ObjectColumn:
return tr("This column shows the QMetaObject class hierarchy");
case ObjectSelfCountColumn:
return tr("This column shows the number of objects of particular type");
case ObjectInclusiveCountColumn:
return tr("This column shows the number of objects that inherit from a particular type");
default:
return QVariant();
}
}

return QAbstractItemModel::headerData(section, orientation, role);
Expand All @@ -64,6 +175,10 @@ QVariant MetaObjectTreeModel::data(const QModelIndex &index, int role) const
switch(column) {
case ObjectColumn:
return object->className();
case ObjectSelfCountColumn:
return m_infoTracker->info(object).selfCount;
case ObjectInclusiveCountColumn:
return m_infoTracker->info(object).inclusiveCount;
default:
break;
}
Expand All @@ -76,7 +191,7 @@ QVariant MetaObjectTreeModel::data(const QModelIndex &index, int role) const
int MetaObjectTreeModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
return _Last;
}

int MetaObjectTreeModel::rowCount(const QModelIndex &parent) const
Expand Down Expand Up @@ -110,18 +225,32 @@ QModelIndex MetaObjectTreeModel::index(int row, int column, const QModelIndex &p
}

void MetaObjectTreeModel::objectAdded(QObject *obj)
{
// when called from background, delay into fore&ground, otherwise call directly
QMetaObject::invokeMethod(this, "objectAddedMainThread", Qt::AutoConnection,
Q_ARG(QObject *, obj));
}

void MetaObjectTreeModel::objectAddedMainThread(QObject* obj)
{
// slot, hence should always land in main thread due to auto connection
Q_ASSERT(thread() == QThread::currentThread());
assert(thread() == QThread::currentThread());

ReadOrWriteLocker objectLock(Probe::instance()->objectLock());
if (!Probe::instance()->isValidObject(obj)) {
return;
}
Q_ASSERT(!obj->parent() || Probe::instance()->isValidObject(obj->parent()));
assert(!obj->parent() || Probe::instance()->isValidObject(obj->parent()));

QWriteLocker lock(&m_lock);
const QMetaObject *metaObject = obj->metaObject();
addMetaObject(metaObject);

// increase counter
const QModelIndex metaModelIndex = indexForMetaObject(metaObject);
assert(metaModelIndex.isValid());
m_infoTracker->objectAdded(metaObject);
emit dataChanged(metaModelIndex, metaModelIndex);
}

void MetaObjectTreeModel::addMetaObject(const QMetaObject *metaObject)
Expand All @@ -141,7 +270,7 @@ void MetaObjectTreeModel::addMetaObject(const QMetaObject *metaObject)

const QModelIndex parentIndex = indexForMetaObject(parentMetaObject);
// either we get a proper parent and hence valid index or there is no parent
Q_ASSERT(parentIndex.isValid() || !parentMetaObject);
assert(parentIndex.isValid() || !parentMetaObject);

QVector<const QMetaObject*> &children = m_parentChildMap[ parentMetaObject ];

Expand All @@ -159,8 +288,32 @@ void MetaObjectTreeModel::removeMetaObject(const QMetaObject *metaObject)

void MetaObjectTreeModel::objectRemoved(QObject *obj)
{
Q_UNUSED(obj);
// TODO
// we need to find out the meta object *now*
// in objectRemovedMainThread the QObject* is no longer valid
const QMetaObject* metaObject = obj->metaObject();

// when called from background, delay into foreground, otherwise call directly
QMetaObject::invokeMethod(this, "objectRemovedMainThread", Qt::AutoConnection,
Q_ARG(const QMetaObject *, metaObject));
}

// We're just interested in the meta object of the object that was removed
// Hence the QMetaObject* parameter
void MetaObjectTreeModel::objectRemovedMainThread(const QMetaObject *metaObject)
{
assert(thread() == QThread::currentThread());

QWriteLocker lock(&m_lock);

// decrease counter
const QModelIndex metaModelIndex = indexForMetaObject(metaObject);
if (!metaModelIndex.isValid()) {
// something went wrong, ignore
return;
}

m_infoTracker->objectRemoved(metaObject);
emit dataChanged(metaModelIndex, metaModelIndex);
}

QModelIndex MetaObjectTreeModel::indexForMetaObject(const QMetaObject *metaObject) const
Expand All @@ -170,6 +323,7 @@ QModelIndex MetaObjectTreeModel::indexForMetaObject(const QMetaObject *metaObjec
}

const QMetaObject *parentObject = m_childParentMap.value(metaObject);
Q_ASSERT(parentObject != metaObject);
const QModelIndex parentIndex = indexForMetaObject(parentObject);
if (!parentIndex.isValid() && parentObject) {
return QModelIndex();
Expand Down
14 changes: 13 additions & 1 deletion core/metaobjecttreemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

namespace GammaRay {

class MetaObjectInfoTracker;

class MetaObjectTreeModel : public QAbstractItemModel
{
Q_OBJECT
Expand All @@ -40,10 +42,14 @@ class MetaObjectTreeModel : public QAbstractItemModel
};

enum Column {
ObjectColumn
ObjectColumn,
ObjectSelfCountColumn,
ObjectInclusiveCountColumn,
_Last
};

explicit MetaObjectTreeModel(QObject *parent = 0);
virtual ~MetaObjectTreeModel();

// reimplemented methods
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
Expand All @@ -64,6 +70,10 @@ class MetaObjectTreeModel : public QAbstractItemModel
void objectAdded(QObject *obj);
void objectRemoved(QObject *obj);

private Q_SLOTS:
void objectAddedMainThread(QObject *obj);
void objectRemovedMainThread(const QMetaObject *metaObject);

private:
void addMetaObject(const QMetaObject *metaObject);
void removeMetaObject(const QMetaObject *metaObject);
Expand All @@ -76,6 +86,8 @@ class MetaObjectTreeModel : public QAbstractItemModel
// data
QHash<const QMetaObject*, const QMetaObject*> m_childParentMap;
QHash<const QMetaObject*, QVector<const QMetaObject*> > m_parentChildMap;

MetaObjectInfoTracker* m_infoTracker;
};

}
Expand Down
1 change: 1 addition & 0 deletions core/probe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ void Probe::objectRemoved(QObject *obj)
instance()->m_queuedObjects.removeOne(obj);

instance()->m_objectListModel->objectRemoved(obj);
instance()->m_metaObjectTreeModel->objectRemoved(obj);

instance()->connectionRemoved(obj, 0, 0, 0);
instance()->connectionRemoved(0, 0, obj, 0);
Expand Down