Commit 9597adca authored by Milian Wolff's avatar Milian Wolff

Add a list of queries to the top of the query debugger.

This list aggregates the query runtime and number of calls to the
query. This is much more useful, imo, than the text view below.

Note that this patch also disables the text view by default, as it
can easily lead to out-of-memory issues. Blindly appending lots
of text is bound to fail, sooner or later.

The context menu is replaced by a tool button. This makes it also
possible to search in the text edit, and to copy text from it.
Much more useful.

Add average duration and aggregated totals row.

REVIEW: 121307
parent 95ac4909
......@@ -23,6 +23,12 @@
#include <QtGui/QCheckBox>
#include <QtGui/QMenu>
#include <QToolButton>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include <QTreeView>
#include <QHeaderView>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusArgument>
#include <QtDBus/QDBusMetaType>
......@@ -36,9 +42,163 @@
#include <KGlobalSettings>
#include <KLocalizedString>
#include <KDebug>
#include <KIcon>
#include <algorithm>
Q_DECLARE_METATYPE(QList< QList<QVariant> >)
struct QueryInfo
{
QString query;
quint64 duration;
quint64 calls;
bool operator<(const QString& other) const
{
return query < other;
}
};
Q_DECLARE_TYPEINFO(QueryInfo, Q_MOVABLE_TYPE);
class QueryDebuggerModel : public QAbstractListModel
{
Q_OBJECT
public:
QueryDebuggerModel(QObject* parent)
: QAbstractListModel(parent)
{
mSpecialRows[TOTAL].query = "TOTAL";
mSpecialRows[TOTAL].duration = 0;
mSpecialRows[TOTAL].calls = 0;
}
enum SPECIAL_ROWS {
TOTAL,
NUM_SPECIAL_ROWS
};
enum Colums {
DurationColumn,
CallsColumn,
AvgDurationColumn,
QueryColumn,
NUM_COLUMNS
};
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const
{
if (orientation == Qt::Vertical || section < 0 || section >= NUM_COLUMNS || role != Qt::DisplayRole) {
return QVariant();
}
if (section == QueryColumn) {
return "Query";
} else if (section == DurationColumn) {
return "Duration [ms]";
} else if (section == CallsColumn) {
return "Calls";
} else if (section == AvgDurationColumn) {
return "Avg. Duration [ms]";
}
return QVariant();
}
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
{
if (role != Qt::DisplayRole && role != Qt::ToolTipRole) {
return QVariant();
}
const int row = index.row();
if (row < 0 || row >= rowCount(index.parent())) {
return QVariant();
}
const int column = index.column();
if (column < 0 || column >= NUM_COLUMNS) {
return QVariant();
}
const QueryInfo& info = (row < NUM_SPECIAL_ROWS)
? mSpecialRows[row]
: mQueries.at(row - NUM_SPECIAL_ROWS);
if (role == Qt::ToolTipRole) {
return QString(QLatin1String("<qt>") + info.query + QLatin1String("</qt>"));
}
if (column == QueryColumn) {
return info.query;
} else if (column == DurationColumn) {
return info.duration;
} else if (column == CallsColumn) {
return info.calls;
} else if (column == AvgDurationColumn) {
return float(info.duration) / info.calls;
}
return QVariant();
}
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const
{
if (!parent.isValid()) {
return mQueries.size() + NUM_SPECIAL_ROWS;
} else {
return 0;
}
}
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const
{
if (!parent.isValid()) {
return NUM_COLUMNS;
} else {
return 0;
}
}
void addQuery(const QString& query, uint duration)
{
QVector<QueryInfo>::iterator it = std::lower_bound(mQueries.begin(), mQueries.end(), query);
const int row = std::distance(mQueries.begin(), it) + NUM_SPECIAL_ROWS;
if (it != mQueries.end() && it->query == query) {
++(it->calls);
it->duration += duration;
emit dataChanged(index(row, DurationColumn), index(row, AvgDurationColumn));
} else {
beginInsertRows(QModelIndex(), row, row);
QueryInfo info;
info.query = query;
info.duration = duration;
info.calls = 1;
mQueries.insert(it, info);
endInsertRows();
}
mSpecialRows[TOTAL].duration += duration;
++mSpecialRows[TOTAL].calls;
emit dataChanged(index(TOTAL, DurationColumn), index(TOTAL, AvgDurationColumn));
}
void clear()
{
beginResetModel();
mQueries.clear();
mSpecialRows[TOTAL].duration = 0;
mSpecialRows[TOTAL].calls = 0;
endResetModel();
}
private:
QVector<QueryInfo> mQueries;
QueryInfo mSpecialRows[NUM_SPECIAL_ROWS];
};
QueryDebugger::QueryDebugger( QWidget* parent ):
QWidget( parent )
{
......@@ -56,20 +216,49 @@ QueryDebugger::QueryDebugger( QWidget* parent ):
QVBoxLayout *layout = new QVBoxLayout( this );
QHBoxLayout *checkBoxLayout = new QHBoxLayout( this );
QCheckBox* enableCB = new QCheckBox( this );
enableCB->setText( "Enable query debugger (slows down server!)");
enableCB->setChecked( mDebugger->isSQLDebuggingEnabled() );
connect( enableCB, SIGNAL(toggled(bool)), mDebugger, SLOT(enableSQLDebugging(bool)) );
layout->addWidget(enableCB);
checkBoxLayout->addWidget( enableCB );
mOnlyAggregate = new QCheckBox( this );
mOnlyAggregate->setText( "Only Aggregate data");
mOnlyAggregate->setChecked( true );
checkBoxLayout->addWidget( mOnlyAggregate );
QToolButton *clearButton = new QToolButton;
clearButton->setText( "clear" );
clearButton->setIcon( KIcon( "edit-clear-list" ) );
clearButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
connect( clearButton, SIGNAL(clicked()), SLOT(clear()) );
checkBoxLayout->addWidget( clearButton );
layout->addLayout( checkBoxLayout );
QTreeView* queryList = new QTreeView( this );
mModel = new QueryDebuggerModel( this );
QSortFilterProxyModel* proxy = new QSortFilterProxyModel( this );
proxy->setSourceModel( mModel );
proxy->setDynamicSortFilter( true );
queryList->setModel( proxy );
queryList->setRootIsDecorated( false );
queryList->setSortingEnabled( true );
queryList->setUniformRowHeights( true );
queryList->header()->setResizeMode( QueryDebuggerModel::CallsColumn, QHeaderView::Fixed );
queryList->header()->setResizeMode( QueryDebuggerModel::DurationColumn, QHeaderView::Fixed );
queryList->header()->setResizeMode( QueryDebuggerModel::AvgDurationColumn, QHeaderView::Fixed );
queryList->header()->setResizeMode( QueryDebuggerModel::QueryColumn, QHeaderView::ResizeToContents );
layout->addWidget( queryList );
mView = new KTextEdit( this );
mView->setReadOnly( true );
mView->setContextMenuPolicy( Qt::CustomContextMenu );
mView->setFont( KGlobalSettings::fixedFont() );
layout->addWidget( mView );
connect( mView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(contextMenu(QPoint)) );
Akonadi::Control::widgetNeedsAkonadi( this );
}
......@@ -80,11 +269,10 @@ QueryDebugger::~QueryDebugger()
mDebugger->enableSQLDebugging( false );
}
void QueryDebugger::contextMenu( const QPoint &pos )
void QueryDebugger::clear()
{
QMenu menu;
menu.addAction( i18n( "Clear View" ), mView, SLOT(clear()) );
menu.exec( mapToGlobal( pos ) );
mView->clear();
mModel->clear();
}
void QueryDebugger::addQuery( double sequence, uint duration, const QString &query,
......@@ -92,6 +280,12 @@ void QueryDebugger::addQuery( double sequence, uint duration, const QString &que
int resultsCount, const QList<QList<QVariant> > &result,
const QString &error )
{
mModel->addQuery(query, duration);
if (mOnlyAggregate->isChecked()) {
return;
}
QString q = query;
const QStringList keys = values.uniqueKeys();
Q_FOREACH ( const QString &key, keys ) {
......@@ -163,5 +357,4 @@ QString QueryDebugger::variantToString( const QVariant &val )
return QString();
}
#include "querydebugger.moc"
......@@ -27,6 +27,8 @@
#include "storagedebuggerinterface.h"
class KTextEdit;
class QCheckBox;
class QueryDebuggerModel;
class QueryDebugger : public QWidget
{
......@@ -37,10 +39,10 @@ class QueryDebugger : public QWidget
virtual ~QueryDebugger();
private Q_SLOTS:
void contextMenu( const QPoint &pos );
void addQuery( double sequence, uint duration, const QString &query,
const QMap<QString,QVariant> & values, int resultsCount,
const QList<QList<QVariant> > & result, const QString &error );
void clear();
private:
QString variantToString( const QVariant &val );
......@@ -48,6 +50,8 @@ class QueryDebugger : public QWidget
org::freedesktop::Akonadi::StorageDebugger *mDebugger;
KTextEdit *mView;
QueryDebuggerModel *mModel;
QCheckBox* mOnlyAggregate;
};
#endif // QUERYDEBUGGER_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment