Introduce TimeTracker debugging tool for plasma
TimeTracker is a small class that keeps track of an object the changes in the properties of an object and relates them to a point in time. This way we can see how things change and react to each other. Then, this information is exported into a json file to analyze. REVIEW: 119199
This commit is contained in:
parent
8afadc6410
commit
92a8007489
@ -50,6 +50,7 @@ set(Plasma_LIB_SRCS
|
||||
private/applet_p.cpp
|
||||
private/associatedapplicationmanager.cpp
|
||||
private/containment_p.cpp
|
||||
private/timetracker.cpp
|
||||
|
||||
#Dataengines, services
|
||||
datacontainer.cpp
|
||||
|
@ -65,6 +65,12 @@ class Package;
|
||||
class PLASMA_EXPORT Applet : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Types::ItemStatus status READ status WRITE setStatus NOTIFY statusChanged)
|
||||
Q_PROPERTY(Types::ImmutabilityType immutability READ immutability WRITE setImmutability NOTIFY immutabilityChanged)
|
||||
Q_PROPERTY(Types::FormFactor formFactor READ formFactor NOTIFY formFactorChanged)
|
||||
Q_PROPERTY(Types::Location location READ location NOTIFY locationChanged)
|
||||
Q_PROPERTY(QString title READ title WRITE setTitle FINAL)
|
||||
Q_PROPERTY(QString icon READ icon WRITE setIcon FINAL)
|
||||
|
||||
public:
|
||||
//CONSTRUCTORS
|
||||
|
@ -59,6 +59,7 @@ class PLASMA_EXPORT Containment : public Applet
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString wallpaper READ wallpaper WRITE setWallpaper NOTIFY wallpaperChanged)
|
||||
Q_PROPERTY(bool isUiReady READ isUiReady NOTIFY uiReadyChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "scripting/scriptengine.h"
|
||||
#include "scripting/appletscript.h"
|
||||
#include "private/containment_p.h"
|
||||
#include "timetracker.h"
|
||||
|
||||
namespace Plasma
|
||||
{
|
||||
@ -76,6 +77,9 @@ AppletPrivate::AppletPrivate(KService::Ptr service, const KPluginInfo *info, int
|
||||
}
|
||||
QObject::connect(actions->action("configure"), SIGNAL(triggered()),
|
||||
q, SLOT(requestConfiguration()));
|
||||
#ifndef NDEBUG
|
||||
new TimeTracker(q);
|
||||
#endif
|
||||
}
|
||||
|
||||
AppletPrivate::~AppletPrivate()
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "pluginloader.h"
|
||||
|
||||
#include "private/applet_p.h"
|
||||
#include "timetracker.h"
|
||||
|
||||
namespace Plasma
|
||||
{
|
||||
@ -53,6 +54,10 @@ ContainmentPrivate::ContainmentPrivate(Containment *c):
|
||||
if (appletParent) {
|
||||
QObject::connect(appletParent->containment(), &Containment::screenChanged, c, &Containment::screenChanged);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
new TimeTracker(q);
|
||||
#endif
|
||||
}
|
||||
|
||||
Plasma::ContainmentPrivate::~ContainmentPrivate()
|
||||
|
142
src/plasma/private/timetracker.cpp
Normal file
142
src/plasma/private/timetracker.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2014 by Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "timetracker.h"
|
||||
#include <containment.h>
|
||||
#include <QDateTime>
|
||||
#include <qmetaobject.h>
|
||||
#include <QFile>
|
||||
#include <qjsondocument.h>
|
||||
#include <qjsonarray.h>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace Plasma;
|
||||
|
||||
struct TimeTrackerWriter : QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TimeTrackerWriter() {
|
||||
QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, static_cast<void (TimeTrackerWriter::*)()>(&TimeTrackerWriter::print));
|
||||
}
|
||||
|
||||
void print() {
|
||||
QJsonArray array;
|
||||
|
||||
Q_FOREACH(const ObjectHistory& history, m_data) {
|
||||
QVariantMap map;
|
||||
map["events"] = serializeEvents(history.events);
|
||||
map["initial"] = history.initial;
|
||||
|
||||
array.append(QJsonValue::fromVariant(map));
|
||||
}
|
||||
Q_ASSERT(array.count() == m_data.count());
|
||||
QJsonDocument doc;
|
||||
doc.setArray(array);
|
||||
|
||||
QFile f(QStringLiteral("/tmp/debug-")+qgetenv("USER")+".json");
|
||||
bool b = f.open(QFile::WriteOnly);
|
||||
Q_ASSERT(b);
|
||||
f.write(doc.toJson());
|
||||
}
|
||||
|
||||
void feed(QObject* obj, const ObjectHistory& tracker)
|
||||
{
|
||||
m_data[obj] = tracker;
|
||||
}
|
||||
|
||||
QHash<QObject*, ObjectHistory> m_data;
|
||||
|
||||
private:
|
||||
QVariantList serializeEvents(const QVector<TimeEvent>& events) const {
|
||||
QVariantList ret;
|
||||
Q_ASSERT(!events.isEmpty());
|
||||
foreach(const TimeEvent& ev, events) {
|
||||
QVariantMap map;
|
||||
map["comment"] = ev.comment;
|
||||
map["time"] = ev.moment.toMSecsSinceEpoch();
|
||||
ret.append(map);
|
||||
}
|
||||
Q_ASSERT(ret.count() == events.count());
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
Q_GLOBAL_STATIC(TimeTrackerWriter, s_writer);
|
||||
|
||||
TimeTracker::TimeTracker(QObject* o)
|
||||
: QObject(o)
|
||||
{
|
||||
m_history.events.append(TimeEvent { QDateTime::currentDateTime(), QString("constructed %1 %2").arg(o->metaObject()->className()).arg(o->objectName()) });
|
||||
|
||||
QTimer* t = new QTimer(this);
|
||||
t->setInterval(2000);
|
||||
t->setSingleShot(false);
|
||||
connect(t, SIGNAL(timeout()), this, SLOT(sync()));
|
||||
t->start();
|
||||
|
||||
QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void TimeTracker::init()
|
||||
{
|
||||
QMetaMethod propChange = metaObject()->method(metaObject()->indexOfSlot("propertyChanged()"));
|
||||
Q_ASSERT(propChange.isValid() && metaObject()->indexOfSlot("propertyChanged()")>=0);
|
||||
|
||||
QObject* o = parent();
|
||||
for (int i = 0, pc = o->metaObject()->propertyCount(); i<pc; ++i) {
|
||||
QMetaProperty prop = o->metaObject()->property(i);
|
||||
if (prop.isFinal() || prop.isConstant())
|
||||
m_history.initial[prop.name()] = prop.read(o);
|
||||
|
||||
if (prop.hasNotifySignal())
|
||||
connect(o, prop.notifySignal(), this, propChange);
|
||||
}
|
||||
}
|
||||
|
||||
TimeTracker::~TimeTracker()
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
void TimeTracker::sync()
|
||||
{
|
||||
s_writer->feed(parent(), m_history);
|
||||
}
|
||||
|
||||
void TimeTracker::propertyChanged()
|
||||
{
|
||||
Q_ASSERT(sender() == parent());
|
||||
|
||||
const QMetaObject* mo = parent()->metaObject();
|
||||
for (int i = 0, pc = mo->propertyCount(); i<pc; ++i) {
|
||||
QMetaProperty prop = mo->property(i);
|
||||
if (prop.notifySignalIndex() == senderSignalIndex()) {
|
||||
QString val;
|
||||
QVariant var = prop.read(parent());
|
||||
if(var.canConvert<QString>()) {
|
||||
val = var.toString();
|
||||
} else {
|
||||
val = QString("<unknown %1>").arg(var.typeName());
|
||||
}
|
||||
m_history.events.append(TimeEvent { QDateTime::currentDateTime(), QString("property %1 changed to %2").arg(prop.name()).arg(val)});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "timetracker.moc"
|
77
src/plasma/private/timetracker.h
Normal file
77
src/plasma/private/timetracker.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2014 by Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TIMETRACKER_H
|
||||
#define TIMETRACKER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
#include <QDateTime>
|
||||
#include <QVector>
|
||||
|
||||
#include "plasma/plasma_export.h"
|
||||
|
||||
namespace Plasma
|
||||
{
|
||||
class Containment;
|
||||
class Applet;
|
||||
|
||||
struct TimeEvent
|
||||
{
|
||||
QDateTime moment;
|
||||
QString comment;
|
||||
};
|
||||
|
||||
struct ObjectHistory
|
||||
{
|
||||
QVariantMap initial;
|
||||
QVector<TimeEvent> events;
|
||||
};
|
||||
|
||||
/**
|
||||
* This debugging class is meant to provide an overview of how the objects change
|
||||
* over time and hopefully provide the information required to detect buggy initialization.
|
||||
*
|
||||
* To use it, you'll pass the object you want to track to the constructor and the TimeTracker
|
||||
* will use Qt introspection to read the properties and check what values they have and how they
|
||||
* change.
|
||||
*
|
||||
* To analyze the results one can read the generated json file /tmp/debug-$USER, as soon
|
||||
* as the process has quit.
|
||||
*/
|
||||
|
||||
class PLASMA_EXPORT TimeTracker : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TimeTracker(QObject* applet);
|
||||
virtual ~TimeTracker();
|
||||
|
||||
private Q_SLOTS:
|
||||
void init();
|
||||
void sync();
|
||||
void propertyChanged();
|
||||
|
||||
private:
|
||||
ObjectHistory m_history;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMETRACKER_H
|
Loading…
Reference in New Issue
Block a user