/****************************************************************************** * 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 #include #include QSK_QT_PRIVATE_BEGIN #include #include #include QSK_QT_PRIVATE_END static struct { QHooks::StartupCallback startup; QHooks::AddQObjectCallback addObject; QHooks::RemoveQObjectCallback removeObject; } qskOtherHooks; static inline bool qskIsItem( const QObject* object ) { QObjectPrivate* o_p = QObjectPrivate::get( const_cast< QObject* >( object ) ); /* The addObject hook is called from the constructor of QObject, where we don't have the derived class constructed yet. So we can't cast the object itself and also have to rely on RTTI being enabled. TODO ... */ return dynamic_cast< QQuickItemPrivate* >( o_p ) != nullptr; } class QskObjectCounterHook { public: 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 ) ); } void startup() const { #if 0 qDebug() << "** QskObjectCounterHook enabled"; #endif } void addObject( QObject* object ) { for ( auto counter : qskAsConst( m_counterSet ) ) counter->addObject( object ); } void removeObject( QObject* object ) { for ( auto counter : qskAsConst( m_counterSet ) ) counter->removeObject( object ); } private: QSet< QskObjectCounter* > m_counterSet; }; static QskObjectCounterHook* qskCounterHook(); static void qskStartupHook() { qskCounterHook()->startup(); if ( qskOtherHooks.startup ) qskOtherHooks.startup(); } static void qskAddObjectHook( QObject* object ) { qskCounterHook()->addObject( object ); if ( qskOtherHooks.addObject ) qskOtherHooks.addObject( object ); } static void qskRemoveObjectHook( QObject* object ) { qskCounterHook()->removeObject( object ); if ( qskOtherHooks.removeObject ) qskOtherHooks.removeObject( object ); } static QskObjectCounterHook* qskCounterHook() { static bool isInitialized = false; if ( !isInitialized ) { qskOtherHooks.startup = reinterpret_cast< QHooks::StartupCallback >( qtHookData[QHooks::Startup] ); qskOtherHooks.addObject = reinterpret_cast< QHooks::AddQObjectCallback >( qtHookData[QHooks::AddQObject] ); qskOtherHooks.removeObject = reinterpret_cast< QHooks::RemoveQObjectCallback >( qtHookData[QHooks::RemoveQObject] ); qtHookData[QHooks::Startup] = reinterpret_cast< quintptr >( &qskStartupHook ); qtHookData[QHooks::AddQObject] = reinterpret_cast< quintptr >( &qskAddObjectHook ); qtHookData[QHooks::RemoveQObject] = reinterpret_cast< quintptr >( &qskRemoveObjectHook ); isInitialized = true; } static QskObjectCounterHook counterHook; return &counterHook; } QskObjectCounter::QskObjectCounter( bool debugAtDestruction ): m_debugAtDestruction( debugAtDestruction ) { setActive( true ); } QskObjectCounter::~QskObjectCounter() { setActive( false ); if ( m_debugAtDestruction ) dump(); } void QskObjectCounter::setActive( bool on ) { qskCounterHook()->registerCounter( this, on ); } bool QskObjectCounter::isActive() const { return qskCounterHook()->isCounterRegistered( this ); } 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 { return m_counter[objectType].created; } int QskObjectCounter::destroyed( ObjectType objectType ) const { return m_counter[objectType].destroyed; } int QskObjectCounter::current( ObjectType objectType ) const { return m_counter[objectType].current; } int QskObjectCounter::maximum( ObjectType objectType ) const { return m_counter[objectType].maximum; } 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