qskinny/src/common/QskObjectCounter.cpp

378 lines
8.7 KiB
C++
Raw Normal View History

2017-07-21 16:21:34 +00: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"
2018-07-19 12:10:48 +00:00
#include <qdebug.h>
2018-08-03 06:15:28 +00:00
#include <qset.h>
2017-07-21 16:21:34 +00:00
QSK_QT_PRIVATE_BEGIN
#include <private/qhooks_p.h>
#include <private/qobject_p.h>
2018-08-03 06:15:28 +00:00
#include <private/qquickitem_p.h>
2017-07-21 16:21:34 +00:00
QSK_QT_PRIVATE_END
2021-10-07 15:47:15 +00:00
#define QSK_OBJECT_INFO 0
#if QSK_OBJECT_INFO
2021-10-08 05:47:00 +00:00
#include <qset.h>
2021-10-07 15:47:15 +00:00
#endif
2017-07-21 16:21:34 +00:00
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.
2018-02-07 14:40:05 +00:00
So we can't cast the object itself and also have to rely on
RTTI being enabled. TODO ...
*/
2017-07-21 16:21:34 +00:00
return dynamic_cast< QQuickItemPrivate* >( o_p ) != nullptr;
}
2021-10-07 11:49:13 +00:00
namespace
2017-07-21 16:21:34 +00:00
{
2021-10-07 11:49:13 +00:00
class Counter
2018-02-07 14:40:05 +00:00
{
2021-10-07 11:49:13 +00:00
public:
Counter()
{
reset();
}
2018-02-07 14:40:05 +00:00
2021-10-07 11:49:13 +00:00
void reset()
{
created = destroyed = current = maximum = 0;
}
2018-02-07 14:40:05 +00:00
2021-10-07 11:49:13 +00:00
void increment()
{
created++;
current++;
2018-02-07 14:40:05 +00:00
2021-10-07 11:49:13 +00:00
if ( current > maximum )
maximum = current;
}
2017-07-21 16:21:34 +00:00
2021-10-07 11:49:13 +00:00
void decrement()
{
destroyed++;
current--;
}
2017-07-21 16:21:34 +00:00
2021-10-07 11:49:13 +00:00
int created;
int destroyed;
int current;
int maximum;
};
2018-02-07 14:40:05 +00:00
2022-03-24 07:27:17 +00:00
class CounterData
2021-10-07 15:47:15 +00:00
{
public:
Counter counter[ 2 ];
#if QSK_OBJECT_INFO
2021-10-08 05:47:00 +00:00
QSet< const QObject* > objectTable;
2021-10-07 15:47:15 +00:00
#endif
};
2021-10-07 11:49:13 +00:00
class CounterHook
2017-07-21 16:21:34 +00:00
{
2021-10-07 11:49:13 +00:00
public:
CounterHook();
~CounterHook();
2021-10-07 15:47:15 +00:00
void registerCounters( CounterData*, bool on );
bool isCountersRegistered( const CounterData* ) const;
2021-10-07 11:49:13 +00:00
bool isActive() const;
void startup();
void addObject( QObject* );
void removeObject( QObject* );
static void cleanupHook();
private:
static void startupHook();
static void addObjectHook( QObject* );
static void removeObjectHook( QObject* );
2021-10-07 15:47:15 +00:00
QSet< CounterData* > m_counterDataSet;
2021-10-07 11:49:13 +00:00
quintptr m_otherStartup;
quintptr m_otherAddObject;
quintptr m_otherRemoveObject;
};
}
static bool qskAutoDeleteHook = false;
static CounterHook* qskCounterHook = nullptr;
CounterHook::CounterHook()
{
m_otherStartup = qtHookData[ QHooks::Startup ];
m_otherAddObject = qtHookData[ QHooks::AddQObject ];
m_otherRemoveObject = qtHookData[ QHooks::RemoveQObject ];
qtHookData[ QHooks::Startup ] = reinterpret_cast< quintptr >( &startupHook );
qtHookData[ QHooks::AddQObject ] = reinterpret_cast< quintptr >( &addObjectHook );
qtHookData[ QHooks::RemoveQObject ] = reinterpret_cast< quintptr >( &removeObjectHook );
}
CounterHook::~CounterHook()
{
qtHookData[ QHooks::Startup ] = m_otherStartup;
qtHookData[ QHooks::AddQObject ] = m_otherAddObject;
qtHookData[ QHooks::RemoveQObject ] = m_otherRemoveObject;
}
2021-10-07 15:47:15 +00:00
void CounterHook::registerCounters( CounterData* data, bool on )
2021-10-07 11:49:13 +00:00
{
if ( on )
2021-10-07 15:47:15 +00:00
m_counterDataSet.insert( data );
2021-10-07 11:49:13 +00:00
else
2021-10-07 15:47:15 +00:00
m_counterDataSet.remove( data );
2021-10-07 11:49:13 +00:00
}
2021-10-07 15:47:15 +00:00
bool CounterHook::isCountersRegistered( const CounterData* data ) const
2021-10-07 11:49:13 +00:00
{
2021-10-07 15:47:15 +00:00
return m_counterDataSet.contains( const_cast< CounterData* >( data ) );
2021-10-07 11:49:13 +00:00
}
bool CounterHook::isActive() const
{
2021-10-07 15:47:15 +00:00
return !m_counterDataSet.isEmpty();
2021-10-07 11:49:13 +00:00
}
void CounterHook::startup()
{
2017-07-21 16:21:34 +00:00
#if 0
2021-10-07 11:49:13 +00:00
qDebug() << "** QskObjectCounterHook enabled";
2017-07-21 16:21:34 +00:00
#endif
2021-10-07 11:49:13 +00:00
if ( m_otherStartup )
reinterpret_cast< QHooks::StartupCallback >( m_otherStartup )();
}
2018-02-07 14:40:05 +00:00
2021-10-07 11:49:13 +00:00
void CounterHook::addObject( QObject* object )
{
const bool isItem = qskIsItem( object );
2017-07-21 16:21:34 +00:00
2022-03-08 10:53:46 +00:00
for ( auto counterData : qAsConst( m_counterDataSet ) )
2017-07-21 16:21:34 +00:00
{
2021-10-07 15:47:15 +00:00
counterData->counter[ QskObjectCounter::Objects ].increment();
2018-02-07 14:40:05 +00:00
2021-10-07 11:49:13 +00:00
if ( isItem )
2021-10-07 15:47:15 +00:00
counterData->counter[ QskObjectCounter::Items ].increment();
#if QSK_OBJECT_INFO
2021-10-08 05:47:00 +00:00
counterData->objectTable.insert( object );
2021-10-07 15:47:15 +00:00
#endif
2017-07-21 16:21:34 +00:00
}
2021-10-07 11:49:13 +00:00
if ( m_otherAddObject )
reinterpret_cast< QHooks::AddQObjectCallback >( m_otherAddObject )( object );
}
2018-02-07 14:40:05 +00:00
2021-10-07 11:49:13 +00:00
void CounterHook::removeObject( QObject* object )
{
const bool isItem = qskIsItem( object );
2018-02-07 14:40:05 +00:00
2022-03-08 10:53:46 +00:00
for ( auto counterData : qAsConst( m_counterDataSet ) )
2021-10-07 11:49:13 +00:00
{
2021-10-07 15:47:15 +00:00
counterData->counter[ QskObjectCounter::Objects ].decrement();
2017-07-21 16:21:34 +00:00
2021-10-07 11:49:13 +00:00
if ( isItem )
2021-10-07 15:47:15 +00:00
counterData->counter[ QskObjectCounter::Items ].decrement();
#if QSK_OBJECT_INFO
2021-10-08 05:47:00 +00:00
counterData->objectTable.remove( object );
2021-10-07 15:47:15 +00:00
#endif
2021-10-07 11:49:13 +00:00
}
if ( m_otherRemoveObject )
reinterpret_cast< QHooks::RemoveQObjectCallback >( m_otherRemoveObject )( object );
}
2017-07-21 16:21:34 +00:00
2021-10-07 11:49:13 +00:00
void CounterHook::startupHook()
2017-07-21 16:21:34 +00:00
{
2018-02-07 14:40:05 +00:00
if ( qskCounterHook )
qskCounterHook->startup();
2017-07-21 16:21:34 +00:00
}
2021-10-07 11:49:13 +00:00
void CounterHook::addObjectHook( QObject* object )
2017-07-21 16:21:34 +00:00
{
2018-02-07 14:40:05 +00:00
if ( qskCounterHook )
qskCounterHook->addObject( object );
2017-07-21 16:21:34 +00:00
}
2021-10-07 11:49:13 +00:00
void CounterHook::removeObjectHook( QObject* object )
2017-07-21 16:21:34 +00:00
{
2018-02-07 14:40:05 +00:00
if ( qskCounterHook )
qskCounterHook->removeObject( object );
2017-07-21 16:21:34 +00:00
}
2021-10-07 11:49:13 +00:00
void CounterHook::cleanupHook()
2017-07-21 16:21:34 +00:00
{
2018-02-07 14:40:05 +00:00
if ( qskCounterHook && !qskCounterHook->isActive() )
2017-07-21 16:21:34 +00:00
{
2018-02-07 14:40:05 +00:00
delete qskCounterHook;
qskCounterHook = nullptr;
2017-07-21 16:21:34 +00:00
}
2018-02-07 14:40:05 +00:00
// From now on we remove the hooks as soon as there are no counters
2021-10-07 11:49:13 +00:00
qskAutoDeleteHook = true;
2017-07-21 16:21:34 +00:00
}
2018-02-07 14:40:05 +00:00
static void qskInstallCleanupHookHandler()
{
2021-10-07 11:49:13 +00:00
qAddPostRoutine( CounterHook::cleanupHook );
2018-02-07 14:40:05 +00:00
}
Q_COREAPP_STARTUP_FUNCTION( qskInstallCleanupHookHandler )
2021-10-07 11:49:13 +00:00
class QskObjectCounter::PrivateData
{
public:
PrivateData( bool debugAtDestruction )
: debugAtDestruction( debugAtDestruction )
{
}
2021-10-07 15:47:15 +00:00
CounterData counterData;
2021-10-07 11:49:13 +00:00
const bool debugAtDestruction;
};
2018-08-03 06:15:28 +00:00
QskObjectCounter::QskObjectCounter( bool debugAtDestruction )
2021-10-07 11:49:13 +00:00
: m_data( new PrivateData( debugAtDestruction ) )
2017-07-21 16:21:34 +00:00
{
setActive( true );
}
QskObjectCounter::~QskObjectCounter()
{
setActive( false );
2021-10-07 11:49:13 +00:00
if ( m_data->debugAtDestruction )
2017-07-21 16:21:34 +00:00
dump();
}
void QskObjectCounter::setActive( bool on )
{
2018-02-07 14:40:05 +00:00
if ( on )
{
if ( qskCounterHook == nullptr )
2021-10-07 11:49:13 +00:00
qskCounterHook = new CounterHook();
2018-02-07 14:40:05 +00:00
2021-10-07 15:47:15 +00:00
qskCounterHook->registerCounters( &m_data->counterData, on );
2018-02-07 14:40:05 +00:00
}
else
{
2021-10-07 15:47:15 +00:00
qskCounterHook->registerCounters( &m_data->counterData, on );
2018-02-07 14:40:05 +00:00
if ( !qskCounterHook->isActive() )
{
2021-10-07 11:49:13 +00:00
if ( qskAutoDeleteHook )
2018-02-07 14:40:05 +00:00
{
delete qskCounterHook;
qskCounterHook = nullptr;
}
}
}
2017-07-21 16:21:34 +00:00
}
bool QskObjectCounter::isActive() const
{
2021-10-07 15:47:15 +00:00
return qskCounterHook && qskCounterHook->isCountersRegistered( &m_data->counterData );
2017-07-21 16:21:34 +00:00
}
void QskObjectCounter::reset()
{
2021-10-07 15:47:15 +00:00
auto& counters = m_data->counterData.counter;
counters[ Objects ].reset();
counters[ Items ].reset();
2017-07-21 16:21:34 +00:00
}
int QskObjectCounter::created( ObjectType objectType ) const
{
2021-10-07 15:47:15 +00:00
return m_data->counterData.counter[ objectType ].created;
2017-07-21 16:21:34 +00:00
}
int QskObjectCounter::destroyed( ObjectType objectType ) const
{
2021-10-07 15:47:15 +00:00
return m_data->counterData.counter[ objectType ].destroyed;
2017-07-21 16:21:34 +00:00
}
int QskObjectCounter::current( ObjectType objectType ) const
{
2021-10-07 15:47:15 +00:00
return m_data->counterData.counter[ objectType ].current;
2017-07-21 16:21:34 +00:00
}
int QskObjectCounter::maximum( ObjectType objectType ) const
{
2021-10-07 15:47:15 +00:00
return m_data->counterData.counter[ objectType ].maximum;
2017-07-21 16:21:34 +00:00
}
void QskObjectCounter::debugStatistics( QDebug debug, ObjectType objectType ) const
{
2021-10-07 15:47:15 +00:00
const auto& c = m_data->counterData.counter[ objectType ];
2017-07-21 16:21:34 +00:00
QDebugStateSaver saver( debug );
debug.nospace();
debug << '(';
debug << "created: " << c.created
<< ", destroyed: " << c.destroyed
<< ", current: " << c.current
<< ", maximum: " << c.maximum;
debug << ')';
2021-10-07 15:47:15 +00:00
#if QSK_OBJECT_INFO
if ( objectType == Objects )
{
const auto& objectTable = m_data->counterData.objectTable;
if ( !objectTable.isEmpty() )
{
debug << "\n\t=== Leaks ===\n";
2021-10-08 05:47:00 +00:00
for ( const auto object : objectTable )
2021-10-07 15:47:15 +00:00
{
2021-10-08 05:47:00 +00:00
debug << "\tClass: " << object->metaObject()->className();
if ( !object->objectName().isEmpty() )
2021-12-23 18:05:59 +00:00
debug << " Name: " << object->objectName();
2021-10-07 15:47:15 +00:00
debug << '\n';
}
}
}
#endif
2017-07-21 16:21:34 +00:00
}
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