6#include "QskObjectCounter.h"
11#include <qcoreapplication.h>
14#include <private/qhooks_p.h>
15#include <private/qquickitem_p.h>
18#define QSK_OBJECT_INFO 0
24static inline bool qskIsItem(
const QObject*
object )
33 auto o_p = QObjectPrivate::get(
const_cast< QObject*
>(
object ) );
34 return dynamic_cast< QQuickItemPrivate*
>( o_p ) !=
nullptr;
49 created = destroyed = current = maximum = 0;
57 if ( current > maximum )
79 QSet< const QObject* > objectTable;
89 void registerCounters( CounterData*,
bool on );
90 bool isCountersRegistered(
const CounterData* )
const;
92 bool isActive()
const;
95 void addObject( QObject* );
96 void removeObject( QObject* );
98 static void cleanupHook();
101 static void startupHook();
102 static void addObjectHook( QObject* );
103 static void removeObjectHook( QObject* );
105 QSet< CounterData* > m_counterDataSet;
107 quintptr m_otherStartup;
108 quintptr m_otherAddObject;
109 quintptr m_otherRemoveObject;
113static bool qskAutoDeleteHook =
false;
114static CounterHook* qskCounterHook =
nullptr;
116CounterHook::CounterHook()
118 m_otherStartup = qtHookData[ QHooks::Startup ];
119 m_otherAddObject = qtHookData[ QHooks::AddQObject ];
120 m_otherRemoveObject = qtHookData[ QHooks::RemoveQObject ];
122 qtHookData[ QHooks::Startup ] =
reinterpret_cast< quintptr
>( &startupHook );
123 qtHookData[ QHooks::AddQObject ] =
reinterpret_cast< quintptr
>( &addObjectHook );
124 qtHookData[ QHooks::RemoveQObject ] =
reinterpret_cast< quintptr
>( &removeObjectHook );
127CounterHook::~CounterHook()
129 qtHookData[ QHooks::Startup ] = m_otherStartup;
130 qtHookData[ QHooks::AddQObject ] = m_otherAddObject;
131 qtHookData[ QHooks::RemoveQObject ] = m_otherRemoveObject;
134void CounterHook::registerCounters( CounterData* data,
bool on )
137 m_counterDataSet.insert( data );
139 m_counterDataSet.remove( data );
142bool CounterHook::isCountersRegistered(
const CounterData* data )
const
144 return m_counterDataSet.contains(
const_cast< CounterData*
>( data ) );
147bool CounterHook::isActive()
const
149 return !m_counterDataSet.isEmpty();
152void CounterHook::startup()
155 qDebug() <<
"** QskObjectCounterHook enabled";
157 if ( m_otherStartup )
158 reinterpret_cast< QHooks::StartupCallback
>( m_otherStartup )();
161void CounterHook::addObject( QObject*
object )
163 const bool isItem = qskIsItem(
object );
165 for (
auto counterData : std::as_const( m_counterDataSet ) )
167 counterData->counter[ QskObjectCounter::Objects ].increment();
170 counterData->counter[ QskObjectCounter::Items ].increment();
173 counterData->objectTable.insert(
object );
177 if ( m_otherAddObject )
178 reinterpret_cast< QHooks::AddQObjectCallback
>( m_otherAddObject )(
object );
181void CounterHook::removeObject( QObject*
object )
183 const bool isItem = qskIsItem(
object );
185 for (
auto counterData : std::as_const( m_counterDataSet ) )
187 counterData->counter[ QskObjectCounter::Objects ].decrement();
190 counterData->counter[ QskObjectCounter::Items ].decrement();
193 counterData->objectTable.remove(
object );
197 if ( m_otherRemoveObject )
198 reinterpret_cast< QHooks::RemoveQObjectCallback
>( m_otherRemoveObject )(
object );
201void CounterHook::startupHook()
203 if ( qskCounterHook )
204 qskCounterHook->startup();
207void CounterHook::addObjectHook( QObject*
object )
209 if ( qskCounterHook )
210 qskCounterHook->addObject(
object );
213void CounterHook::removeObjectHook( QObject*
object )
215 if ( qskCounterHook )
216 qskCounterHook->removeObject(
object );
219void CounterHook::cleanupHook()
221 if ( qskCounterHook && !qskCounterHook->isActive() )
223 delete qskCounterHook;
224 qskCounterHook =
nullptr;
228 qskAutoDeleteHook =
true;
231static void qskInstallCleanupHookHandler()
233 qAddPostRoutine( CounterHook::cleanupHook );
236Q_COREAPP_STARTUP_FUNCTION( qskInstallCleanupHookHandler )
238class QskObjectCounter::PrivateData
241 PrivateData(
bool debugAtDestruction )
242 : debugAtDestruction( debugAtDestruction )
246 CounterData counterData;
247 const bool debugAtDestruction;
250QskObjectCounter::QskObjectCounter(
bool debugAtDestruction )
251 : m_data( new PrivateData( debugAtDestruction ) )
256QskObjectCounter::~QskObjectCounter()
260 if ( m_data->debugAtDestruction )
264void QskObjectCounter::setActive(
bool on )
268 if ( qskCounterHook ==
nullptr )
269 qskCounterHook =
new CounterHook();
271 qskCounterHook->registerCounters( &m_data->counterData, on );
275 qskCounterHook->registerCounters( &m_data->counterData, on );
276 if ( !qskCounterHook->isActive() )
278 if ( qskAutoDeleteHook )
280 delete qskCounterHook;
281 qskCounterHook =
nullptr;
287bool QskObjectCounter::isActive()
const
289 return qskCounterHook && qskCounterHook->isCountersRegistered( &m_data->counterData );
292void QskObjectCounter::reset()
294 auto& counters = m_data->counterData.counter;
296 counters[ Objects ].reset();
297 counters[ Items ].reset();
300int QskObjectCounter::created( ObjectType objectType )
const
302 return m_data->counterData.counter[ objectType ].created;
305int QskObjectCounter::destroyed( ObjectType objectType )
const
307 return m_data->counterData.counter[ objectType ].destroyed;
310int QskObjectCounter::current( ObjectType objectType )
const
312 return m_data->counterData.counter[ objectType ].current;
315int QskObjectCounter::maximum( ObjectType objectType )
const
317 return m_data->counterData.counter[ objectType ].maximum;
320void QskObjectCounter::debugStatistics( QDebug debug, ObjectType objectType )
const
322 const auto& c = m_data->counterData.counter[ objectType ];
324 QDebugStateSaver saver( debug );
327 debug <<
"created: " << c.created
328 <<
", destroyed: " << c.destroyed
329 <<
", current: " << c.current
330 <<
", maximum: " << c.maximum;
334 if ( objectType == Objects )
336 const auto& objectTable = m_data->counterData.objectTable;
338 if ( !objectTable.isEmpty() )
340 debug <<
"\n\t=== Leaks ===\n";
341 for (
const auto object : objectTable )
343 debug <<
"\tClass: " <<
object->metaObject()->className();
344 if ( !object->objectName().isEmpty() )
345 debug <<
" Name: " <<
object->objectName();
353void QskObjectCounter::dump()
const
355 QDebug debug = qDebug();
357 QDebugStateSaver saver( debug );
361 debug <<
"* Statistics\n";
362 debug <<
" Objects: ";
363 debugStatistics( debug, Objects );
365 debug <<
"\n Items: ";
366 debugStatistics( debug, Items );
369#ifndef QT_NO_DEBUG_STREAM
373 counter.debugStatistics( debug, QskObjectCounter::Objects );