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"