2017-07-21 18:21:34 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskObjectCounter.h"
|
|
|
|
#include <QQuickItem>
|
|
|
|
#include <QSet>
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
QSK_QT_PRIVATE_BEGIN
|
|
|
|
#include <private/qhooks_p.h>
|
|
|
|
#include <private/qquickitem_p.h>
|
|
|
|
#include <private/qobject_p.h>
|
|
|
|
QSK_QT_PRIVATE_END
|
|
|
|
|
|
|
|
static inline bool qskIsItem( const QObject* object )
|
|
|
|
{
|
|
|
|
QObjectPrivate* o_p = QObjectPrivate::get( const_cast< QObject* >( object ) );
|
|
|
|
|
2018-02-07 09:25:31 +01:00
|
|
|
/*
|
|
|
|
The addObject hook is called from the constructor of QObject,
|
|
|
|
where we don't have the derived class constructed yet.
|
2018-02-07 15:40:05 +01:00
|
|
|
So we can't cast the object itself and also have to rely on
|
2018-02-07 09:25:31 +01:00
|
|
|
RTTI being enabled. TODO ...
|
|
|
|
*/
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
return dynamic_cast< QQuickItemPrivate* >( o_p ) != nullptr;
|
|
|
|
}
|
|
|
|
|
2018-02-07 15:40:05 +01:00
|
|
|
static void qskStartupHook();
|
|
|
|
static void qskAddObjectHook( QObject* );
|
|
|
|
static void qskRemoveObjectHook( QObject* );
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
class QskObjectCounterHook
|
|
|
|
{
|
|
|
|
public:
|
2018-02-07 15:40:05 +01:00
|
|
|
QskObjectCounterHook()
|
|
|
|
{
|
|
|
|
m_otherStartup = qtHookData[QHooks::Startup];
|
|
|
|
m_otherAddObject = qtHookData[QHooks::AddQObject];
|
|
|
|
m_otherRemoveObject = qtHookData[QHooks::RemoveQObject];
|
|
|
|
|
|
|
|
qtHookData[QHooks::Startup] = reinterpret_cast< quintptr >( &qskStartupHook );
|
|
|
|
qtHookData[QHooks::AddQObject] = reinterpret_cast< quintptr >( &qskAddObjectHook );
|
|
|
|
qtHookData[QHooks::RemoveQObject] = reinterpret_cast< quintptr >( &qskRemoveObjectHook );
|
|
|
|
}
|
|
|
|
|
|
|
|
~QskObjectCounterHook()
|
|
|
|
{
|
|
|
|
qtHookData[QHooks::Startup] = m_otherStartup;
|
|
|
|
qtHookData[QHooks::AddQObject] = m_otherAddObject;
|
|
|
|
qtHookData[QHooks::RemoveQObject] = m_otherRemoveObject;
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void registerCounter( QskObjectCounter* counter, bool on )
|
|
|
|
{
|
|
|
|
if ( on )
|
|
|
|
m_counterSet.insert( counter );
|
|
|
|
else
|
|
|
|
m_counterSet.remove( counter );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isCounterRegistered( const QskObjectCounter* counter ) const
|
|
|
|
{
|
|
|
|
return m_counterSet.contains( const_cast< QskObjectCounter* >( counter ) );
|
|
|
|
}
|
|
|
|
|
2018-02-07 15:40:05 +01:00
|
|
|
bool isActive() const
|
|
|
|
{
|
|
|
|
return !m_counterSet.isEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
void startup()
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
qDebug() << "** QskObjectCounterHook enabled";
|
|
|
|
#endif
|
2018-02-07 15:40:05 +01:00
|
|
|
if ( m_otherStartup )
|
|
|
|
reinterpret_cast< QHooks::StartupCallback >( m_otherStartup )();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void addObject( QObject* object )
|
|
|
|
{
|
2017-10-30 14:38:30 +01:00
|
|
|
for ( auto counter : qskAsConst( m_counterSet ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
counter->addObject( object );
|
2018-02-07 15:40:05 +01:00
|
|
|
|
|
|
|
if ( m_otherAddObject )
|
|
|
|
reinterpret_cast< QHooks::AddQObjectCallback >( m_otherAddObject )( object );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void removeObject( QObject* object )
|
|
|
|
{
|
2017-10-30 14:38:30 +01:00
|
|
|
for ( auto counter : qskAsConst( m_counterSet ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
counter->removeObject( object );
|
2018-02-07 15:40:05 +01:00
|
|
|
|
|
|
|
if ( m_otherRemoveObject )
|
|
|
|
reinterpret_cast< QHooks::RemoveQObjectCallback >( m_otherRemoveObject )( object );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-07 15:40:05 +01:00
|
|
|
static bool autoDelete;
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
private:
|
|
|
|
QSet< QskObjectCounter* > m_counterSet;
|
2018-02-07 15:40:05 +01:00
|
|
|
|
|
|
|
quintptr m_otherStartup;
|
|
|
|
quintptr m_otherAddObject;
|
|
|
|
quintptr m_otherRemoveObject;
|
2017-07-21 18:21:34 +02:00
|
|
|
};
|
|
|
|
|
2018-02-07 15:40:05 +01:00
|
|
|
bool QskObjectCounterHook::autoDelete = false;
|
|
|
|
static QskObjectCounterHook* qskCounterHook = nullptr;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
static void qskStartupHook()
|
|
|
|
{
|
2018-02-07 15:40:05 +01:00
|
|
|
if ( qskCounterHook )
|
|
|
|
qskCounterHook->startup();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void qskAddObjectHook( QObject* object )
|
|
|
|
{
|
2018-02-07 15:40:05 +01:00
|
|
|
if ( qskCounterHook )
|
|
|
|
qskCounterHook->addObject( object );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void qskRemoveObjectHook( QObject* object )
|
|
|
|
{
|
2018-02-07 15:40:05 +01:00
|
|
|
if ( qskCounterHook )
|
|
|
|
qskCounterHook->removeObject( object );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-07 15:40:05 +01:00
|
|
|
static void qskCleanupHook()
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-02-07 15:40:05 +01:00
|
|
|
if ( qskCounterHook && !qskCounterHook->isActive() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-02-07 15:40:05 +01:00
|
|
|
delete qskCounterHook;
|
|
|
|
qskCounterHook = nullptr;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-07 15:40:05 +01:00
|
|
|
// From now on we remove the hooks as soon as there are no counters
|
|
|
|
QskObjectCounterHook::autoDelete = true;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-07 15:40:05 +01:00
|
|
|
static void qskInstallCleanupHookHandler()
|
|
|
|
{
|
|
|
|
qAddPostRoutine( qskCleanupHook );
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_COREAPP_STARTUP_FUNCTION( qskInstallCleanupHookHandler )
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
QskObjectCounter::QskObjectCounter( bool debugAtDestruction ):
|
|
|
|
m_debugAtDestruction( debugAtDestruction )
|
|
|
|
{
|
|
|
|
setActive( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskObjectCounter::~QskObjectCounter()
|
|
|
|
{
|
|
|
|
setActive( false );
|
|
|
|
|
|
|
|
if ( m_debugAtDestruction )
|
|
|
|
dump();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskObjectCounter::setActive( bool on )
|
|
|
|
{
|
2018-02-07 15:40:05 +01:00
|
|
|
if ( on )
|
|
|
|
{
|
|
|
|
if ( qskCounterHook == nullptr )
|
|
|
|
qskCounterHook = new QskObjectCounterHook();
|
|
|
|
|
|
|
|
qskCounterHook->registerCounter( this, on );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
qskCounterHook->registerCounter( this, on );
|
|
|
|
if ( !qskCounterHook->isActive() )
|
|
|
|
{
|
|
|
|
if ( QskObjectCounterHook::autoDelete )
|
|
|
|
{
|
|
|
|
delete qskCounterHook;
|
|
|
|
qskCounterHook = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool QskObjectCounter::isActive() const
|
|
|
|
{
|
2018-02-07 15:40:05 +01:00
|
|
|
return qskCounterHook && qskCounterHook->isCounterRegistered( this );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskObjectCounter::addObject( QObject* object )
|
|
|
|
{
|
|
|
|
m_counter[Objects].increment();
|
|
|
|
|
|
|
|
if ( qskIsItem( object ) )
|
|
|
|
m_counter[Items].increment();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskObjectCounter::removeObject( QObject* object )
|
|
|
|
{
|
|
|
|
m_counter[Objects].decrement();
|
|
|
|
|
|
|
|
if ( qskIsItem( object ) )
|
|
|
|
m_counter[Items].decrement();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskObjectCounter::reset()
|
|
|
|
{
|
|
|
|
m_counter[Objects].reset();
|
|
|
|
m_counter[Items].reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
int QskObjectCounter::created( ObjectType objectType ) const
|
|
|
|
{
|
2017-10-30 08:08:58 +01:00
|
|
|
return m_counter[objectType].created;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int QskObjectCounter::destroyed( ObjectType objectType ) const
|
|
|
|
{
|
2017-10-30 08:08:58 +01:00
|
|
|
return m_counter[objectType].destroyed;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int QskObjectCounter::current( ObjectType objectType ) const
|
|
|
|
{
|
2017-10-30 08:08:58 +01:00
|
|
|
return m_counter[objectType].current;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int QskObjectCounter::maximum( ObjectType objectType ) const
|
|
|
|
{
|
2017-10-30 08:08:58 +01:00
|
|
|
return m_counter[objectType].maximum;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskObjectCounter::debugStatistics( QDebug debug, ObjectType objectType ) const
|
|
|
|
{
|
|
|
|
const Counter& c = m_counter[objectType];
|
|
|
|
|
|
|
|
QDebugStateSaver saver( debug );
|
|
|
|
debug.nospace();
|
|
|
|
debug << '(';
|
|
|
|
debug << "created: " << c.created
|
|
|
|
<< ", destroyed: " << c.destroyed
|
|
|
|
<< ", current: " << c.current
|
|
|
|
<< ", maximum: " << c.maximum;
|
|
|
|
debug << ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskObjectCounter::dump() const
|
|
|
|
{
|
|
|
|
QDebug debug = qDebug();
|
|
|
|
|
|
|
|
QDebugStateSaver saver( debug );
|
|
|
|
|
|
|
|
debug.nospace();
|
|
|
|
|
|
|
|
debug << "* Statistics\n";
|
|
|
|
debug << " Objects: ";
|
|
|
|
debugStatistics( debug, Objects );
|
|
|
|
|
|
|
|
debug << "\n Items: ";
|
|
|
|
debugStatistics( debug, Items );
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef QT_NO_DEBUG_STREAM
|
|
|
|
|
|
|
|
QDebug operator<<( QDebug debug, const QskObjectCounter& counter )
|
|
|
|
{
|
|
|
|
counter.debugStatistics( debug, QskObjectCounter::Objects );
|
|
|
|
return debug;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|