File indexing completed on 2024-04-14 05:35:53
0001 /* 0002 * Copyright 2014-2017 by Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 * 0004 * This program is free software; you can redistribute it and/or modify 0005 * it under the terms of the GNU Library General Public License as 0006 * published by the Free Software Foundation; either version 2, or 0007 * (at your option) any later version. 0008 * 0009 * This program is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 * GNU General Public License for more details 0013 * 0014 * You should have received a copy of the GNU Library General Public 0015 * License along with this program; if not, write to the 0016 * Free Software Foundation, Inc., 0017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "objecttimetracker.h" 0021 #include <QDateTime> 0022 #include <qmetaobject.h> 0023 #include <QFile> 0024 #include <QString> 0025 #include <QDebug> 0026 #include <QJsonDocument> 0027 #include <QJsonArray> 0028 #include <QJsonObject> 0029 #include <QTimer> 0030 #include <QChildEvent> 0031 #include <QCoreApplication> 0032 0033 #include "objectwatcher.h" 0034 0035 class ObjectHistory 0036 { 0037 public: 0038 QString name; 0039 QJsonObject initial; 0040 QVector<QJsonObject> events; 0041 }; 0042 0043 Q_GLOBAL_STATIC_WITH_ARGS(const qint64, s_beginning, (QDateTime::currentDateTime().toMSecsSinceEpoch())) 0044 0045 struct TimeTrackerWriter : QObject { 0046 Q_OBJECT 0047 public: 0048 TimeTrackerWriter() { 0049 QObject::connect(QCoreApplication::instance(), &QCoreApplication::destroyed, this, &TimeTrackerWriter::print); 0050 } 0051 0052 void print() const { 0053 QJsonArray array; 0054 0055 Q_FOREACH(const ObjectHistory& history, m_data) { 0056 array.append(QJsonObject { 0057 { QStringLiteral("name"), history.name }, 0058 { QStringLiteral("events"), serializeEvents(history.events) }, 0059 { QStringLiteral("initial"), QJsonValue::fromVariant(history.initial) } 0060 }); 0061 } 0062 Q_ASSERT(array.count() == m_data.count()); 0063 QJsonDocument doc; 0064 doc.setArray(array); 0065 0066 QFile f(QStringLiteral("/tmp/debug-") + QCoreApplication::instance()->applicationName() + QLatin1Char('-') + QString::fromUtf8(qgetenv("USER")) + QStringLiteral(".json")); 0067 bool b = f.open(QFile::WriteOnly); 0068 Q_ASSERT(b); 0069 f.write(doc.toJson()); 0070 } 0071 0072 void feed(QObject* obj, const ObjectHistory& tracker) 0073 { 0074 m_data[obj] = tracker; 0075 } 0076 0077 QHash<QObject*, ObjectHistory> m_data; 0078 0079 private: 0080 QJsonArray serializeEvents(const QVector<QJsonObject>& events) const { 0081 QJsonArray ret; 0082 for(const auto& ev: events) { 0083 ret.append(ev); 0084 } 0085 Q_ASSERT(ret.count() == events.count()); 0086 return ret; 0087 } 0088 }; 0089 Q_GLOBAL_STATIC(TimeTrackerWriter, s_writer) 0090 0091 static QString variantToString(const QVariant &v) 0092 { 0093 QString val; 0094 QDebug d(&val); 0095 d << v; 0096 return val; 0097 } 0098 0099 ObjectTimeTracker::ObjectTimeTracker(QObject* o) 0100 : QObject() 0101 , m_object(o) 0102 , m_history(new ObjectHistory) 0103 { 0104 *s_beginning * 1; // ensure it's initialized 0105 0106 QTimer* t = new QTimer(this); 0107 t->setInterval(2000); 0108 t->setSingleShot(false); 0109 connect(t, &QTimer::timeout, this, &ObjectTimeTracker::sync); 0110 t->start(); 0111 0112 connect(o, &QObject::destroyed, this, &ObjectTimeTracker::objectDestroyed); 0113 0114 auto mo = m_object->metaObject(); 0115 m_history->name = QString::fromUtf8(mo->className()); 0116 m_history->events.append(QJsonObject { 0117 { QStringLiteral("time"), QDateTime::currentDateTime().toMSecsSinceEpoch() - *s_beginning }, 0118 { QStringLiteral("type"), QStringLiteral("constructor") }, 0119 { QStringLiteral("name"), {} }, 0120 { QStringLiteral("value"), m_object->objectName() } 0121 }); 0122 0123 QMetaMethod propChange = metaObject()->method(metaObject()->indexOfSlot("propertyChanged()")); 0124 Q_ASSERT(propChange.isValid() && metaObject()->indexOfSlot("propertyChanged()")>=0); 0125 0126 for (int i = 0, pc = mo->propertyCount(); i<pc; ++i) { 0127 QMetaProperty prop = mo->property(i); 0128 m_history->initial[QLatin1String(prop.name())] = variantToString(prop.read(o)); 0129 0130 if (prop.hasNotifySignal()) 0131 connect(o, prop.notifySignal(), this, propChange); 0132 } 0133 } 0134 0135 ObjectTimeTracker::~ObjectTimeTracker() 0136 { 0137 sync(); 0138 } 0139 0140 void ObjectTimeTracker::objectDestroyed() 0141 { 0142 m_history->events.append(QJsonObject { 0143 { QStringLiteral("time"), QDateTime::currentDateTime().toMSecsSinceEpoch() - *s_beginning }, 0144 { QStringLiteral("type"), QStringLiteral("destroyed") } 0145 }); 0146 deleteLater(); 0147 } 0148 0149 void ObjectTimeTracker::sync() 0150 { 0151 s_writer->feed(m_object, *m_history); 0152 } 0153 0154 void ObjectTimeTracker::propertyChanged() 0155 { 0156 Q_ASSERT(sender() == m_object); 0157 0158 const QMetaObject* mo = m_object->metaObject(); 0159 0160 for (int i = 0, pc = mo->propertyCount(); i<pc; ++i) { 0161 const QMetaProperty prop = mo->property(i); 0162 if (prop.notifySignalIndex() == senderSignalIndex()) { 0163 0164 m_history->events.append(QJsonObject { 0165 { QStringLiteral("time"), QDateTime::currentDateTime().toMSecsSinceEpoch() - *s_beginning }, 0166 { QStringLiteral("type"), QStringLiteral("property") }, 0167 { QStringLiteral("name"), QString::fromLatin1(prop.name()) }, 0168 { QStringLiteral("value"), variantToString(prop.read(m_object)) } 0169 }); 0170 } 0171 } 0172 } 0173 0174 #include "objecttimetracker.moc"