diff --git a/src/libs/valgrind/callgrind/callgrindabstractmodel.cpp b/src/libs/valgrind/callgrind/callgrindabstractmodel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f55790d7779540073983f066622afeeb6e4dc54d --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindabstractmodel.cpp @@ -0,0 +1,52 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "callgrindparsedata.h" +#include "callgrinddatamodel.h" + +namespace Valgrind { +namespace Callgrind { + +AbstractModel::AbstractModel() +{ + +} + +AbstractModel::~AbstractModel() +{ + +} + + +} // Callgrind +} // Valgrind diff --git a/src/libs/valgrind/callgrind/callgrindabstractmodel.h b/src/libs/valgrind/callgrind/callgrindabstractmodel.h new file mode 100644 index 0000000000000000000000000000000000000000..2bd253c07b535b70cdb0e6d9f6bc66872d0db670 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindabstractmodel.h @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef LIBVALGRIND_CALLGRINDABSTRACTMODEL_H +#define LIBVALGRIND_CALLGRINDABSTRACTMODEL_H + +#include "../valgrind_global.h" + +namespace Valgrind { +namespace Callgrind { + +class ParseData; + +class VALGRINDSHARED_EXPORT AbstractModel { +public: + AbstractModel(); + virtual ~AbstractModel(); + + virtual void setParseData(const ParseData *data) = 0; + virtual const ParseData *parseData() const = 0; + + /// Only one cost event column will be shown, this decides which one it is. + /// By default it is the first event in the @c ParseData, i.e. 0. + virtual int costEvent() const = 0; + + //BEGIN SLOTS + virtual void setCostEvent(int event) = 0; + //END SLOTS + + //BEGIN SIGNALS + virtual void parseDataChanged(AbstractModel *model) = 0; + //END SIGNALS + + enum Roles { + ParentCostRole = Qt::UserRole, + RelativeTotalCostRole, + RelativeParentCostRole, + NextCustomRole + }; +}; + +} // Callgrind +} // Valgrind + +#endif // LIBVALGRIND_CALLGRINDABSTRACTMODEL_H diff --git a/src/libs/valgrind/callgrind/callgrindcallmodel.cpp b/src/libs/valgrind/callgrind/callgrindcallmodel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..40664f6df395a529a49c74f6c98979acb880b997 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindcallmodel.cpp @@ -0,0 +1,234 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + + +#include "callgrindcallmodel.h" + +#include "callgrindfunctioncall.h" +#include "callgrindfunction.h" +#include "callgrindparsedata.h" + +#include <utils/qtcassert.h> + +#include <QVector> + +namespace Valgrind { +namespace Callgrind { + +//BEGIN CallModel::Private + +class CallModel::Private { +public: + Private(); + + const ParseData *m_data; + QVector<const FunctionCall *> m_calls; + int m_event; + const Function *m_function; +}; + +CallModel::Private::Private() +: m_data(0) +, m_event(0) +, m_function(0) +{ + +} + +//END CallModel::Private + +//BEGIN CallModel +CallModel::CallModel(QObject *parent) +: QAbstractItemModel(parent) +, d(new Private) +{ + +} + +CallModel::~CallModel() +{ + delete d; +} + +void CallModel::clear() +{ + beginResetModel(); + d->m_function = 0; + d->m_calls.clear(); + endResetModel(); +} + +void CallModel::setCalls(const QVector<const FunctionCall *> &calls, const Function *function) +{ + beginResetModel(); + d->m_function = function; + d->m_calls = calls; + endResetModel(); +} + +QVector<const FunctionCall *> CallModel::calls() const +{ + return d->m_calls; +} + +const Function *CallModel::function() const +{ + return d->m_function; +} + +void CallModel::setCostEvent(int event) +{ + d->m_event = event; +} + +int CallModel::costEvent() const +{ + return d->m_event; +} + +void CallModel::setParseData(const ParseData *data) +{ + if (d->m_data == data) + return; + + if (!data) + clear(); + + d->m_data = data; + emit parseDataChanged(this); +} + +const ParseData *CallModel::parseData() const +{ + return d->m_data; +} + +int CallModel::rowCount(const QModelIndex &parent) const +{ + QTC_ASSERT(!parent.isValid() || parent.model() == this, return 0); + + if (parent.isValid()) + return 0; + + return d->m_calls.count(); +} + +int CallModel::columnCount(const QModelIndex &parent) const +{ + QTC_ASSERT(!parent.isValid() || parent.model() == this, return 0); + + if (parent.isValid()) + return 0; + + return ColumnCount; +} + +QModelIndex CallModel::parent(const QModelIndex &child) const +{ + QTC_ASSERT(!child.isValid() || child.model() == this, return QModelIndex()); + + return QModelIndex(); +} + +QModelIndex CallModel::index(int row, int column, const QModelIndex &parent) const +{ + QTC_ASSERT(!parent.isValid() || parent.model() == this, return QModelIndex()); + + if (row == 0 && rowCount(parent) == 0) // happens with empty models + return QModelIndex(); + + QTC_ASSERT(row >= 0 && row < rowCount(parent), return QModelIndex()); + + return createIndex(row, column); +} + +QVariant CallModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid() && index.model() == this, return QVariant()); + QTC_ASSERT(index.column() >= 0 && index.column() < columnCount(index.parent()), return QVariant()); + QTC_ASSERT(index.row() >= 0 && index.row() < rowCount(index.parent()), return QVariant()); + + const FunctionCall *call = d->m_calls.at(index.row()); + const quint64 callCost = call->cost(d->m_event); + const quint64 parentCost = d->m_function->inclusiveCost(d->m_event); + if (role == ParentCostRole) { + return parentCost; + } + else if (role == FunctionCallRole + || role == RelativeParentCostRole || role == RelativeTotalCostRole) { + if (role == FunctionCallRole) + return QVariant::fromValue(call); + + if (role == RelativeTotalCostRole) { + const quint64 totalCost = d->m_data->totalCost(d->m_event); + return (float) callCost / totalCost; + } + + if (role == RelativeParentCostRole) + return (float) callCost / parentCost; + } + else if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { + if (index.column() == CalleeColumn) + return call->callee()->name(); + else if (index.column() == CallerColumn) + return call->caller()->name(); + else if (index.column() == CostColumn && role != Qt::ToolTipRole) + return callCost; + else if (index.column() == CallsColumn && role != Qt::ToolTipRole) + return call->calls(); + } + + return QVariant(); +} + +QVariant CallModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical || role != Qt::DisplayRole) + return QVariant(); + + QTC_ASSERT(section >= 0 && section < columnCount(), return QVariant()); + + if (section == CalleeColumn) + return tr("Callee"); + else if (section == CallerColumn) + return tr("Caller"); + else if (section == CostColumn) + return tr("Cost"); + else if (section == CallsColumn) + return tr("Calls"); + + return QVariant(); +} + +} // Callgrind +} // Valgrind diff --git a/src/libs/valgrind/callgrind/callgrindcallmodel.h b/src/libs/valgrind/callgrind/callgrindcallmodel.h new file mode 100644 index 0000000000000000000000000000000000000000..d64e67c7ab6ee5351ca3cc0df0c2d2c2a54bf5d6 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindcallmodel.h @@ -0,0 +1,109 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + + +#ifndef VALGRIND_CALLGRIND_CALLGRINDCALLMODEL_H +#define VALGRIND_CALLGRIND_CALLGRINDCALLMODEL_H + +#include <QtCore/QAbstractItemModel> + +#include "../valgrind_global.h" + +#include "callgrindabstractmodel.h" + +namespace Valgrind { +namespace Callgrind { + +class FunctionCall; +class Function; + +/** + * Model to display list of function calls. + */ +class VALGRINDSHARED_EXPORT CallModel : public QAbstractItemModel, public AbstractModel +{ + Q_OBJECT + +public: + explicit CallModel(QObject *parent = 0); + virtual ~CallModel(); + + void clear(); + + /// Only one cost event column will be shown, this decides which one it is. + /// By default it is the first event in the @c ParseData, i.e. 0. + virtual int costEvent() const; + + virtual void setParseData(const ParseData *data); + virtual const ParseData *parseData() const; + + void setCalls(const QVector<const FunctionCall *> &calls, const Function *function); + QVector<const FunctionCall *> calls() const; + const Function *function() const; + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &child) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + enum Columns { + CallerColumn, + CalleeColumn, + CallsColumn, + CostColumn, + ColumnCount + }; + + enum Roles { + FunctionCallRole = AbstractModel::NextCustomRole + }; + +public slots: + /// Only one cost event column will be shown, this decides which one it is. + /// By default it is the first event in the @c ParseData, i.e. 0. + void setCostEvent(int event); + +signals: + void parseDataChanged(AbstractModel *model); + +private: + class Private; + Private *d; +}; + +} // Callgrind +} // Valgrind + +#endif // VALGRIND_CALLGRIND_CALLGRINDCALLMODEL_H diff --git a/src/libs/valgrind/callgrind/callgrindcontroller.cpp b/src/libs/valgrind/callgrind/callgrindcontroller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c27eabefb2e83ff0f6498fb9f152d20073cf38f --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindcontroller.cpp @@ -0,0 +1,268 @@ +/************************************************************************** +** +** This file is part of Qt Creator Analyzer Tools +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "callgrindcontroller.h" + +#include <QDebug> +#include <QDir> + +#include <valgrind/valgrindprocess.h> + +#include <utils/qtcassert.h> +#include <utils/ssh/sftpchannel.h> + +#include <QTemporaryFile> + +#define CALLGRIND_CONTROL_DEBUG 0 + +const QLatin1String CALLGRIND_CONTROL_BINARY("callgrind_control"); + +namespace Valgrind { +namespace Callgrind { + +CallgrindController::CallgrindController(QObject *parent) + : QObject(parent) + , m_process(0) + , m_valgrindProc(0) + , m_lastOption(Unknown) +{ +} + +CallgrindController::~CallgrindController() +{ + cleanupTempFile(); +} + +QString toOptionString(CallgrindController::Option option) +{ + /* Callgrind help from v3.6.0 + + Options: + -h --help Show this help text + --version Show version + -l --long Show more information + -s --stat Show statistics + -b --back Show stack/back trace + -e [<A>,...] Show event counters for <A>,... (default: all) + --dump[=<s>] Request a dump optionally using <s> as description + -z --zero Zero all event counters + -k --kill Kill + --instr=<on|off> Switch instrumentation state on/off + -w=<dir> Specify the startup directory of an active Callgrind run + */ + + switch(option) { + case CallgrindController::Dump: + return "--dump"; + case CallgrindController::ResetEventCounters: + return "--zero"; + case CallgrindController::Pause: + return "--instr=off"; + case CallgrindController::UnPause: + return "--instr=on"; + default: + return ""; // never reached + } +} + + +void CallgrindController::run(Option option) +{ + if (m_process) { + emit statusMessage(tr("Previous command has not yet finished.")); + return; + } + QTC_ASSERT(m_valgrindProc, return) + + if (RemoteValgrindProcess *remote = qobject_cast<RemoteValgrindProcess *>(m_valgrindProc)) + m_process = new RemoteValgrindProcess(remote->connection(), this); + else + m_process = new LocalValgrindProcess(this); + + connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), + SLOT(processFinished(int,QProcess::ExitStatus))); + connect(m_process, SIGNAL(error(QProcess::ProcessError)), + SLOT(processError(QProcess::ProcessError))); + + // save back current running operation + m_lastOption = option; + + const QString optionString = toOptionString(option); + + switch(option) { + case CallgrindController::Dump: + emit statusMessage(tr("Dumping profile data...")); + break; + case CallgrindController::ResetEventCounters: + emit statusMessage(tr("Resetting event counters...")); + break; + case CallgrindController::Pause: + emit statusMessage(tr("Pausing instrumentation...")); + break; + case CallgrindController::UnPause: + emit statusMessage(tr("Unpausing instrumentation...")); + break; + default: + break; + } + +#if CALLGRIND_CONTROL_DEBUG + m_process->setProcessChannelMode(QProcess::ForwardedChannels); +#endif + m_process->run(CALLGRIND_CONTROL_BINARY, + QStringList() << optionString << QString::number(m_valgrindProc->pid()), + QString(), QString()); +} + +void CallgrindController::processError(QProcess::ProcessError processError) +{ + QTC_ASSERT(m_process, return) + const QString error = m_process->errorString(); + emit statusMessage(QString("An error occured while trying to run %1: %2").arg(CALLGRIND_CONTROL_BINARY).arg(error)); + + m_process->deleteLater(); + m_process = 0; +} + +void CallgrindController::processFinished(int rc, QProcess::ExitStatus status) +{ + QTC_ASSERT(m_process, return); + const QString error = m_process->errorString(); + + delete m_process; + m_process = 0; + + if (rc != 0 || status != QProcess::NormalExit) { + qWarning() << "Controller exited abnormally:" << error; + return; + } + + // this call went fine, we might run another task after this + switch(m_lastOption) { + case ResetEventCounters: + // lets dump the new resetted profiling info + run(Dump); + return; + case Pause: + // on pause, reset profiling info (for now) + run(ResetEventCounters); + return; + case Dump: + emit statusMessage(tr("Callgrind dumped profiling info")); + break; + case UnPause: + emit statusMessage(tr("Callgrind unpaused.")); + break; + default: + break; + } + + emit finished(m_lastOption); + m_lastOption = Unknown; +} + +void CallgrindController::setValgrindProcess(ValgrindProcess *proc) +{ + m_valgrindProc = proc; +} + +void CallgrindController::getLocalDataFile() +{ + QTC_ASSERT(m_valgrindProc, return); + + // we look for callgrind.out.PID, but there may be updated ones called ~.PID.NUM + QString baseFileName = QString("callgrind.out.%1").arg(m_valgrindProc->pid()); + const QString workingDir = m_valgrindProc->workingDirectory(); + // first, set the to-be-parsed file to callgrind.out.PID + QString fileName = workingDir.isEmpty() ? baseFileName : (workingDir + QDir::separator() + baseFileName); + + if (RemoteValgrindProcess *remote = qobject_cast<RemoteValgrindProcess *>(m_valgrindProc)) { + ///TODO: error handling + emit statusMessage(tr("Downloading remote profile data...")); + m_ssh = remote->connection(); + // if there are files like callgrind.out.PID.NUM, set it to the most recent one of those + QString cmd = QString("ls -t %1* | head -n 1").arg(fileName); + m_findRemoteFile = m_ssh->createRemoteProcess(cmd.toUtf8()); + connect(m_findRemoteFile.data(), SIGNAL(outputAvailable(QByteArray)), + this, SLOT(foundRemoteFile(QByteArray))); + m_findRemoteFile->start(); + } else { + QDir dir(workingDir, QString("%1.*").arg(baseFileName), QDir::Time); + QStringList outputFiles = dir.entryList(); + // if there are files like callgrind.out.PID.NUM, set it to the most recent one of those + if (!outputFiles.isEmpty()) + fileName = workingDir + QDir::separator() + dir.entryList().first(); + + emit localParseDataAvailable(fileName); + } +} + +void CallgrindController::foundRemoteFile(const QByteArray &file) +{ + m_remoteFile = file.trimmed(); + + m_sftp = m_ssh->createSftpChannel(); + connect(m_sftp.data(), SIGNAL(finished(Utils::SftpJobId,QString)), + this, SLOT(sftpJobFinished(Utils::SftpJobId,QString))); + connect(m_sftp.data(), SIGNAL(initialized()), this, SLOT(sftpInitialized())); + m_sftp->initialize(); +} + +void CallgrindController::sftpInitialized() +{ + cleanupTempFile(); + QTemporaryFile dataFile(QDir::tempPath() + QDir::separator() + "callgrind.out."); + QTC_ASSERT(dataFile.open(), return); + m_tempDataFile = dataFile.fileName(); + dataFile.setAutoRemove(false); + dataFile.close(); + + m_downloadJob = m_sftp->downloadFile(m_remoteFile, m_tempDataFile, Utils::SftpOverwriteExisting); +} + +void CallgrindController::sftpJobFinished(Utils::SftpJobId job, const QString &error) +{ + QTC_ASSERT(job == m_downloadJob, return); + + m_sftp->closeChannel(); + + if (error.isEmpty()) + emit localParseDataAvailable(m_tempDataFile); +} + +void CallgrindController::cleanupTempFile() +{ + if (!m_tempDataFile.isEmpty() && QFile::exists(m_tempDataFile)) + QFile::remove(m_tempDataFile); + + m_tempDataFile = QString(); +} + +} +} diff --git a/src/libs/valgrind/callgrind/callgrindcontroller.h b/src/libs/valgrind/callgrind/callgrindcontroller.h new file mode 100644 index 0000000000000000000000000000000000000000..52a98ee205b60ed82d8d572f7d82b4ffba9c793a --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindcontroller.h @@ -0,0 +1,115 @@ +/************************************************************************** +** +** This file is part of Qt Creator Analyzer Tools +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CALLGRINDCONTROLLER_H +#define CALLGRINDCONTROLLER_H + +#include <QObject> + +#include <qprocess.h> +#include <qmetatype.h> + +#include <utils/ssh/sshconnection.h> +#include <utils/ssh/sshremoteprocess.h> +#include <utils/ssh/sftpchannel.h> + +#include <valgrind/valgrind_global.h> + +namespace Valgrind { + +class ValgrindProcess; + +namespace Callgrind { + +class VALGRINDSHARED_EXPORT CallgrindController : public QObject +{ + Q_OBJECT + Q_ENUMS(Option); + +public: + enum Option { + Unknown, + Dump, + ResetEventCounters, + Pause, UnPause + }; + + explicit CallgrindController(QObject *parent = 0); + virtual ~CallgrindController(); + + void run(Valgrind::Callgrind::CallgrindController::Option option); + + void setValgrindProcess(ValgrindProcess *process); + + /** + * Make data file available locally, triggers @c localParseDataAvailable. + * + * If the valgrind process was run remotely, this transparently + * downloads the data file first and returns a local path. + */ + void getLocalDataFile(); + +Q_SIGNALS: + void finished(Valgrind::Callgrind::CallgrindController::Option option); + + void localParseDataAvailable(const QString &file); + + void statusMessage(const QString &msg); + +private Q_SLOTS: + void processError(QProcess::ProcessError); + void processFinished(int, QProcess::ExitStatus); + + void foundRemoteFile(const QByteArray &file); + void sftpInitialized(); + void sftpJobFinished(Utils::SftpJobId job, const QString &error); + +private: + void cleanupTempFile(); + + // callgrind_controll process + Valgrind::ValgrindProcess *m_process; + // valgrind process + Valgrind::ValgrindProcess *m_valgrindProc; + + Option m_lastOption; + + // remote callgrind support + Utils::SshConnection::Ptr m_ssh; + QString m_tempDataFile; + Utils::SshRemoteProcess::Ptr m_findRemoteFile; + Utils::SftpChannel::Ptr m_sftp; + Utils::SftpJobId m_downloadJob; + QByteArray m_remoteFile; +}; + +} +} + +#endif // CALLGRINDCONTROLLER_H diff --git a/src/libs/valgrind/callgrind/callgrindcostitem.cpp b/src/libs/valgrind/callgrind/callgrindcostitem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b93ee753e61723f7d57afaa2d09d26ab8ba2ae08 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindcostitem.cpp @@ -0,0 +1,147 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "callgrindcostitem.h" + +#include <QtCore/QVector> +#include <QtCore/QString> +#include <QtCore/QStringList> + +#include "callgrindparsedata.h" +#include "callgrindfunctioncall.h" + +namespace Valgrind { +namespace Callgrind { + +//BEGIN CostItem::Private + +class CostItem::Private { +public: + Private(ParseData *data); + ~Private(); + + QVector<quint64> m_positions; + QVector<quint64> m_events; + const FunctionCall *m_call; + + const ParseData *m_data; + qint64 m_differingFileId; +}; + +CostItem::Private::Private(ParseData *data) +: m_positions(data->positions().size(), 0) +, m_events(data->events().size(), 0) +, m_call(0) +, m_data(data) +, m_differingFileId(-1) +{ +} + +CostItem::Private::~Private() +{ + delete m_call; +} + + +//BEGIN CostItem +CostItem::CostItem(ParseData *data) +: d(new Private(data)) +{ + +} + +CostItem::~CostItem() +{ + delete d; +} + +quint64 CostItem::position(int posIdx) const +{ + return d->m_positions.at(posIdx); +} + +void CostItem::setPosition(int posIdx, quint64 position) +{ + d->m_positions[posIdx] = position; +} + +QVector< quint64 > CostItem::positions() const +{ + return d->m_positions; +} + +quint64 CostItem::cost(int event) const +{ + return d->m_events.at(event); +} + +void CostItem::setCost(int event, quint64 cost) +{ + d->m_events[event] = cost; +} + +QVector< quint64 > CostItem::costs() const +{ + return d->m_events; +} + +const FunctionCall *CostItem::call() const +{ + return d->m_call; +} + +void CostItem::setCall(const FunctionCall *call) +{ + d->m_call = call; +} + +QString CostItem::differingFile() const +{ + if (d->m_differingFileId != -1) + return d->m_data->stringForFileCompression(d->m_differingFileId); + else + return QString(); +} + +qint64 CostItem::differingFileId() const +{ + return d->m_differingFileId; +} + +void CostItem::setDifferingFile(qint64 fileId) +{ + d->m_differingFileId = fileId; +} + +} // Callgrind +} // Valgrind diff --git a/src/libs/valgrind/callgrind/callgrindcostitem.h b/src/libs/valgrind/callgrind/callgrindcostitem.h new file mode 100644 index 0000000000000000000000000000000000000000..9c946ecc942986179383a01798c809bd4a15956c --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindcostitem.h @@ -0,0 +1,103 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef LIBVALGRIND_CALLGRIND_COSTITEM_H +#define LIBVALGRIND_CALLGRIND_COSTITEM_H + +#include "../valgrind_global.h" + +QT_BEGIN_NAMESPACE +class QString; +template<typename T> class QVector; +QT_END_NAMESPACE + +namespace Valgrind { +namespace Callgrind { + +class FunctionCall; +class ParseData; + +/** + * This class represents the cost(s) at given position(s). + */ +class VALGRINDSHARED_EXPORT CostItem { +public: + /// @p data the file data this cost item was parsed in. + /// required for decompression of string data like differing source file information + explicit CostItem(ParseData *data); + ~CostItem(); + + /** + * Position data for the given position-index @p posIdx + * @see ParseData::positions() + */ + quint64 position(int posIdx) const; + void setPosition(int posIdx, quint64 position); + QVector<quint64> positions() const; + + /** + * Cost data for the given event-index @p event + * @see ParseData::events() + */ + quint64 cost(int event) const; + void setCost(int event, quint64 cost); + QVector<quint64> costs() const; + + /** + * If this cost item represents a function call, this will return the @c Callee. + * Otherwise zero will be returned. + */ + const FunctionCall *call() const; + ///NOTE: @c CostItem will take ownership + void setCall(const FunctionCall *call); + + /** + * If this cost item represents a jump to a different file, this will + * return the path to that file. The string will be empty otherwise. + */ + QString differingFile() const; + /// @return compressed file id or -1 if none is set + qint64 differingFileId() const; + void setDifferingFile(qint64 fileId); + +private: + Q_DISABLE_COPY(CostItem); + + class Private; + Private *d; +}; + +} // Callgrind +} // Valgrind + +#endif // LIBVALGRIND_CALLGRIND_COSTITEM_H diff --git a/src/libs/valgrind/callgrind/callgrindcycledetection.cpp b/src/libs/valgrind/callgrind/callgrindcycledetection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f53d20f027f92333e35a43a464b4da24a2b5018 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindcycledetection.cpp @@ -0,0 +1,125 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "callgrindcycledetection.h" + +#include "callgrindfunction.h" +#include "callgrindfunctioncall.h" +#include "callgrindparsedata.h" +#include "callgrindfunctioncycle.h" + +#include <QtCore/QDebug> + +#include <utils/qtcassert.h> + +namespace Valgrind { +namespace Callgrind { +namespace Internal { + +CycleDetection::CycleDetection(ParseData *data) +: m_data(data) +, m_depth(0) +, m_cycle(0) +{ + +} + +QVector<const Function *> CycleDetection::run(const QVector<const Function *> &input) +{ + foreach(const Function *function, input) { + Node *node = new Node; + node->function = function; + node->dfs = -1; + node->lowlink = -1; + m_nodes.insert(function, node); + } + foreach(Node *node, m_nodes) { + if (node->dfs == -1) + tarjan(node); + } + qDeleteAll(m_nodes); + return m_ret; +} + +void CycleDetection::tarjan(Node *node) +{ + QTC_ASSERT(node->dfs == -1, return); + node->dfs = m_depth; + node->lowlink = m_depth; + + m_depth++; + m_stack.push(node); + + foreach(const FunctionCall *call, node->function->outgoingCalls()) + tarjanForChildNode(node, m_nodes.value(call->callee())); + + if (node->dfs == node->lowlink) { + QVector<const Function *> functions; + Node *n; + do { + n = m_stack.pop(); + functions << n->function; + } while(n != node); + + if (functions.size() == 1) { + // not a real cycle + m_ret.append(node->function); + } else { + // actual cycle + FunctionCycle *cycle = new FunctionCycle(m_data); + cycle->setFile(node->function->fileId()); + m_cycle++; + qint64 id = -1; + m_data->addCompressedFunction(QString("cycle %1").arg(m_cycle), id); + cycle->setName(id); + cycle->setObject(node->function->objectId()); + cycle->setFunctions(functions); + m_ret.append(cycle); + } + } +} + +void CycleDetection::tarjanForChildNode(Node *node, Node *childNode) +{ + if (childNode->dfs == -1) { + tarjan(childNode); + if (childNode->lowlink < node->lowlink) + node->lowlink = childNode->lowlink; + } else if (childNode->dfs < node->lowlink && m_stack.contains(childNode)) { + node->lowlink = childNode->dfs; + } +} + +} +} +} diff --git a/src/libs/valgrind/callgrind/callgrindcycledetection.h b/src/libs/valgrind/callgrind/callgrindcycledetection.h new file mode 100644 index 0000000000000000000000000000000000000000..83e5d53bf6488c167c52c1182fc8f92bae1b13db --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindcycledetection.h @@ -0,0 +1,85 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef LIBVALGRIND_CALLGRINDCYCLEDETECTION_H +#define LIBVALGRIND_CALLGRINDCYCLEDETECTION_H + +#include <QtCore/QHash> +#include <QtCore/QStack> + +namespace Valgrind { +namespace Callgrind { + +class Function; +class ParseData; + +namespace Internal { + +/** + * Implementation of Tarjan's strongly connected components algorithm, to find function cycles, + * as suggested by the GProf paper: + * + * ``gprof: A Call Graph Execution Profiler'', by S. Graham, P. Kessler, + * M. McKusick; Proceedings of the SIGPLAN '82 Symposium on Compiler Construction, + * SIGPLAN Notices, Vol. 17, No 6, pp. 120-126, June 1982. + */ +class CycleDetection { +public: + explicit CycleDetection(ParseData *data); + QVector<const Function *> run(const QVector<const Function *> &input); + +private: + ParseData *m_data; + + struct Node { + int dfs; + int lowlink; + const Function *function; + }; + + void tarjan(Node *node); + void tarjanForChildNode(Node *node, Node *childNode); + + QHash<const Function *, Node *> m_nodes; + QStack<Node *> m_stack; + QVector<const Function *> m_ret; + int m_depth; + + int m_cycle; +}; + +} +} +} + +#endif // LIBVALGRIND_CALLGRINDCYCLEDETECTION_H diff --git a/src/libs/valgrind/callgrind/callgrinddatamodel.cpp b/src/libs/valgrind/callgrind/callgrinddatamodel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5449ef085ddd8e1b6a65474e816306fbab58c439 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrinddatamodel.cpp @@ -0,0 +1,346 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + + +#include "callgrinddatamodel.h" + +#include "callgrindparsedata.h" +#include "callgrindfunction.h" +#include "callgrindcostitem.h" + +#include <QChar> +#include <QDebug> +#include <QtCore/QStringList> +#include <QtCore/QVector> + +#include <utils/qtcassert.h> + +namespace Valgrind { +namespace Callgrind { + +//BEGIN Helper + +namespace { + // minimum amount of columns, i.e.: + // function name + // file name + // object name + // num called + // Additional to this, 2 * ParseData::events().size will be shown (inclusive + self cost) + const int MinColumnSize = 4; +} + +//BEGIN DataModel::Private + +class DataModel::Private { +public: + Private() + : m_data(0) + , m_event(0) + , m_cycleDetection(false) + { + } + + ~Private() + { + } + + void updateFunctions(); + + const ParseData *m_data; + int m_event; + bool m_cycleDetection; + QVector<const Function *> m_functions; +}; + +struct SortFunctions { + SortFunctions(int event) + : m_event(event) + { + } + bool operator()(const Function *left, const Function *right) + { + return left->inclusiveCost(m_event) > right->inclusiveCost(m_event); + } + int m_event; +}; + +void DataModel::Private::updateFunctions() +{ + if (m_data) { + m_functions = m_data->functions(m_cycleDetection); + qSort(m_functions.begin(), m_functions.end(), SortFunctions(m_event)); + } else { + m_functions.clear(); + } +} + +//BEGIN DataModel + +DataModel::DataModel(QObject *parent) +: QAbstractItemModel(parent) +, d(new Private) +{ + +} + +DataModel::~DataModel() +{ + delete d; +} + +void DataModel::setParseData(const ParseData *data) +{ + if (d->m_data == data) + return; + + beginResetModel(); + d->m_data = data; + d->m_event = 0; + d->updateFunctions(); + endResetModel(); + emit parseDataChanged(this); +} + +const ParseData *DataModel::parseData() const +{ + return d->m_data; +} + +void DataModel::setCostEvent(int event) +{ + if (!d->m_data) + return; + + QTC_ASSERT(event >= 0 && d->m_data->events().size() > event, return) + beginResetModel(); + d->m_event = event; + d->updateFunctions(); + endResetModel(); + emit dataChanged(index(0, SelfCostColumn), index(qMax(0, rowCount() - 1), InclusiveCostColumn)); +} + +int DataModel::costEvent() const +{ + return d->m_event; +} + +int DataModel::rowCount(const QModelIndex &parent) const +{ + QTC_ASSERT(!parent.isValid() || parent.model() == this, return 0); + + if (!d->m_data || parent.isValid()) + return 0; + + return d->m_functions.size(); +} + +int DataModel::columnCount(const QModelIndex &parent) const +{ + QTC_ASSERT(!parent.isValid() || parent.model() == this, return 0); + if (parent.isValid()) + return 0; + + return ColumnCount; +} + +QModelIndex DataModel::index(int row, int column, const QModelIndex &parent) const +{ + QTC_ASSERT(!parent.isValid() || parent.model() == this, return QModelIndex()); + if (row == 0 && rowCount(parent) == 0) // happens with empty models + return QModelIndex(); + QTC_ASSERT(row >= 0 && row < rowCount(parent), return QModelIndex()); + return createIndex(row, column); +} + +QModelIndex DataModel::parent(const QModelIndex &child) const +{ + QTC_ASSERT(!child.isValid() || child.model() == this, return QModelIndex()); + return QModelIndex(); +} + +QModelIndex DataModel::indexForObject(const Function *function) const +{ + if (!function) + return QModelIndex(); + + const int row = d->m_functions.indexOf(function); + if (row < 0) + return QModelIndex(); + + return createIndex(row, 0); +} + +/** + * Evil workaround for http://bugreports.qt.nokia.com/browse/QTBUG-1135 + * Just replace the bad hyphens by a 'NON-BREAKING HYPHEN' unicode char + */ +static QString noWrap(const QString &str) +{ + QString escapedStr = str; + return escapedStr.replace("-", "‑"); +} + +QVariant DataModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid() && index.model() == this, return QVariant()); + QTC_ASSERT(index.column() >= 0 && index.column() < columnCount(index.parent()), return QVariant()); + QTC_ASSERT(index.row() >= 0 && index.row() < rowCount(index.parent()), return QVariant()); + + const Function *func = d->m_functions.at(index.row()); + const quint64 selfCost = func->selfCost(d->m_event); + const quint64 inclusiveCost = func->inclusiveCost(d->m_event); + const quint64 totalCost = d->m_data->totalCost(d->m_event); + + if (role == FunctionRole) { + return QVariant::fromValue(func); + } + else if (role == ParentCostRole) { + return totalCost; + } + // the data model does not know about parent<->child relationship + else if (role == RelativeParentCostRole || role == RelativeTotalCostRole) { + if (index.column() == SelfCostColumn) + return (float)selfCost / totalCost; + else if (index.column() == InclusiveCostColumn) + return (float)inclusiveCost / totalCost; + } + else if (role == LineNumberRole) { + return func->lineNumber(); + } + else if (role == FileNameRole) { + return func->file(); + } + else if (role == Qt::TextAlignmentRole) { + if (index.column() == CalledColumn) { + return Qt::AlignRight; + } + } + else if (role == Qt::DisplayRole) { + if (index.column() == NameColumn) + return func->name(); + else if (index.column() == LocationColumn) + return func->location(); + else if (index.column() == CalledColumn) + return func->called(); + else if (index.column() == SelfCostColumn) + return selfCost; + else if (index.column() == InclusiveCostColumn) + return inclusiveCost; + } else if (role == Qt::ToolTipRole) { + QString ret = "<html><head><style>\ + dt { font-weight: bold; }\ + dd { font-family: monospace; }\ + tr.head, td.head { font-weight: bold; }\ + tr.head { text-decoration: underline; }\ + td.group { padding-left: 20px; }\ + td { white-space: nowrap; }\ + </style></head>\n"; + + // body, function info first + ret += "<body><dl>"; + ret += "<dt>" + tr("Function:") + "</dt><dd>" + func->name() + "</dd>\n"; + ret += "<dt>" + tr("File:") + "</dt><dd>" + func->file() + "</dd>\n"; + if (!func->costItems().isEmpty()) { + const CostItem *firstItem = func->costItems().first(); + for(int i = 0; i < d->m_data->positions().size(); ++i) { + ret += "<dt>" + ParseData::prettyStringForPosition(d->m_data->positions().at(i)) + "</dt>"; + ret += "<dd>" + QString::number(firstItem->position(i)) + "</dd>\n"; + } + } + ret += "<dt>" + tr("Object:") + "</dt><dd>" + func->object() + "</dd>\n"; + ret += "<dt>" + tr("Called:") + "</dt><dd>" + tr("%1 times").arg(func->called()) + "</dd>\n"; + ret += "</dl><p/>"; + + // self/inclusive costs + ret += "<table>"; + ret += "<thead><tr class='head'><td>" + tr("Events") + "</td>"; + ret += "<td class='group'>" + tr("Self costs") + "</td><td>" + tr("(%)") + "</td>"; + ret += "<td class='group'>" + tr("Incl. costs") + "</td><td>" + tr("(%)") + "</td>"; + ret += "</tr></thead>"; + ret += "<tbody>"; + for(int i = 0; i < d->m_data->events().size(); ++i) { + quint64 selfCost = func->selfCost(i); + quint64 inclCost = func->inclusiveCost(i); + quint64 totalCost = d->m_data->totalCost(i); + // 0.00% format + const float relSelfCost = (float)qRound((float)selfCost / totalCost * 10000) / 100; + const float relInclCost = (float)qRound((float)inclCost / totalCost * 10000) / 100; + + ret += "<tr>"; + ret += "<td class='head'><nobr>" + noWrap(ParseData::prettyStringForEvent(d->m_data->events().at(i))) + "</nobr></td>"; + ret += "<td class='group'>" + tr("%1").arg(selfCost) + "</td>"; + ret += "<td>" + tr("(%1%)").arg(relSelfCost) + "</td>"; + ret += "<td class='group'>" + tr("%1").arg(inclCost) + "</td>"; + ret += "<td>" + tr("(%1%)").arg(relInclCost) + "</td>"; + ret += "</tr>"; + } + ret += "</tbody></table>"; + ret += "</body></html>"; + return ret; + } + + return QVariant(); +} + +QVariant DataModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical || role != Qt::DisplayRole) + return QVariant(); + + QTC_ASSERT(section >= 0 && section < columnCount(), return QVariant()); + + if (section == NameColumn) + return tr("Function"); + else if (section == LocationColumn) + return tr("Location"); + else if (section == CalledColumn) + return tr("Called"); + else if (section == SelfCostColumn) + return tr("Self Cost: %1").arg(d->m_data ? d->m_data->events().value(d->m_event) : QString()); + else if (section == InclusiveCostColumn) + return tr("Incl. Cost: %1").arg(d->m_data ? d->m_data->events().value(d->m_event) : QString()); + + return QVariant(); +} + +void DataModel::enableCycleDetection(bool enabled) +{ + beginResetModel(); + d->m_cycleDetection = enabled; + d->updateFunctions(); + endResetModel(); +} + +} +} diff --git a/src/libs/valgrind/callgrind/callgrinddatamodel.h b/src/libs/valgrind/callgrind/callgrinddatamodel.h new file mode 100644 index 0000000000000000000000000000000000000000..61a655cb601ccb8d42f7042eac3ee46bb0608981 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrinddatamodel.h @@ -0,0 +1,109 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + + +#ifndef VALGRIND_CALLGRIND_CALLGRINDDATAMODEL_H +#define VALGRIND_CALLGRIND_CALLGRINDDATAMODEL_H + +#include <QtCore/QAbstractItemModel> + +#include "../valgrind_global.h" + +#include "callgrindabstractmodel.h" + +namespace Valgrind { +namespace Callgrind { + +class Function; +class ParseData; + +class VALGRINDSHARED_EXPORT DataModel : public QAbstractItemModel, public AbstractModel +{ + Q_OBJECT + +public: + explicit DataModel(QObject *parent = 0); + virtual ~DataModel(); + + virtual void setParseData(const ParseData *data); + virtual const ParseData *parseData() const; + + /// Only one cost event column will be shown, this decides which one it is. + /// By default it is the first event in the @c ParseData, i.e. 0. + virtual int costEvent() const; + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &child) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + QModelIndex indexForObject(const Function *function) const; + + enum Columns { + NameColumn, + LocationColumn, + CalledColumn, + SelfCostColumn, + InclusiveCostColumn, + ColumnCount + }; + + enum Roles { + FunctionRole = AbstractModel::NextCustomRole, + LineNumberRole, + FileNameRole + }; + +public slots: + /// enable/disable cycle detection + void enableCycleDetection(bool enabled); + + /// Only one cost event column will be shown, this decides which one it is. + /// By default it is the first event in the @c ParseData, i.e. 0. + virtual void setCostEvent(int event); + +signals: + void parseDataChanged(AbstractModel *model); + +private: + class Private; + Private *d; +}; + +} // Callgrind +} // Valgrind + +#endif // VALGRIND_CALLGRIND_CALLGRINDDATAMODEL_H diff --git a/src/libs/valgrind/callgrind/callgrindfunction.cpp b/src/libs/valgrind/callgrind/callgrindfunction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..58ad416c196bd05f90fd947ce1808c758b53cf4b --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindfunction.cpp @@ -0,0 +1,337 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "callgrindfunction.h" + +#include "callgrindfunctioncall.h" +#include "callgrindcostitem.h" +#include "callgrindparsedata.h" +#include "callgrindfunction_p.h" + +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> + +#include <utils/qtcassert.h> + +namespace Valgrind { +namespace Callgrind { + +//BEGIN Function::Private + +Function::Private::Private(const ParseData *data) +: m_data(data) +, m_fileId(-1) +, m_objectId(-1) +, m_nameId(-1) +, m_selfCost(data->events().size(), 0) +, m_inclusiveCost(data->events().size(), 0) +, m_called(0) +{ +} + +Function::Private::~Private() +{ + // we don't own m_callers + // we own the costitem which in turn owns the callees, + // so only delete the former + qDeleteAll(m_costItems); + + qDeleteAll(m_outgoingCalls); +} + +void Function::Private::accumulateCost(QVector<quint64> &base, const QVector<quint64> &add) +{ + if (base.isEmpty()) { + base = add; + } else { + ///TODO: see whether .data() is noticably faster (less detaching) + int i = 0; + foreach(quint64 cost, add) + base[i++] += cost; + } +} + +FunctionCall *Function::Private::accumulateCall(const FunctionCall *call, CallType type) +{ + const Function *key = (type == Incoming) ? call->caller() : call->callee(); + QHash<const Function *, FunctionCall *> &callMap = (type == Incoming) ? m_incomingCallMap : m_outgoingCallMap; + + FunctionCall *accumulatedCall = callMap.value(key, 0); + if (!accumulatedCall) { + accumulatedCall = new FunctionCall; + if (type == Incoming) + m_incomingCalls << accumulatedCall; + else + m_outgoingCalls << accumulatedCall; + + accumulatedCall->setCallee(call->callee()); + accumulatedCall->setCaller(call->caller()); + ///TODO: could the destinations differ from call to call? they should not, or? + accumulatedCall->setDestinations(call->destinations()); + callMap.insert(key, accumulatedCall); + + accumulatedCall->setCosts(call->costs()); + } else { + QVector<quint64> costs = accumulatedCall->costs(); + accumulateCost(costs, call->costs()); + accumulatedCall->setCosts(costs); + } + + accumulatedCall->setCalls(accumulatedCall->calls() + call->calls()); + return accumulatedCall; +} + +//BEGIN Function +Function::Function(const ParseData *data) +: d(new Private(data)) +{ + +} + +Function::Function(Function::Private *d) +: d(d) +{ + +} + +Function::~Function() +{ + delete d; +} + +qint64 Function::nameId() const +{ + return d->m_nameId; +} + +QString Function::name() const +{ + if (d->m_nameId != -1) + return d->m_data->stringForFunctionCompression(d->m_nameId); + else + return QString(); +} + +void Function::setName(qint64 id) +{ + d->m_nameId = id; +} + +qint64 Function::fileId() const +{ + return d->m_fileId; +} + +QString Function::file() const +{ + if (d->m_fileId != -1) + return d->m_data->stringForFileCompression(d->m_fileId); + else + return QString(); +} + +void Function::setFile(qint64 id) +{ + d->m_fileId = id; +} + +qint64 Function::objectId() const +{ + return d->m_objectId; +} + +QString Function::object() const +{ + if (d->m_objectId != -1) + return d->m_data->stringForObjectCompression(d->m_objectId); + else + return QString(); +} + +void Function::setObject(qint64 id) +{ + d->m_objectId = id; +} + +QString Function::location() const +{ + QString pos; + foreach(const CostItem *costItem, d->m_costItems) { + if (costItem->differingFileId() != -1) { + QTextStream stream(&pos); + stream << '('; + for(int i = 0, c = costItem->positions().count(); i < c; ++i) { + ///TODO: remember what was hex formatted + stream << costItem->position(i); + if (i != c - 1) + stream << ", "; + } + stream << ')'; + break; + } + } + QString f = file(); + + if (!f.isEmpty()) { + QFileInfo info(f); + if (info.exists()) { + f = info.canonicalFilePath(); + } + } + + QString o = object(); + if (o.isEmpty()) + return QString(); + if (f.isEmpty() || f == "???") + return o; + if (pos.isEmpty()) + return QObject::tr("%1 in %2").arg(f, o); + + return QObject::tr("%1:%2 in %3").arg(f, pos, o); +} + +int Function::lineNumber() const +{ + const int lineIdx = d->m_data->lineNumberPositionIndex(); + if (lineIdx == -1) + return -1; + + foreach(const CostItem *costItem, d->m_costItems) { + if (costItem->differingFileId() == -1) + return costItem->position(lineIdx); + } + + return -1; +} + +quint64 Function::selfCost(int event) const +{ + return d->m_selfCost.at(event); +} + +QVector< quint64 > Function::selfCosts() const +{ + return d->m_selfCost; +} + +quint64 Function::inclusiveCost(int event) const +{ + return d->m_inclusiveCost.at(event) + d->m_selfCost.at(event); +} + +QVector<const FunctionCall *> Function::outgoingCalls() const +{ + return d->m_outgoingCalls; +} + +void Function::addOutgoingCall(const FunctionCall *call) +{ + QTC_ASSERT(call->caller() == this, return); + + d->accumulateCall(call, Private::Outgoing); +} + +QVector<const FunctionCall *> Function::incomingCalls() const +{ + return d->m_incomingCalls; +} + +void Function::addIncomingCall(const FunctionCall *call) +{ + QTC_ASSERT(call->callee() == this, return); + d->m_called += call->calls(); + d->accumulateCall(call, Private::Incoming); +} + +quint64 Function::called() const +{ + return d->m_called; +} + +QVector<const CostItem *> Function::costItems() const +{ + return d->m_costItems; +} + +void Function::addCostItem(const CostItem *item) +{ + QTC_ASSERT(!d->m_costItems.contains(item), return); + + d->m_costItems.append(item); + + // accumulate costs + if (item->call()) { + d->accumulateCost(d->m_inclusiveCost, item->costs()); + } else { + d->accumulateCost(d->m_selfCost, item->costs()); + } +} + +void Function::finalize() +{ + bool recursive = false; + foreach(const FunctionCall *call, d->m_incomingCalls) { + if (call->caller() == this) { + recursive = true; + break; + } + } + + if (recursive) { + // now handle recursive calls by setting the incl cost to the sum of all (external) calls + // to this function + // e.g.: A -> B -> B ..., C -> B -> B ... + // cost of B = cost of call to B in A + cost of call to B in C + ... + d->m_inclusiveCost.fill(0); + foreach(const FunctionCall *call, d->m_incomingCalls) { + if (call->caller() != this) { + foreach(const CostItem *costItem, call->caller()->costItems()) { + if (costItem->call() && costItem->call()->callee() == this) + d->accumulateCost(d->m_inclusiveCost, costItem->costs()); + } + } + } + /// now substract self cost (see @c inclusiveCost() implementation) + for(int i = 0, c = d->m_inclusiveCost.size(); i < c; ++i) { + if (d->m_inclusiveCost.at(i) < d->m_selfCost.at(i)) + d->m_inclusiveCost[i] = 0; + else + d->m_inclusiveCost[i] -= d->m_selfCost.at(i); + } + } +} + +} // Callgrind +} // Valgrind diff --git a/src/libs/valgrind/callgrind/callgrindfunction.h b/src/libs/valgrind/callgrind/callgrindfunction.h new file mode 100644 index 0000000000000000000000000000000000000000..e110e8562d5131c4e6e2326e0d4b94ce532c981b --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindfunction.h @@ -0,0 +1,162 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef LIBVALGRIND_CALLGRIND_FUNCTION_H +#define LIBVALGRIND_CALLGRIND_FUNCTION_H + +#include "../valgrind_global.h" + +#include <QtCore/QMetaType> + +QT_BEGIN_NAMESPACE +class QString; +template <typename T> class QVector; +template <typename T> class QSet; +QT_END_NAMESPACE + +namespace Valgrind { +namespace Callgrind { + +class FunctionCall; +class CostItem; +class ParseData; + +class VALGRINDSHARED_EXPORT Function { +public: + /// @p data the ParseData for the file this function was part of + /// required for the decompression of string data like function name etc. + explicit Function(const ParseData *data); + virtual ~Function(); + + /// @return the compressed function name id + qint64 nameId() const; + /// @return the function name. + QString name() const; + /** + * Set function name to internal string id @p id. + * @see ParseData::stringForFunction() + */ + void setName(qint64 id); + + /// @return the compressed file id + qint64 fileId() const; + /// @return the file path where this function was defined + QString file() const; + /** + * Set function name to internal string id @p id. + * @see ParseData::stringForFunction() + */ + void setFile(qint64 id); + + /// @return the compressed object id + qint64 objectId() const; + /// @return the object where this function was defined + QString object() const; + /** + * Set function name to internal string id @p id. + * @see ParseData::stringForFunction() + */ + void setObject(qint64 id); + + /** + * @return a string representing the location of this function + * It is a combination of file, object and line of the first CostItem. + */ + QString location() const; + + /** + * @return the line number of the function or -1 if not known + */ + int lineNumber() const; + + /** + * total accumulated self cost of @p event + * @see ParseData::events() + */ + quint64 selfCost(int event) const; + QVector<quint64> selfCosts() const; + + /** + * total accumulated inclusive cost of @p event + * @see ParseData::events() + */ + quint64 inclusiveCost(int event) const; + + /// calls from other functions to this function + QVector<const FunctionCall *> incomingCalls() const; + void addIncomingCall(const FunctionCall *call); + /// @return how often this function was called in total + quint64 called() const; + + /** + * The detailed list of cost items, which could e.g. be used for + * a detailed view of the function's source code annotated with + * cost per line. + */ + QVector<const CostItem *> costItems() const; + + /** + * Add parsed @c CostItem @p item to this function. + * + * NOTE: The @c Function will take ownership. + */ + void addCostItem(const CostItem *item); + + /** + * Function calls from this function to others. + */ + QVector<const FunctionCall *> outgoingCalls() const; + void addOutgoingCall(const FunctionCall *call); + + /** + * Gets called after all functions where looked up, required + * to properly calculate inclusive cost of recursive functions + * for example + */ + void finalize(); +protected: + class Private; + Private *d; + + explicit Function(Private *d); + +private: + Q_DISABLE_COPY(Function) +}; + +} +} + +Q_DECLARE_METATYPE(const Valgrind::Callgrind::Function *); + +#endif // LIBVALGRIND_CALLGRIND_FUNCTION_H diff --git a/src/libs/valgrind/callgrind/callgrindfunction_p.h b/src/libs/valgrind/callgrind/callgrindfunction_p.h new file mode 100644 index 0000000000000000000000000000000000000000..88195931187598f660c9e0400b572ee451b3ab71 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindfunction_p.h @@ -0,0 +1,82 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef LIBVALGRIND_CALLGRINDFUNCTION_P_H +#define LIBVALGRIND_CALLGRINDFUNCTION_P_H + +#include "callgrindfunction.h" +#include "callgrindparsedata.h" +#include "callgrindcostitem.h" +#include "callgrindfunctioncall.h" + +#include <QtCore/QVector> +#include <QtCore/QHash> + +namespace Valgrind { +namespace Callgrind { + +class Function::Private { +public: + Private(const ParseData *data); + ~Private(); + + static void accumulateCost(QVector<quint64> &base, const QVector<quint64> &add); + enum CallType { + Incoming, + Outgoing + }; + ///@return accumulated call + FunctionCall *accumulateCall(const FunctionCall *call, CallType type); + + const ParseData *m_data; + qint64 m_fileId; + qint64 m_objectId; + qint64 m_nameId; + + QVector<quint64> m_selfCost; + QVector<quint64> m_inclusiveCost; + + QVector<const CostItem *> m_costItems; + // used to accumulate, hence values not const + QHash<const Function *, FunctionCall *> m_outgoingCallMap; + QHash<const Function *, FunctionCall *> m_incomingCallMap; + // used in public api, hence const + QVector<const FunctionCall *> m_outgoingCalls; + QVector<const FunctionCall *> m_incomingCalls; + quint64 m_called; +}; + +} +} + +#endif // LIBVALGRIND_CALLGRINDFUNCTION_P_H diff --git a/src/libs/valgrind/callgrind/callgrindfunctioncall.cpp b/src/libs/valgrind/callgrind/callgrindfunctioncall.cpp new file mode 100644 index 0000000000000000000000000000000000000000..252ee39621d7f1e18e3af3649d2a4ae5d54d2dae --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindfunctioncall.cpp @@ -0,0 +1,143 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "callgrindfunctioncall.h" + +#include "callgrindfunction.h" + +#include <QtCore/QVector> + +#include <utils/qtcassert.h> + +namespace Valgrind { +namespace Callgrind { + +//BEGIN FunctionCall::Private +class FunctionCall::Private { +public: + explicit Private(); + + const Function *m_callee; + const Function *m_caller; + quint64 m_calls; + quint64 m_totalInclusiveCost; + QVector<quint64> m_destinations; + QVector<quint64> m_costs; +}; + +FunctionCall::Private::Private() +: m_callee(0) +, m_caller(0) +, m_calls(0) +, m_totalInclusiveCost(0) +{ + +} + +//BEGIN FunctionCall + +FunctionCall::FunctionCall() +: d(new Private) +{ + +} + +FunctionCall::~FunctionCall() +{ + delete d; +} + +const Function *FunctionCall::callee() const +{ + return d->m_callee; +} + +void FunctionCall::setCallee(const Function *function) +{ + d->m_callee = function; +} + +const Function *FunctionCall::caller() const +{ + return d->m_caller; +} + +void FunctionCall::setCaller(const Function *function) +{ + d->m_caller = function; +} + +quint64 FunctionCall::calls() const +{ + return d->m_calls; +} + +void FunctionCall::setCalls(quint64 calls) +{ + d->m_calls = calls; +} + +quint64 FunctionCall::destination(int posIdx) const +{ + return d->m_destinations.at(posIdx); +} + +QVector<quint64> FunctionCall::destinations() const +{ + return d->m_destinations; +} + +void FunctionCall::setDestinations(const QVector<quint64> &destinations) +{ + d->m_destinations = destinations; +} + +quint64 FunctionCall::cost(int event) const +{ + QTC_ASSERT(event >= 0 && event < d->m_costs.size(), return 0); + return d->m_costs.at(event); +} + +QVector<quint64> FunctionCall::costs() const +{ + return d->m_costs; +} + +void FunctionCall::setCosts(const QVector<quint64> &costs) +{ + d->m_costs = costs; +} + + +} // Callgrind +} // Valgrind diff --git a/src/libs/valgrind/callgrind/callgrindfunctioncall.h b/src/libs/valgrind/callgrind/callgrindfunctioncall.h new file mode 100644 index 0000000000000000000000000000000000000000..4cb73a8beb0213717d240050cd6b6ebd259e6bc6 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindfunctioncall.h @@ -0,0 +1,98 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef LIBVALGRIND_CALLGRIND_CALLEE_H +#define LIBVALGRIND_CALLGRIND_CALLEE_H + +#include "../valgrind_global.h" + +#include <QtCore/QMetaType> + +QT_BEGIN_NAMESPACE +template <typename T> class QVector; +QT_END_NAMESPACE + +namespace Valgrind { +namespace Callgrind { + +class Function; + +/** + * This represents a function call. + */ +class VALGRINDSHARED_EXPORT FunctionCall { +public: + explicit FunctionCall(); + ~FunctionCall(); + + /// the called function + const Function *callee() const; + void setCallee(const Function *function); + + /// the calling function + const Function *caller() const; + void setCaller(const Function *function); + + /// how often the function was called + quint64 calls() const; + void setCalls(quint64 calls); + + /** + * Destination position data for the given position-index @p posIdx + * @see ParseData::positions() + */ + quint64 destination(int posIdx) const; + QVector<quint64> destinations() const; + void setDestinations(const QVector<quint64> &destinations); + + /** + * Inclusive cost of the function call. + * @see ParseData::events() + */ + quint64 cost(int event) const; + QVector<quint64> costs() const; + void setCosts(const QVector<quint64> &costs); + +private: + Q_DISABLE_COPY(FunctionCall); + + class Private; + Private *d; +}; + +} // Callgrind +} // Valgrind + +Q_DECLARE_METATYPE(const Valgrind::Callgrind::FunctionCall *); + +#endif // LIBVALGRIND_CALLGRIND_CALLEE_H diff --git a/src/libs/valgrind/callgrind/callgrindfunctioncycle.cpp b/src/libs/valgrind/callgrind/callgrindfunctioncycle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..58a8a4a02bf4655b8ec8732907100982fd613a06 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindfunctioncycle.cpp @@ -0,0 +1,119 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "callgrindfunctioncycle.h" +#include "callgrindfunction_p.h" + +#include "callgrindfunctioncall.h" +#include "callgrindparsedata.h" + +#include <QtCore/QStringList> +#include <QtCore/QDebug> + +namespace Valgrind { +namespace Callgrind { + +//BEGIN FunctionCycle::Private + +class FunctionCycle::Private : public Function::Private { +public: + Private(const ParseData *data); + QVector<const Function *> m_functions; +}; + +FunctionCycle::Private::Private(const ParseData *data) +: Function::Private(data) +{ + +} + +#define CYCLE_D static_cast<FunctionCycle::Private *>(this->d) + +//BEGIN FunctionCycle + +FunctionCycle::FunctionCycle(const ParseData *data) +: Function(new Private(data)) +{ +} + +FunctionCycle::~FunctionCycle() +{ + // d should be deleted by Function::~Function() +} + +void FunctionCycle::setFunctions(const QVector<const Function *> functions) +{ + Private *d = CYCLE_D; + + d->m_functions = functions; + + d->m_incomingCallMap.clear(); + d->m_outgoingCallMap.clear(); + d->m_called = 0; + d->m_selfCost.fill(0, d->m_data->events().size()); + d->m_inclusiveCost.fill(0, d->m_data->events().size()); + + foreach(const Function *func, functions) { + // just add up self cost + d->accumulateCost(d->m_selfCost, func->selfCosts()); + // add outgoing calls to functions that are not part of the cycle + foreach(const FunctionCall *call, func->outgoingCalls()) { + if (!functions.contains(call->callee())) + d->accumulateCall(call, Function::Private::Outgoing); + } + // add incoming calls from functions that are not part of the cycle + foreach(const FunctionCall *call, func->incomingCalls()) { + if (!functions.contains(call->caller())) { + d->accumulateCall(call, Function::Private::Incoming); + d->m_called += call->calls(); + d->accumulateCost(d->m_inclusiveCost, call->costs()); + } + } + } + // now substract self from incl. cost (see implementation of inclusiveCost()) + /// now substract self cost (see @c inclusiveCost() implementation) + for(int i = 0, c = d->m_inclusiveCost.size(); i < c; ++i) { + if (d->m_inclusiveCost.at(i) < d->m_selfCost.at(i)) + d->m_inclusiveCost[i] = 0; + else + d->m_inclusiveCost[i] -= d->m_selfCost.at(i); + } +} + +QVector<const Function *> FunctionCycle::functions() const +{ + return CYCLE_D->m_functions; +} + +} +} diff --git a/src/libs/valgrind/callgrind/callgrindfunctioncycle.h b/src/libs/valgrind/callgrind/callgrindfunctioncycle.h new file mode 100644 index 0000000000000000000000000000000000000000..28e4c988824a92185d98520e928230b835476b7e --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindfunctioncycle.h @@ -0,0 +1,69 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef LIBVALGRIND_CALLGRINDFUNCTIONCYCLE_H +#define LIBVALGRIND_CALLGRINDFUNCTIONCYCLE_H + +#include "callgrindfunction.h" + +namespace Valgrind { +namespace Callgrind { + +/** + * self cost of a function cycle: sum of self costs of functions in the cycle + * callers of a function cycle: set of callers to functions in the cycle + * excluding calls inside the cycle + * callees of a function cycle: set of callees from functions in the cycle + * excluding calees inside the cycle + * inclusive cost of a function cycle: sum of inclusive cost of callees of the cycle (see above) + */ +class VALGRINDSHARED_EXPORT FunctionCycle : public Function { +public: + explicit FunctionCycle(const ParseData *data); + virtual ~FunctionCycle(); + + /// sets the list of functions that make up this cycle + /// NOTE: ownership is *not* transferred to the cycle + void setFunctions(const QVector<const Function *> functions); + /// @return the functions that make up this cycle + QVector<const Function *> functions() const; + +private: + class Private; +}; + +} +} + + +#endif // LIBVALGRIND_CALLGRINDFUNCTIONCYCLE_H diff --git a/src/libs/valgrind/callgrind/callgrindparsedata.cpp b/src/libs/valgrind/callgrind/callgrindparsedata.cpp new file mode 100644 index 0000000000000000000000000000000000000000..83c9c0b69db93c9482676df94ce09309f30db39f --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindparsedata.cpp @@ -0,0 +1,377 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "callgrindparsedata.h" + +#include "callgrindfunction.h" +#include "callgrindcycledetection.h" +#include "callgrindfunctioncycle.h" + +#include <utils/qtcassert.h> + +#include <QtCore/QStringList> +#include <QtCore/QVector> +#include <QtCore/QHash> + +namespace Valgrind { +namespace Callgrind { + +//BEGIN ParseData::Private + +class ParseData::Private { +public: + Private(ParseData *q) + : m_lineNumberPositionIndex(-1) + , m_pid(0) + , m_part(0) + , m_version(0) + , m_cycleCacheValid(false) + , m_q(q) + { + } + + ~Private(); + + QStringList m_events; + QStringList m_positions; + int m_lineNumberPositionIndex; + QVector<quint64> m_totalCosts; + QVector<const Function *> m_functions; + QString m_command; + quint64 m_pid; + uint m_part; + QStringList m_descriptions; + int m_version; + QString m_creator; + + QHash<qint64, QHash<qint64, QVector<Function *> > > functionLookup; + + typedef QHash<qint64, QString> NameLookupTable; + QString stringForCompression(const NameLookupTable &lookup, qint64 id); + void addCompressedString(NameLookupTable &lookup, const QString &string, qint64 &id); + + NameLookupTable m_objectCompression; + NameLookupTable m_fileCompression; + NameLookupTable m_functionCompression; + + void cycleDetection(); + void cleanupFunctionCycles(); + bool m_cycleCacheValid; + QVector<const Function *> m_cycleCache; + + ParseData *m_q; +}; + +ParseData::Private::~Private() +{ + cleanupFunctionCycles(); + qDeleteAll(m_functions); +} + +void ParseData::Private::cleanupFunctionCycles() +{ + m_cycleCacheValid = false; + foreach(const Function *func, m_cycleCache) { + if (dynamic_cast<const FunctionCycle *>(func)) + delete func; + } + m_cycleCache.clear(); +} + +QString ParseData::Private::stringForCompression(const NameLookupTable &lookup, qint64 id) +{ + if (id == -1) { + return QString(); + } else { + QTC_ASSERT(lookup.contains(id), return QString()); + return lookup.value(id); + } +} + +void ParseData::Private::addCompressedString(NameLookupTable &lookup, const QString &string, + qint64 &id) +{ + QTC_ASSERT(!string.isEmpty(), return); + + if (id == -1) { + // for uncompressed files, use a hash of the string + id = qHash(string); + + if (lookup.contains(id)) { + QTC_ASSERT(lookup.value(id) == string, return); + return; + } + } + + QTC_ASSERT(!lookup.contains(id), return); + lookup.insert(id, string); +} + +void ParseData::Private::cycleDetection() +{ + if (m_cycleCacheValid) { + return; + } + cleanupFunctionCycles(); + Internal::CycleDetection algorithm(m_q); + m_cycleCache = algorithm.run(m_functions); + m_cycleCacheValid = true; +} + +//BEGIN ParseData + +ParseData::ParseData() +: d(new Private(this)) +{ + +} + +ParseData::~ParseData() +{ + delete d; +} + +QString ParseData::prettyStringForEvent(const QString &event) +{ + /* + From Callgrind documentation, see: http://valgrind.org/docs/manual/cg-manual.html#cg-manual.overview + + I cache reads (Ir, which equals the number of instructions executed), + I1 cache read misses (I1mr) and LL cache instruction read misses (ILmr). + D cache reads (Dr, which equals the number of memory reads), + D1 cache read misses (D1mr), and LL cache data read misses (DLmr). + D cache writes (Dw, which equals the number of memory writes), + D1 cache write misses (D1mw), and LL cache data write misses (DLmw). + Conditional branches executed (Bc) and conditional branches mispredicted (Bcm). + Indirect branches executed (Bi) and indirect branches mispredicted (Bim) + */ + + QTC_ASSERT(event.size() >= 2, return event) // should not happen + + bool isMiss = event.contains("m"); // else hit + bool isRead = event.contains("r"); // else write + + QString type; + if (event.contains("L")) + type = QT_TR_NOOP("Last-level"); // first, "L" overwrites the others + else if (event.at(0) == 'I') + type = QT_TR_NOOP("Instruction"); + else if (event.at(0) == 'D') + type = QT_TR_NOOP("Cache"); + else if (event.leftRef(2) == "Bc") + type = QT_TR_NOOP("Conditional branches"); + else if (event.leftRef(2) == "Bi") + type = QT_TR_NOOP("Indirect branches"); + + QStringList prettyString; + prettyString << type; + + if (event.at(1).isNumber()) + prettyString << QString("level %1").arg(event.at(1)); + prettyString << (isRead ? QT_TR_NOOP("read") : QT_TR_NOOP("write")); + + if (event.at(0) == 'B') + prettyString << (isMiss ? QT_TR_NOOP("mispredicted") : QT_TR_NOOP("executed")); + else + prettyString << (isMiss ? QT_TR_NOOP("miss") : QT_TR_NOOP("access")); + + // add original abbreviation + prettyString << QString("(%1)").arg(event); + + return prettyString.join(" "); +} + +QStringList ParseData::events() const +{ + return d->m_events; +} + +void ParseData::setEvents(const QStringList &events) +{ + d->m_events = events; + d->m_totalCosts.fill(0, d->m_events.size()); +} + +QString ParseData::prettyStringForPosition(const QString &position) +{ + if (position == "line") + return QT_TR_NOOP("Line:"); // as in: "line number" + else if (position == "instr") + return QT_TR_NOOP("Instruction"); // as in: "instruction address" + return QT_TR_NOOP("Position:"); // never reached, in theory +} + +QStringList ParseData::positions() const +{ + return d->m_positions; +} + +int ParseData::lineNumberPositionIndex() const +{ + return d->m_lineNumberPositionIndex; +} + +void ParseData::setPositions(const QStringList &positions) +{ + d->m_positions = positions; + d->m_lineNumberPositionIndex = -1; + for(int i = 0; i < positions.size(); ++i) { + if (positions.at(i) == "line") { + d->m_lineNumberPositionIndex = i; + break; + } + } +} + +quint64 ParseData::totalCost(uint event) const +{ + return d->m_totalCosts.at(event); +} + +void ParseData::setTotalCost(uint event, quint64 cost) +{ + d->m_totalCosts[event] = cost; +} + +QVector<const Function *> ParseData::functions(bool detectCycles) const +{ + if (detectCycles) { + d->cycleDetection(); + return d->m_cycleCache; + } + return d->m_functions; +} + +void ParseData::addFunction(const Function *function) +{ + d->m_cycleCacheValid = false; + d->m_functions.append(function); +} + +QString ParseData::command() const +{ + return d->m_command; +} + +void ParseData::setCommand(const QString &command) +{ + d->m_command = command; +} + +quint64 ParseData::pid() const +{ + return d->m_pid; +} + +void ParseData::setPid(quint64 pid) +{ + d->m_pid = pid; +} + +uint ParseData::part() const +{ + return d->m_part; +} + +void ParseData::setPart(uint part) const +{ + d->m_part = part; +} + +QStringList ParseData::descriptions() const +{ + return d->m_descriptions; +} + +void ParseData::addDescription(const QString &description) +{ + d->m_descriptions.append(description); +} + +void ParseData::setDescriptions(const QStringList &descriptions) +{ + d->m_descriptions = descriptions; +} + +int ParseData::version() const +{ + return d->m_version; +} + +void ParseData::setVersion(int version) +{ + d->m_version = version; +} + +QString ParseData::creator() const +{ + return d->m_creator; +} + +void ParseData::setCreator(const QString &creator) +{ + d->m_creator = creator; +} + +QString ParseData::stringForObjectCompression(qint64 id) const +{ + return d->stringForCompression(d->m_objectCompression, id); +} + +void ParseData::addCompressedObject(const QString &object, qint64 &id) +{ + d->addCompressedString(d->m_objectCompression, object, id); +} + +QString ParseData::stringForFileCompression(qint64 id) const +{ + return d->stringForCompression(d->m_fileCompression, id); +} + +void ParseData::addCompressedFile(const QString &file, qint64 &id) +{ + d->addCompressedString(d->m_fileCompression, file, id); +} + +QString ParseData::stringForFunctionCompression(qint64 id) const +{ + return d->stringForCompression(d->m_functionCompression, id); +} + +void ParseData::addCompressedFunction(const QString &function, qint64 &id) +{ + d->addCompressedString(d->m_functionCompression, function, id); +} + +} // Callgrind +} // Valgrind diff --git a/src/libs/valgrind/callgrind/callgrindparsedata.h b/src/libs/valgrind/callgrind/callgrindparsedata.h new file mode 100644 index 0000000000000000000000000000000000000000..6b9b6e5bf8d6ff8c1357598e505528e5d58827d8 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindparsedata.h @@ -0,0 +1,145 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef LIBVALGRIND_CALLGRIND_PARSEDATA_P_H +#define LIBVALGRIND_CALLGRIND_PARSEDATA_P_H + +#include "../valgrind_global.h" + +QT_BEGIN_NAMESPACE +class QString; +template <typename T> class QVector; +class QStringList; +QT_END_NAMESPACE + +namespace Valgrind { +namespace Callgrind { + +class Function; + +/** + * Represents all the information extracted from a callgrind data file. + */ +class VALGRINDSHARED_EXPORT ParseData { +public: + explicit ParseData(); + ~ParseData(); + + static QString prettyStringForEvent(const QString &event); + /// List of events reported in the data file. + QStringList events() const; + void setEvents(const QStringList &events); + + static QString prettyStringForPosition(const QString &position); + /// List of positions reported in the data file. + QStringList positions() const; + void setPositions(const QStringList &positions); + + /// the index of the line number in @c positions() + /// or -1 if no line numbers where reported. + int lineNumberPositionIndex() const; + + /** + * Total cost of @p event reported in the data file. + * + * @see events() + */ + quint64 totalCost(uint event) const; + void setTotalCost(uint event, quint64 cost); + + /** + * When @p detectCycles is set to true, the returned list will have all @c Function's in call + * cycles replaced with @c FunctionCycle. + * + * @return All functions that where reported in the data file. + */ + QVector<const Function *> functions(bool detectCycles = false) const; + /// NOTE: The @c ParseData will take ownership. + void addFunction(const Function *function); + + /// @return executed command with arguments + QString command() const; + void setCommand(const QString &command); + + /// @return pid of executed command + quint64 pid() const; + void setPid(quint64 pid); + + /// @return number of data, if callgrind_control --dump was used + uint part() const; + void setPart(uint part) const; + + /// @return list of desc: lines in the data + QStringList descriptions() const; + void addDescription(const QString &description); + void setDescriptions(const QStringList &descriptions); + + /// @return version of the callgrind data format + int version() const; + void setVersion(int version); + + /// @return creator of the data + QString creator() const; + void setCreator(const QString &creator); + + /** + * Internal name compression lookup table. + * + * We save the @c QString representations of the compressed data format only once here. + * This should make sure the memory consumption doesn't skyrocket as long + * as these strings are only displayed without applying detaching operations on them. + */ + + /// for Objects + QString stringForObjectCompression(qint64 id) const; + /// @p id if it is -1, an uncompressed string is assumed and it will be compressed internally + void addCompressedObject(const QString &object, qint64 &id); + + /// for Files + QString stringForFileCompression(qint64 id) const; + /// @p id if it is -1, an uncompressed string is assumed and it will be compressed internally + void addCompressedFile(const QString &file, qint64 &id); + + /// for Functions + QString stringForFunctionCompression(qint64 id) const; + /// @p id if it is -1, an uncompressed string is assumed and it will be compressed internally + void addCompressedFunction(const QString &function, qint64 &id); +private: + class Private; + Private *d; +}; + +} +} + +#endif // LIBVALGRIND_CALLGRIND_PARSEDATA_P_H diff --git a/src/libs/valgrind/callgrind/callgrindparser.cpp b/src/libs/valgrind/callgrind/callgrindparser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d859b827abea892c702d04bb1fd5565176c297b4 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindparser.cpp @@ -0,0 +1,681 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "callgrindparser.h" + +#include "callgrindparsedata.h" +#include "callgrindfunctioncall.h" +#include "callgrindcostitem.h" +#include "callgrindfunction.h" + +#include <utils/qtcassert.h> + +#include <QtCore/QHash> +#include <QtCore/QVector> +#include <QtCore/QStringList> +#include <QtCore/QDebug> + +// #define DEBUG_PARSER + +namespace { + +static void skipSpace(const char **current, const char *end) +{ + const char *b = *current; + while (b < end) { + if (*b == ' ' || *b == '\t') + b++; + else + break; + } + *current = b; +} + +// set *ok to true if at least one digit was parsed; "garbage" after the number is not considered +// an error. +// *current is moved to one char after the last digit +static qint64 parseDecimal(const char **current, const char *end, bool *ok) +{ + const char *b = *current; + bool parsedDigit = false; + qint64 ret = 0; + while (b < end) { + const char c = *b; + if (c >= '0' && c <= '9') { + b++; + ret *= 10; + ret += c - '0'; + parsedDigit = true; + } else { + break; + } + } + + *ok = parsedDigit; + *current = b; + return ret; +} + +//like parseDecimal, but for 0xabcd-style hex encoding (without the leading 0x) +static qint64 parseHex(const char **current, const char *end, bool *ok) +{ + const char *b = *current; + bool parsedDigit = false; + qint64 ret = 0; + while (b < end) { + char c = *b; + if (c >= '0' && c <= '9') + c &= 0x0f; + else if (c >= 'a' && c <= 'f') + c = c - 'a' + 10; + else + break; + + b++; + ret <<= 4; + ret += c; + parsedDigit = true; + } + + *ok = parsedDigit; + *current = b; + return ret; +} + +static quint64 parseAddr(const char **current, const char *end, bool *ok) +{ + if (**current == '0' && *(*current + 1) == 'x') { + *current += 2; + return parseHex(current, end, ok); + } else { + return parseDecimal(current, end, ok); + } +} + +// this function expects that *current already points one past the opening parenthesis +static int parseNameShorthand(const char **current, const char *end) +{ + bool ok; + int ret = parseDecimal(current, end, &ok); + if (ok) { + if (**current == ')') { + (*current)++; + return ret; + } + } + return -1; // invalid +} + +} + +namespace Valgrind { +namespace Callgrind { + +class Parser::Private +{ + Parser *const q; +public: + + explicit Private(Parser *qq) + : q(qq), + addressValuesCount(0), + costValuesCount(0), + data(0), + currentFunction(0), + lastObject(-1), + lastFile(-1), + currentDifferingFile(-1), + isParsingFunctionCall(false), + callsCount(0) + { + } + + ~Private() + { + delete data; + } + + void parse(QIODevice *device); + void parseHeader(QIODevice *device); + + typedef QPair<qint64, QString> NamePair; + NamePair parseName(const char *begin, const char *end); + + void dispatchLine(const QByteArray &line); + void parseCostItem(const char *begin, const char *end); + void parseFunction(const char *begin, const char *end); + void parseSourceFile(const char *begin, const char *end); + void parseDifferingSourceFile(const char *begin, const char *end); + void parseObjectFile(const char *begin, const char *end); + void parseCalls(const char *begin, const char *end); + void parseCalledFunction(const char *begin, const char *end); + void parseCalledSourceFile(const char *begin, const char *end); + void parseCalledObjectFile(const char *begin, const char *end); + + int addressValuesCount; + int costValuesCount; + + ParseData *data; + Function *currentFunction; + qint64 lastObject; + qint64 lastFile; + qint64 currentDifferingFile; + + bool isParsingFunctionCall; + quint64 callsCount; + struct CallData { + CallData() + : calledFunction(-1) + , calledObject(-1) + , calledFile(-1) + , call(0) + { + } + + qint64 calledFunction; + qint64 calledObject; + qint64 calledFile; + FunctionCall *call; + }; + CallData currentCallData; + QVector<quint64> callDestinations; + + // we can only resolve callees after parsing the whole file so save that data here for now + QVector<CallData> pendingCallees; + + // id(s) for the ??? file + QVector<quint64> unknownFiles; + + // functions which call themselves + QSet<Function *> recursiveFunctions; +}; + +void Parser::Private::parse(QIODevice *device) +{ + // be sure to clean up existing data before re-allocating + // the callee might not have taken the parse data + delete data; + data = 0; + + data = new ParseData; + parseHeader(device); + while (!device->atEnd()) { + QByteArray line = device->readLine(); + // empty lines actually have no meaning - only fn= starts a new function + if (line.length() > 1) + dispatchLine(line); + } + + // build fast lookup of functions by their nameId + QHash<qint64, QList<const Function *> > functionLookup; + foreach(const Function *function, data->functions()) { + functionLookup[function->nameId()].append(function); + } + + // functions that need to accumulate their calees + QSet<Function *> pendingFunctions; + foreach(const CallData &callData, pendingCallees) { + Function *calledFunction = 0; + QTC_ASSERT(callData.call, continue); + QTC_ASSERT(callData.call->caller(), continue); + foreach(const Function *function, functionLookup.value(callData.calledFunction)) { + QTC_ASSERT(function->nameId() == callData.calledFunction, continue); + if (function->objectId() == callData.calledObject + && function->fileId() == callData.calledFile) + { + calledFunction = const_cast<Function *>(function); + break; + } + } +#ifdef DEBUG_PARSER + if (!calledFunction) { + qDebug() << unknownFiles; + qDebug() << "could not find called function:" << data->stringForFunctionCompression(callData.calledFunction) << callData.calledFunction; + qDebug() << "caller is:" << callData.call->caller()->name() << callData.call->caller()->nameId(); + qDebug() << "called file:" << callData.calledFile << "object:" << callData.calledObject; + qDebug() << data->stringForFileCompression(callData.calledFile) << data->stringForObjectCompression(callData.calledObject); + foreach(const Function *function, functionLookup.value(callData.calledFunction)) { + qDebug() << "available function file:" << function->fileId() << function->file() << "object:" << function->objectId() << function->object(); + } + } +#endif + QTC_ASSERT(calledFunction, continue) + callData.call->setCallee(calledFunction); + calledFunction->addIncomingCall(callData.call); + + Function *caller = const_cast<Function *>(callData.call->caller()); + caller->addOutgoingCall(callData.call); + pendingFunctions.insert(caller); + } + + pendingCallees.clear(); + // lookup done + + // now accumulate callees + foreach(Function *func, pendingFunctions) + func->finalize(); + + q->parserDataReady(); // emit +} + +inline QString getValue(const QByteArray &line, const int prefixLength) +{ + // we are not interested in the trailing newline + // TODO: \r\n ? + return QString::fromLatin1(line.mid(prefixLength, line.length() - 1 - prefixLength).constData()); +} + +void Parser::Private::parseHeader(QIODevice *device) +{ + QTC_ASSERT(device->isOpen(), return); + QTC_ASSERT(device->isReadable(), return); + + // parse expected headers until we hit the first non-empty line + while (!device->atEnd()) { + QByteArray line = device->readLine(); + + // now that we're done checking if we're done (heh) with the header, parse the address + // and cost column descriptions. speed is unimportant here. + if (line.startsWith("positions: ")) { + QString values = getValue(line, 11); + data->setPositions(values.split(QLatin1Char(' '), QString::SkipEmptyParts)); + addressValuesCount = data->positions().count(); + } else if (line.startsWith("events: ")) { + QString values = getValue(line, 8); + data->setEvents(values.split(QLatin1Char(' '), QString::SkipEmptyParts)); + costValuesCount = data->events().count(); + } else if (line.startsWith("version: ")) { + QString value = getValue(line, 9); + data->setVersion(value.toInt()); + } else if (line.startsWith("creator: ")) { + QString value = getValue(line, 9); + data->setCreator(value); + } else if (line.startsWith("pid: ")) { + QString value = getValue(line, 5); + data->setPid(value.toULongLong()); + } else if (line.startsWith("cmd: ")) { + QString value = getValue(line, 5); + data->setCommand(value); + } else if (line.startsWith("part: ")) { + QString value = getValue(line, 6); + data->setPart(value.toUInt()); + } else if (line.startsWith("desc: ")) { + QString value = getValue(line, 6); + data->addDescription(value); + } else if (line.startsWith("summary: ")) { + QString values = getValue(line, 9); + uint i = 0; + foreach(const QString value, values.split(QLatin1Char(' '), QString::SkipEmptyParts)) + data->setTotalCost(i++, value.toULongLong()); + } else if (!line.trimmed().isEmpty()) { + // handle line and exit parseHeader + dispatchLine(line); + return; + } + } +} + +Parser::Private::NamePair Parser::Private::parseName(const char *begin, const char *end) +{ + const char *current = begin; + qint64 nameShorthand = -1; + if (*current == '(') { + current++; + if ((nameShorthand = parseNameShorthand(¤t, end)) == -1) + return qMakePair(qint64(-1), QString()); // error + } + + skipSpace(¤t, end); + return qMakePair(nameShorthand, QString::fromUtf8(QByteArray(current, end - current))); +} + +/* + * fl means source file + * ob means object file + * fn means function + * fe, fi means different source file + * cfi or cfl means called source file + * cob means called object file + * cfn means called function + */ + +void Parser::Private::dispatchLine(const QByteArray &line) +{ + const char *const begin = line.constData(); + const char *const end = begin + line.length() - 1; // we're not interested in the '\n' + const char *current = begin; + + // shortest possible line is "1 1" - a cost item line + QTC_ASSERT(end - begin >= 3, return); + + const char first = *begin; + + if ((first >= '0' && first <= '9') || first == '+' || first == '*' || first =='-') { + parseCostItem(begin, end); + if (isParsingFunctionCall) + isParsingFunctionCall = false; + return; + } + + QTC_ASSERT(!isParsingFunctionCall, return); + + current++; + const char second = *current++; + const char third = *current++; + // current now points to the fourth char... + + if (first == 'c') { + // information about a callee + const char fourth = *current++; + // current now points to the fifth char... + + switch (second) { + // comments show the shortest possible line for every case + case 'a': + { + // "calls=1 1", length 9 + QTC_ASSERT(end - begin >= 9, return); + const char fifth = *current++; + const char sixth = *current++; + if (third == 'l' && fourth == 'l' && fifth == 's' && sixth == '=') + parseCalls(current, end); + break; + } + case 'f': + QTC_ASSERT(end - begin >= 5, return); + // "cfi=a" / "cfl=a", length 5 + // "cfn=a", length 5 + if (fourth == '=') { + if (third == 'i' || third == 'l') + parseCalledSourceFile(current, end); + else if (third == 'n') + parseCalledFunction(current, end); + } + break; + case 'o': + QTC_ASSERT(end - begin >= 5, return); + // "cob=a", length 5 + if (third == 'b' && fourth == '=') + parseCalledObjectFile(current, end); + break; + default: + break; + } + + } else { + // information about this function + // shortest possible line is always four chars here, of the type "fl=a" + QTC_ASSERT(end - begin >= 4, return); + if (third == '=') { + if (first == 'f') { + if (second == 'l') + parseSourceFile(current, end); + else if (second == 'n') + parseFunction(current, end); + else if (second == 'i' || second == 'e') + parseDifferingSourceFile(current, end); + } else if (first == 'o' && second == 'b') { + parseObjectFile(current, end); + } + } + } +} + +void Parser::Private::parseCostItem(const char *begin, const char *end) +{ + QTC_ASSERT(currentFunction, return); + + bool ok; + const char *current = begin; + + CostItem *costItem = new CostItem(data); + QTC_ASSERT(currentDifferingFile == -1 || currentDifferingFile != currentFunction->fileId(), return); + costItem->setDifferingFile(currentDifferingFile); + FunctionCall *call = 0; + if (isParsingFunctionCall) { + call = new FunctionCall; + call->setCaller(currentFunction); + + currentCallData.call = call; + costItem->setCall(call); + call->setCalls(callsCount); + callsCount = 0; + + call->setDestinations(callDestinations); + callDestinations.clear(); + + if (currentCallData.calledFile == -1) { + currentCallData.calledFile = currentDifferingFile != -1 ? currentDifferingFile : lastFile; + //HACK: workaround issue where sometimes fi=??? lines are prepended to function calls + if (unknownFiles.contains(currentCallData.calledFile)) + currentCallData.calledFile = lastFile; + } + if (currentCallData.calledObject == -1) + currentCallData.calledObject = lastObject; + + + if (currentCallData.calledFunction == currentFunction->nameId() && + currentCallData.calledFile == currentFunction->fileId() && + currentCallData.calledObject == currentFunction->objectId() ) + { + // recursive call, + recursiveFunctions << currentFunction; + } + + pendingCallees.append(currentCallData); + currentCallData = CallData(); + } + + const CostItem *lastCostItem = 0; + if (!currentFunction->costItems().isEmpty()) + lastCostItem = currentFunction->costItems().last(); + + // parse positions ("where") + for (int i = 0; i < addressValuesCount; ++i) { + char c = *current; + // TODO overflow checks + quint64 position = 0; + if (c == '*') { + // leave the old value unchanged + current++; + QTC_ASSERT(lastCostItem, continue); + position = lastCostItem->position(i); + } else { + if (c == '+' || c == '-') + current++; + + quint64 addr = parseAddr(¤t, end, &ok); + + if (!ok) + break; /// TODO: error reporting + + if (c == '+') { + QTC_ASSERT(lastCostItem, continue); + position = lastCostItem->position(i) + addr; + } else if (c == '-') { + QTC_ASSERT(lastCostItem, continue); + position = lastCostItem->position(i) - addr; + } else + position = addr; + } + costItem->setPosition(i, position); + skipSpace(¤t, end); + } + + // parse events ("what") + for (int i = 0; i < costValuesCount; ++i) { + quint64 parsedCost = parseDecimal(¤t, end, &ok); + if (!ok) + break; /// TODO: error reporting + costItem->setCost(i, parsedCost); + skipSpace(¤t, end); + } + + if (call) { + call->setCosts(costItem->costs()); + } + + currentFunction->addCostItem(costItem); +} + +void Parser::Private::parseSourceFile(const char *begin, const char *end) +{ + NamePair name = parseName(begin, end); + + if (!name.second.isEmpty()) { + data->addCompressedFile(name.second, name.first); + if (name.second == QLatin1String("???")) + unknownFiles << name.first; + } + + lastFile = name.first; + currentDifferingFile = -1; +} + +void Parser::Private::parseFunction(const char *begin, const char *end) +{ + currentFunction = new Function(data); + currentFunction->setFile(lastFile); + currentFunction->setObject(lastObject); + + data->addFunction(currentFunction); + + NamePair name = parseName(begin, end); + + if (!name.second.isEmpty()) + data->addCompressedFunction(name.second, name.first); + + currentFunction->setName(name.first); +} + +void Parser::Private::parseDifferingSourceFile(const char *begin, const char *end) +{ + NamePair name = parseName(begin, end); + + if (!name.second.isEmpty()) { + data->addCompressedFile(name.second, name.first); + if (name.second == QLatin1String("???")) + unknownFiles << name.first; + } + + if (name.first == currentFunction->fileId()) + currentDifferingFile = -1; + else + currentDifferingFile = name.first; +} + +void Parser::Private::parseObjectFile(const char *begin, const char *end) +{ + NamePair name = parseName(begin, end); + if (!name.second.isEmpty()) + data->addCompressedObject(name.second, name.first); + + lastObject = name.first; +} + +void Parser::Private::parseCalls(const char *begin, const char *end) +{ + const char *current = begin; + bool ok; + callsCount = parseDecimal(¤t, end, &ok); + skipSpace(¤t, end); + + callDestinations.fill(0, addressValuesCount); + for(int i = 0; i < addressValuesCount; ++i) { + callDestinations[i] = parseAddr(¤t, end, &ok); + if (!ok) + break; // TODO error handling? + skipSpace(¤t, end); + } + + isParsingFunctionCall = true; +} + +void Parser::Private::parseCalledFunction(const char *begin, const char *end) +{ + NamePair name = parseName(begin, end); + if (!name.second.isEmpty()) + data->addCompressedFunction(name.second, name.first); + + currentCallData.calledFunction = name.first; +} + +void Parser::Private::parseCalledSourceFile(const char *begin, const char *end) +{ + NamePair name = parseName(begin, end); + if (!name.second.isEmpty()) { + data->addCompressedFile(name.second, name.first); + if (name.second == QLatin1String("???")) + unknownFiles << name.first; + } + + currentCallData.calledFile = name.first; +} + +void Parser::Private::parseCalledObjectFile(const char *begin, const char *end) +{ + NamePair name = parseName(begin, end); + if (!name.second.isEmpty()) + data->addCompressedObject(name.second, name.first); + + currentCallData.calledObject = name.first; +} + +//BEGIN Parser + +void Parser::parse(QIODevice *device) +{ + d->parse(device); +} + +Parser::Parser(QObject *parent) + : QObject(parent), + d(new Private(this)) +{ +} + +Parser::~Parser() +{ + delete d; +} + +ParseData *Parser::takeData() +{ + ParseData *data = d->data; + d->data = 0; + return data; +} + + +} //Callgrind +} //Valgrind diff --git a/src/libs/valgrind/callgrind/callgrindparser.h b/src/libs/valgrind/callgrind/callgrindparser.h new file mode 100644 index 0000000000000000000000000000000000000000..2762a4ae4e09099a8f05c1bce4e23693ab12eef6 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindparser.h @@ -0,0 +1,85 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef LIBVALGRIND_CALLGRIND_PARSER_H +#define LIBVALGRIND_CALLGRIND_PARSER_H + +#include "../valgrind_global.h" + +#include <QObject> + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +namespace Valgrind { +namespace Callgrind { + +class ParseData; + +/** + * Parser for Valgrind --tool=callgrind output + * most of the format is documented at http://kcachegrind.sourceforge.net/html/CallgrindFormat.html + * + * FIXME: most length asserts are not correct, see documentation 1.2: + * "If a cost line specifies less event counts than given in the "events" line, + * the rest is assumed to be zero." + * + */ +class VALGRINDSHARED_EXPORT Parser : public QObject { + Q_OBJECT +public: + explicit Parser(QObject *parent=0); + ~Parser(); + + // get and take ownership of the parsing results. If this method is not called the repository + // will be destroyed when the parser is destroyed. Subsequent calls return null. + ParseData *takeData(); + +signals: + void parserDataReady(); + +public Q_SLOTS: + void parse(QIODevice *stream); + +private: + Q_DISABLE_COPY(Parser) + + class Private; + Private *const d; +}; + +} // Callgrind +} // Valgrind + +#endif //LIBVALGRIND_CALLGRIND_PARSER_H diff --git a/src/libs/valgrind/callgrind/callgrindproxymodel.cpp b/src/libs/valgrind/callgrind/callgrindproxymodel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9d172d2eb7012391e18e1ebfd2a6bed95fa8a99 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindproxymodel.cpp @@ -0,0 +1,162 @@ +/************************************************************************** +** +** This file is part of Qt Creator Analyzer Tools +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "callgrindproxymodel.h" + +#include "callgrinddatamodel.h" +#include "callgrindfunction.h" +#include "callgrindfunctioncall.h" +#include "callgrindparsedata.h" + +#include <utils/qtcassert.h> + +#include <QDebug> + +using namespace Valgrind::Callgrind; + +DataProxyModel::DataProxyModel(QObject *parent) + : QSortFilterProxyModel(parent) + , m_function(0) + , m_maxRows(0) + , m_minimumInclusiveCostRatio(0.0) +{ + setDynamicSortFilter(true); +} + +const Function *DataProxyModel::filterFunction() const +{ + return m_function; +} + +void DataProxyModel::setFilterBaseDir ( const QString &baseDir ) +{ + if (m_baseDir == baseDir) + return; + + m_baseDir = baseDir; + invalidateFilter(); +} + +void DataProxyModel::setFilterFunction(const Function *function) +{ + if (m_function == function) + return; + + const Function *previousFunction = m_function; + m_function = function; + invalidateFilter(); + emit filterFunctionChanged(previousFunction, function); +} + +void DataProxyModel::setFilterMaximumRows(int rows) +{ + if (m_maxRows == rows) + return; + + m_maxRows = rows; + invalidateFilter(); + emit filterMaximumRowsChanged(rows); +} + +void DataProxyModel::setMinimumInclusiveCostRatio(double minimumInclusiveCost) +{ + if (m_minimumInclusiveCostRatio == minimumInclusiveCost) + return; + + m_minimumInclusiveCostRatio = minimumInclusiveCost; + invalidateFilter(); +} + +void DataProxyModel::setSourceModel(QAbstractItemModel *sourceModel) +{ + if (!qobject_cast<DataModel *>(sourceModel)) { + qWarning() << Q_FUNC_INFO << "accepts DataModel instances only"; + return; + } + + QSortFilterProxyModel::setSourceModel(sourceModel); +} + +DataModel *DataProxyModel::dataModel() const +{ + return qobject_cast<DataModel *>(sourceModel()); +} + +bool DataProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + const QModelIndex source_index = sourceModel()->index( source_row, 0, source_parent ); + if (!source_index.isValid()) + return false; + + // if the filter regexp is a non-empty string, ignore our filters + if (!filterRegExp().isEmpty()) { + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); + } + + // check max rows + if (m_maxRows > 0 && source_row > m_maxRows) + return false; + + const Function *func = source_index.data(DataModel::FunctionRole).value<const Function *>(); + + // check if func is located in the specific base directory, if any + if (func && !m_baseDir.isEmpty()) { + if (!func->location().startsWith(m_baseDir)) + return false; + } + + // check if the function from this index is a child of (called by) the filter function + if (func && m_function) { + bool isValid = false; + foreach(const FunctionCall *call, func->incomingCalls()) { + if (call->caller() == m_function) { + isValid = true; + break; + } + } + if (!isValid) { + return false; + } + } + + // check minimum inclusive costs + DataModel *model = dataModel(); + QTC_ASSERT(model, return false) // as always: this should never happen + const ParseData *data = model->parseData(); + QTC_ASSERT(data, return false) + if (m_minimumInclusiveCostRatio != 0.0) { + const quint64 totalCost = data->totalCost(0); + const quint64 inclusiveCost = func->inclusiveCost(0); + const float inclusiveCostRatio = (float)inclusiveCost / totalCost; + if (inclusiveCostRatio < m_minimumInclusiveCostRatio) + return false; + } + + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); +} diff --git a/src/libs/valgrind/callgrind/callgrindproxymodel.h b/src/libs/valgrind/callgrind/callgrindproxymodel.h new file mode 100644 index 0000000000000000000000000000000000000000..8932b89842d9b86cc928766f61cb1b765279d025 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindproxymodel.h @@ -0,0 +1,88 @@ +/************************************************************************** +** +** This file is part of Qt Creator Analyzer Tools +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef VALGRIND_CALLGRIND_CALLGRINDPROXYMODEL_H +#define VALGRIND_CALLGRIND_CALLGRINDPROXYMODEL_H + +#include <QSortFilterProxyModel> + +#include "../valgrind_global.h" + +namespace Valgrind { +namespace Callgrind { + +class DataModel; +class Function; + +class VALGRINDSHARED_EXPORT DataProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + explicit DataProxyModel(QObject *parent = 0); + + virtual void setSourceModel(QAbstractItemModel *sourceModel); + + QString filterBaseDir() const { return m_baseDir; } + const Function *filterFunction() const; + int filterMaximumRows() const { return m_maxRows; } + + /// Only functions with an inclusive cost ratio above this minimum will be shown in the model + double minimumInclusiveCostRatio() const { return m_minimumInclusiveCostRatio; } + +public Q_SLOTS: + /// This will filter out all entries that are not located within \param baseDir + void setFilterBaseDir(const QString& baseDir); + void setFilterFunction(const Function *call); + void setFilterMaximumRows(int rows); + + /// Only rows with a inclusive cost ratio above @p minimumInclusiveCost will be shown + /// by this model. If @c 0 is passed as argument, all rows will be shown. + void setMinimumInclusiveCostRatio(double minimumInclusiveCost); + +Q_SIGNALS: + void filterFunctionChanged(const Function *previous, const Function *current); + void filterMaximumRowsChanged(int rows); + +protected: + virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + +private: + DataModel *dataModel() const; + + QString m_baseDir; + const Function *m_function; + int m_maxRows; + double m_minimumInclusiveCostRatio; +}; + +} +} + +#endif // VALGRIND_CALLGRIND_CALLGRINDPROXYMODEL_H diff --git a/src/libs/valgrind/callgrind/callgrindrunner.cpp b/src/libs/valgrind/callgrind/callgrindrunner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e129b796b90221200952a77b5abc07932f9d993 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindrunner.cpp @@ -0,0 +1,134 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "callgrindrunner.h" + +#include <utils/qtcassert.h> + +#include <QtCore/QFile> + +#include "callgrindparser.h" + +using namespace Valgrind::Callgrind; + +CallgrindRunner::CallgrindRunner(QObject *parent) + : ValgrindRunner(parent) + , m_controller(new CallgrindController(this)) + , m_parser(new Parser(this)) + , m_paused(false) +{ + connect(m_controller, + SIGNAL(finished(Valgrind::Callgrind::CallgrindController::Option)), + SLOT(controllerFinished(Valgrind::Callgrind::CallgrindController::Option))); + connect(m_controller, SIGNAL(localParseDataAvailable(QString)), + this, SLOT(localParseDataAvailable(QString))); + connect(m_controller, SIGNAL(statusMessage(QString)), + this, SIGNAL(statusMessage(QString))); +} + +QString CallgrindRunner::tool() const +{ + return QString("callgrind"); +} + +Parser *CallgrindRunner::parser() const +{ + return m_parser; +} + +CallgrindController *CallgrindRunner::controller() const +{ + return m_controller; +} + +void CallgrindRunner::start() +{ + ValgrindRunner::start(); + m_controller->setValgrindProcess(valgrindProcess()); +} + +void CallgrindRunner::startRemotely(const Utils::SshConnectionParameters &sshParams) +{ + ValgrindRunner::startRemotely(sshParams); + m_controller->setValgrindProcess(valgrindProcess()); +} + +void CallgrindRunner::processFinished(int ret, QProcess::ExitStatus status) +{ + triggerParse(); + m_controller->setValgrindProcess(0); + + ValgrindRunner::processFinished(ret, status); // call base class method +} + +bool CallgrindRunner::isPaused() const +{ + return m_paused; +} + +void CallgrindRunner::triggerParse() +{ + m_controller->getLocalDataFile(); +} + +void CallgrindRunner::localParseDataAvailable(const QString &file) +{ + // parse the callgrind file + QTC_ASSERT(!file.isEmpty(), return); + QFile outputFile(file); + QTC_ASSERT(outputFile.exists(), return); + if (outputFile.open(QIODevice::ReadOnly)) { + emit statusMessage(tr("Parsing Profile Data...")); + m_parser->parse(&outputFile); + } else { + qWarning() << "Could not open file for parsing:" << outputFile.fileName(); + } +} + +void CallgrindRunner::controllerFinished(CallgrindController::Option option) +{ + switch(option) + { + case CallgrindController::Pause: + m_paused = true; + break; + case CallgrindController::UnPause: + m_paused = false; + break; + case CallgrindController::Dump: + triggerParse(); + break; + default: + break; // do nothing + } +} diff --git a/src/libs/valgrind/callgrind/callgrindrunner.h b/src/libs/valgrind/callgrind/callgrindrunner.h new file mode 100644 index 0000000000000000000000000000000000000000..97ec9ef7ed305410585fa9e7f37889556b29ccae --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindrunner.h @@ -0,0 +1,88 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef VALGRIND_CALLGRIND_CALLGRINDRUNNER_H +#define VALGRIND_CALLGRIND_CALLGRINDRUNNER_H + +#include <valgrind/valgrindrunner.h> +#include <valgrind/valgrind_global.h> + +#include "callgrindcontroller.h" + +namespace Valgrind { +namespace Callgrind { + +class Parser; +class CallgrindController; + +class VALGRINDSHARED_EXPORT CallgrindRunner : public ValgrindRunner +{ + Q_OBJECT + +public: + explicit CallgrindRunner(QObject *parent = 0); + + Parser *parser() const; + + CallgrindController *controller() const; + + bool isPaused() const; + + virtual void start(); + virtual void startRemotely(const Utils::SshConnectionParameters &sshParams); + +signals: + void statusMessage(const QString &message); + +private slots: + void localParseDataAvailable(const QString &file); + + void controllerFinished(Valgrind::Callgrind::CallgrindController::Option); + + void processFinished(int, QProcess::ExitStatus); + +private: + void triggerParse(); + + QString tool() const; + + CallgrindController *m_controller; + Parser *m_parser; + + bool m_paused; +}; + +} // namespace Callgrind +} // namespace Valgrind + +#endif // VALGRIND_CALLGRIND_CALLGRINDRUNNER_H diff --git a/src/libs/valgrind/callgrind/callgrindstackbrowser.cpp b/src/libs/valgrind/callgrind/callgrindstackbrowser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5bd548f75c0814d7ba6b4b560a3a7452544d9e2a --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindstackbrowser.cpp @@ -0,0 +1,101 @@ +/************************************************************************** +** +** This file is part of Qt Creator Analyzer Tools +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "callgrindstackbrowser.h" + +using namespace Valgrind::Callgrind; + +HistoryItem::HistoryItem(StackBrowser *stack) +{ + if (stack) + stack->select(this); +} + +HistoryItem::~HistoryItem() +{ + +} + + +FunctionHistoryItem::FunctionHistoryItem(const Function *function, StackBrowser *stack) + : HistoryItem(stack) + , m_function(function) +{ +} + +FunctionHistoryItem::~FunctionHistoryItem() +{ +} + +StackBrowser::StackBrowser(QObject *parent) + : QObject(parent) +{ +} + +StackBrowser::~StackBrowser() +{ + qDeleteAll(m_stack); + m_stack.clear(); +} + +void StackBrowser::clear() +{ + qDeleteAll(m_stack); + m_stack.clear(); + emit currentChanged(); +} + +int StackBrowser::size() const +{ + return m_stack.size(); +} + +void StackBrowser::select(HistoryItem *item) +{ + if (!m_stack.isEmpty() && m_stack.top() == item) + return; + + m_stack.push(item); + emit currentChanged(); +} + +HistoryItem *StackBrowser::current() const +{ + return m_stack.isEmpty() ? 0 : m_stack.top(); +} + +void StackBrowser::goBack() +{ + if (m_stack.isEmpty()) + return; + + HistoryItem *item = m_stack.pop(); + delete item; + emit currentChanged(); +} diff --git a/src/libs/valgrind/callgrind/callgrindstackbrowser.h b/src/libs/valgrind/callgrind/callgrindstackbrowser.h new file mode 100644 index 0000000000000000000000000000000000000000..fc6fb29db583523626efcfaea49997802936d4a4 --- /dev/null +++ b/src/libs/valgrind/callgrind/callgrindstackbrowser.h @@ -0,0 +1,90 @@ +/************************************************************************** +** +** This file is part of Qt Creator Analyzer Tools +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CALLGRINDSTACKBROWSER_H +#define CALLGRINDSTACKBROWSER_H + +#include "../valgrind_global.h" + +#include <QObject> +#include <QStack> + +namespace Valgrind { +namespace Callgrind { + +class Function; +class StackBrowser; + +class VALGRINDSHARED_EXPORT HistoryItem +{ +public: + HistoryItem(StackBrowser *stack = 0); + virtual ~HistoryItem(); +}; + +class VALGRINDSHARED_EXPORT FunctionHistoryItem : public HistoryItem +{ +public: + FunctionHistoryItem(const Function *function, StackBrowser *stack = 0); + virtual ~FunctionHistoryItem(); + + const Function *function() const { return m_function; } + +private: + const Function *m_function; +}; + +class VALGRINDSHARED_EXPORT StackBrowser : public QObject +{ + Q_OBJECT + +public: + explicit StackBrowser(QObject *parent = 0); + virtual ~StackBrowser(); + + void select(HistoryItem *item); + HistoryItem *current() const; + + void clear(); + int size() const; + +public Q_SLOTS: + void goBack(); + +Q_SIGNALS: + void currentChanged(); + +private: + QStack<HistoryItem *> m_stack; +}; + +} +} + +#endif // CALLGRINDSTACKBROWSER_H diff --git a/src/libs/valgrind/valgrind.pro b/src/libs/valgrind/valgrind.pro index cc795b2ba339645ce69298f9d46a57e5f8f0eff2..b670b92bff035dd037443d562c418b4bf9723b67 100644 --- a/src/libs/valgrind/valgrind.pro +++ b/src/libs/valgrind/valgrind.pro @@ -22,6 +22,21 @@ HEADERS += valgrind_global.h \ xmlprotocol/errorlistmodel.h \ xmlprotocol/stackmodel.h \ xmlprotocol/modelhelpers.h \ + callgrind/callgrindparser.h \ + callgrind/callgrindparsedata.h \ + callgrind/callgrindfunction.h \ + callgrind/callgrindfunction_p.h \ + callgrind/callgrindfunctioncycle.h \ + callgrind/callgrindfunctioncall.h \ + callgrind/callgrindcostitem.h \ + callgrind/callgrinddatamodel.h \ + callgrind/callgrindabstractmodel.h \ + callgrind/callgrindcallmodel.h \ + callgrind/callgrindcontroller.h \ + callgrind/callgrindcycledetection.h \ + callgrind/callgrindproxymodel.h \ + callgrind/callgrindstackbrowser.h \ + callgrind/callgrindrunner.h \ memcheck/memcheckrunner.h \ valgrindrunner.h \ valgrindprocess.h @@ -37,6 +52,20 @@ SOURCES += xmlprotocol/error.cpp \ xmlprotocol/errorlistmodel.cpp \ xmlprotocol/stackmodel.cpp \ xmlprotocol/modelhelpers.cpp \ + callgrind/callgrindparser.cpp \ + callgrind/callgrindparsedata.cpp \ + callgrind/callgrindfunction.cpp \ + callgrind/callgrindfunctioncycle.cpp \ + callgrind/callgrindfunctioncall.cpp \ + callgrind/callgrindcostitem.cpp \ + callgrind/callgrindabstractmodel.cpp \ + callgrind/callgrinddatamodel.cpp \ + callgrind/callgrindcallmodel.cpp \ + callgrind/callgrindcontroller.cpp \ + callgrind/callgrindcycledetection.cpp \ + callgrind/callgrindproxymodel.cpp \ + callgrind/callgrindrunner.cpp \ + callgrind/callgrindstackbrowser.cpp \ memcheck/memcheckrunner.cpp \ valgrindrunner.cpp \ valgrindprocess.cpp