Add callgrind support to valgrind library.

Merge-request: 284
Reviewed-by: hjk <qtc-committer@nokia.com>
This commit is contained in:
Mike McQuaid
2011-04-04 14:39:27 +02:00
committed by hjk
parent 472833ebbd
commit 86f90c4a4e
30 changed files with 4758 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}
}
}
}

View File

@@ -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

View File

@@ -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("-", "&#8209;");
}
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();
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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(&current, end)) == -1)
return qMakePair(qint64(-1), QString()); // error
}
skipSpace(&current, 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(&current, 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(&current, end);
}
// parse events ("what")
for (int i = 0; i < costValuesCount; ++i) {
quint64 parsedCost = parseDecimal(&current, end, &ok);
if (!ok)
break; /// TODO: error reporting
costItem->setCost(i, parsedCost);
skipSpace(&current, 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(&current, end, &ok);
skipSpace(&current, end);
callDestinations.fill(0, addressValuesCount);
for(int i = 0; i < addressValuesCount; ++i) {
callDestinations[i] = parseAddr(&current, end, &ok);
if (!ok)
break; // TODO error handling?
skipSpace(&current, 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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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