forked from qt-creator/qt-creator
Add callgrind tests for valgrind library.
Merge-request: 284 Reviewed-by: hjk <qtc-committer@nokia.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -126,3 +126,5 @@ tests/valgrind/memcheck/testapps/uninit1/uninit1
|
||||
tests/valgrind/memcheck/testapps/uninit2/uninit2
|
||||
tests/valgrind/memcheck/testapps/uninit3/uninit3
|
||||
tests/valgrind/memcheck/testrunner
|
||||
tests/valgrind/callgrind/callgrindparsertests
|
||||
tests/valgrind/callgrind/modeltest
|
||||
|
@@ -1,6 +1,15 @@
|
||||
# How to Compile
|
||||
# HOWTO COMPILE
|
||||
|
||||
cd qtc-build # go to your build folder of qtc
|
||||
# go to your build folder of Qt Creator
|
||||
cd /path/to/qtc-build
|
||||
|
||||
# create and enter build directory for valgrind tests
|
||||
mkdir valgrind-test
|
||||
cd valgrind-test
|
||||
|
||||
# make library paths known to ldconfig
|
||||
export LD_LIBRARY_PATH=/path/to/qtc-build/lib/qtcreator:/path/to/qtc-build/lib/qtcreator/plugins/Nokia
|
||||
|
||||
# run qmake, make
|
||||
qmake CONFIG+=debug IDE_BUILD_TREE=$(readlink -f ..) ../../path/to/qtc/tests/valgrind
|
||||
make
|
||||
|
3
tests/valgrind/callgrind/callgrind.pro
Normal file
3
tests/valgrind/callgrind/callgrind.pro
Normal file
@@ -0,0 +1,3 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += callgrindparsertests.pro modeltest.pro
|
476
tests/valgrind/callgrind/callgrindparsertests.cpp
Normal file
476
tests/valgrind/callgrind/callgrindparsertests.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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 "callgrindparsertests.h"
|
||||
|
||||
#include <valgrind/callgrind/callgrindparser.h>
|
||||
#include <valgrind/callgrind/callgrindfunctioncall.h>
|
||||
#include <valgrind/callgrind/callgrindfunction.h>
|
||||
#include <valgrind/callgrind/callgrindfunctioncycle.h>
|
||||
#include <valgrind/callgrind/callgrindcostitem.h>
|
||||
#include <valgrind/callgrind/callgrindparsedata.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QString>
|
||||
#include <QtTest/QTest>
|
||||
|
||||
using namespace Valgrind;
|
||||
using namespace Valgrind::Callgrind;
|
||||
|
||||
namespace {
|
||||
|
||||
static QString dataFile(const char *file)
|
||||
{
|
||||
return QLatin1String(PARSERTESTS_DATA_DIR) + QLatin1String("/") + QLatin1String(file);
|
||||
}
|
||||
|
||||
void testCostItem(const CostItem *item, quint64 expectedPosition, quint64 expectedCost)
|
||||
{
|
||||
QCOMPARE(item->cost(0), expectedCost);
|
||||
QCOMPARE(item->position(0), expectedPosition);
|
||||
}
|
||||
|
||||
void testSimpleCostItem(const CostItem *item, quint64 expectedPosition, quint64 expectedCost)
|
||||
{
|
||||
QVERIFY(item->differingFileId() == -1);
|
||||
QVERIFY(!item->call());
|
||||
QCOMPARE(item->differingFileId(), qint64(-1));
|
||||
|
||||
testCostItem(item, expectedPosition, expectedCost);
|
||||
}
|
||||
|
||||
void testFunctionCall(const FunctionCall *call, const Function *caller, const Function *callee,
|
||||
quint64 expectedCost, quint64 expectedCalls, quint64 expectedDestination)
|
||||
{
|
||||
QCOMPARE(call->callee(), callee);
|
||||
QCOMPARE(call->caller(), caller);
|
||||
QCOMPARE(call->cost(0), expectedCost);
|
||||
QCOMPARE(call->calls(), expectedCalls);
|
||||
QCOMPARE(call->destination(0), expectedDestination);
|
||||
}
|
||||
|
||||
void testCallCostItem(const CostItem *item, const Function *caller, const Function *callee,
|
||||
quint64 expectedCalls, quint64 expectedDestination, quint64 expectedPosition,
|
||||
quint64 expectedCost)
|
||||
{
|
||||
QCOMPARE(item->differingFileId(), qint64(-1));
|
||||
testCostItem(item, expectedPosition, expectedCost);
|
||||
|
||||
QVERIFY(item->call());
|
||||
testFunctionCall(item->call(), caller, callee, expectedCost, expectedCalls, expectedDestination);
|
||||
}
|
||||
|
||||
void testDifferringFileCostItem(const CostItem *item, const QString &differingFile, quint64 expectedPosition, quint64 expectedCost)
|
||||
{
|
||||
QVERIFY(!item->call());
|
||||
QCOMPARE(item->differingFile(), differingFile);
|
||||
|
||||
testCostItem(item, expectedPosition, expectedCost);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CallgrindParserTests::initTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void CallgrindParserTests::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
ParseData* parseDataFile(const QString &dataFile)
|
||||
{
|
||||
QFile file(dataFile);
|
||||
Q_ASSERT(file.exists());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
|
||||
Parser p;
|
||||
p.parse(&file);
|
||||
|
||||
return p.takeData();
|
||||
}
|
||||
|
||||
void CallgrindParserTests::testHeaderData()
|
||||
{
|
||||
QScopedPointer<const ParseData> data(parseDataFile(dataFile("simpleFunction.out")));
|
||||
|
||||
QCOMPARE(data->command(), QString("ls"));
|
||||
QCOMPARE(data->creator(), QString("callgrind-3.6.0.SVN-Debian"));
|
||||
QCOMPARE(data->pid(), quint64(2992));
|
||||
QCOMPARE(data->version(), 1);
|
||||
QCOMPARE(data->part(), 1u);
|
||||
QCOMPARE(data->descriptions(), QStringList() << "I1 cache:" << "D1 cache:" << "L2 cache:"
|
||||
<< "Timerange: Basic block 0 - 300089"
|
||||
<< "Trigger: Program termination");
|
||||
|
||||
QCOMPARE(data->positions(), QStringList() << "line");
|
||||
QCOMPARE(data->lineNumberPositionIndex(), 0);
|
||||
QCOMPARE(data->events(), QStringList() << "Ir");
|
||||
QCOMPARE(data->totalCost(0), quint64(1434186));
|
||||
}
|
||||
|
||||
void CallgrindParserTests::testSimpleFunction()
|
||||
{
|
||||
QScopedPointer<const ParseData> data(parseDataFile(dataFile("simpleFunction.out")));
|
||||
|
||||
QCOMPARE(data->functions().size(), 4);
|
||||
|
||||
{
|
||||
const Function *func = data->functions().at(0);
|
||||
QCOMPARE(func->file(), QString("/my/file.cpp"));
|
||||
QCOMPARE(func->object(), QString("/my/object"));
|
||||
QCOMPARE(func->name(), QString("myFunction"));
|
||||
|
||||
QVERIFY(func->outgoingCalls().isEmpty());
|
||||
QCOMPARE(func->called(), quint64(0));
|
||||
QVERIFY(func->incomingCalls().isEmpty());
|
||||
QCOMPARE(func->costItems().size(), 7);
|
||||
testSimpleCostItem(func->costItems().at(0), 1, 1);
|
||||
testSimpleCostItem(func->costItems().at(1), 3, 1);
|
||||
testSimpleCostItem(func->costItems().at(2), 3, 3);
|
||||
testSimpleCostItem(func->costItems().at(3), 1, 1);
|
||||
testSimpleCostItem(func->costItems().at(4), 5, 4);
|
||||
testDifferringFileCostItem(func->costItems().at(5), QString("/my/file3.h"), 1, 5);
|
||||
testSimpleCostItem(func->costItems().at(6), 7, 5);
|
||||
QCOMPARE(func->selfCost(0), quint64(20));
|
||||
QCOMPARE(func->inclusiveCost(0), quint64(20));
|
||||
}
|
||||
|
||||
{
|
||||
const Function *func = data->functions().at(1);
|
||||
QCOMPARE(func->file(), QString("/my/file.cpp"));
|
||||
QCOMPARE(func->object(), QString("/my/object"));
|
||||
QCOMPARE(func->name(), QString("myFunction2"));
|
||||
|
||||
QVERIFY(func->incomingCalls().isEmpty());
|
||||
QCOMPARE(func->called(), quint64(0));
|
||||
QVERIFY(func->outgoingCalls().isEmpty());
|
||||
QCOMPARE(func->costItems().size(), 1);
|
||||
QCOMPARE(func->selfCost(0), quint64(1));
|
||||
QCOMPARE(func->inclusiveCost(0), func->selfCost(0));
|
||||
}
|
||||
|
||||
{
|
||||
const Function *func = data->functions().at(2);
|
||||
QCOMPARE(func->file(), QString("/my/file2.cpp"));
|
||||
QCOMPARE(func->object(), QString("/my/object"));
|
||||
QCOMPARE(func->name(), QString("myFunction4"));
|
||||
|
||||
QVERIFY(func->incomingCalls().isEmpty());
|
||||
QCOMPARE(func->called(), quint64(0));
|
||||
QVERIFY(func->outgoingCalls().isEmpty());
|
||||
QCOMPARE(func->costItems().size(), 1);
|
||||
QCOMPARE(func->selfCost(0), quint64(1));
|
||||
QCOMPARE(func->inclusiveCost(0), func->selfCost(0));
|
||||
}
|
||||
|
||||
{
|
||||
const Function *func = data->functions().at(3);
|
||||
QCOMPARE(func->file(), QString("/my/file.cpp"));
|
||||
QCOMPARE(func->object(), QString("/my/object2"));
|
||||
QCOMPARE(func->name(), QString("myFunction3"));
|
||||
|
||||
QVERIFY(func->incomingCalls().isEmpty());
|
||||
QCOMPARE(func->called(), quint64(0));
|
||||
QVERIFY(func->outgoingCalls().isEmpty());
|
||||
QCOMPARE(func->costItems().size(), 1);
|
||||
QCOMPARE(func->selfCost(0), quint64(1));
|
||||
}
|
||||
}
|
||||
|
||||
void CallgrindParserTests::testCallee()
|
||||
{
|
||||
QScopedPointer<const ParseData> data(parseDataFile(dataFile("calleeFunctions.out")));
|
||||
|
||||
QCOMPARE(data->functions().size(), 3);
|
||||
|
||||
// basic function data testing
|
||||
const Function *main = data->functions().at(0);
|
||||
QCOMPARE(main->file(), QString("file1.c"));
|
||||
QCOMPARE(main->object(), QString(""));
|
||||
QCOMPARE(main->name(), QString("main"));
|
||||
|
||||
QVERIFY(main->incomingCalls().isEmpty());
|
||||
QCOMPARE(main->called(), quint64(0));
|
||||
QCOMPARE(main->outgoingCalls().size(), 2);
|
||||
QCOMPARE(main->costItems().size(), 5);
|
||||
testSimpleCostItem(main->costItems().at(0), 16, 20);
|
||||
testSimpleCostItem(main->costItems().at(3), 17, 10);
|
||||
QCOMPARE(main->selfCost(0), quint64(30));
|
||||
QCOMPARE(main->inclusiveCost(0), quint64(1230));
|
||||
|
||||
const Function *func1 = data->functions().at(1);
|
||||
QCOMPARE(func1->file(), QString("file1.c"));
|
||||
QCOMPARE(func1->object(), QString(""));
|
||||
QCOMPARE(func1->name(), QString("func1"));
|
||||
|
||||
QCOMPARE(func1->incomingCalls().size(), 1);
|
||||
QCOMPARE(func1->called(), quint64(1));
|
||||
QCOMPARE(func1->outgoingCalls().size(), 1);
|
||||
QCOMPARE(func1->costItems().size(), 2);
|
||||
testSimpleCostItem(func1->costItems().at(0), 51, 100);
|
||||
QCOMPARE(func1->selfCost(0), quint64(100));
|
||||
QCOMPARE(func1->inclusiveCost(0), quint64(400));
|
||||
|
||||
const Function *func2 = data->functions().at(2);
|
||||
QCOMPARE(func2->file(), QString("file2.c"));
|
||||
QCOMPARE(func2->object(), QString(""));
|
||||
QCOMPARE(func2->name(), QString("func2"));
|
||||
|
||||
QCOMPARE(func2->incomingCalls().size(), 2);
|
||||
QCOMPARE(func2->called(), quint64(8));
|
||||
QVERIFY(func2->outgoingCalls().isEmpty());
|
||||
QCOMPARE(func2->costItems().size(), 1);
|
||||
testSimpleCostItem(func2->costItems().at(0), 20, 700);
|
||||
QCOMPARE(func2->selfCost(0), quint64(700));
|
||||
QCOMPARE(func2->inclusiveCost(0), quint64(700));
|
||||
|
||||
// now test callees
|
||||
testCallCostItem(main->costItems().at(1), main, func1, 1, 50, 16, 400);
|
||||
testCallCostItem(main->costItems().at(2), main, func2, 3, 20, 16, 400);
|
||||
|
||||
testCallCostItem(func1->costItems().at(1), func1, func2, 2, 20, 51, 300);
|
||||
|
||||
// order is undefined
|
||||
if (func2->incomingCalls().first()->caller() == func1) {
|
||||
testFunctionCall(func2->incomingCalls().first(), func1, func2, 300, 2, 20);
|
||||
testFunctionCall(func2->incomingCalls().last(), main, func2, 800, 6, 20);
|
||||
} else {
|
||||
testFunctionCall(func2->incomingCalls().last(), func1, func2, 300, 2, 20);
|
||||
testFunctionCall(func2->incomingCalls().first(), main, func2, 800, 6, 20);
|
||||
}
|
||||
|
||||
// order is undefined
|
||||
if (main->outgoingCalls().first()->callee() == func2) {
|
||||
testFunctionCall(main->outgoingCalls().first(), main, func2, 800, 6, 20);
|
||||
testFunctionCall(main->outgoingCalls().last(), main, func1, 400, 1, 50);
|
||||
} else {
|
||||
testFunctionCall(main->outgoingCalls().last(), main, func2, 800, 6, 20);
|
||||
testFunctionCall(main->outgoingCalls().first(), main, func1, 400, 1, 50);
|
||||
}
|
||||
|
||||
testFunctionCall(func1->outgoingCalls().first(), func1, func2, 300, 2, 20);
|
||||
testFunctionCall(func1->incomingCalls().first(), main, func1, 400, 1, 50);
|
||||
}
|
||||
|
||||
void CallgrindParserTests::testInlinedCalls()
|
||||
{
|
||||
QScopedPointer<const ParseData> data(parseDataFile(dataFile("inlinedFunctions.out")));
|
||||
QCOMPARE(data->functions().size(), 3);
|
||||
|
||||
const Function *main = data->functions().first();
|
||||
QCOMPARE(main->name(), QString("main"));
|
||||
QCOMPARE(main->file(), QString("file1.c"));
|
||||
QCOMPARE(main->selfCost(0), quint64(4));
|
||||
QCOMPARE(main->inclusiveCost(0), quint64(804));
|
||||
|
||||
const Function *inlined = data->functions().at(1);
|
||||
QCOMPARE(inlined->name(), QString("Something::Inlined()"));
|
||||
QCOMPARE(inlined->file(), QString("file.h"));
|
||||
|
||||
const Function *func1 = data->functions().last();
|
||||
QCOMPARE(func1->name(), QString("func1"));
|
||||
QCOMPARE(func1->file(), QString("file3.h"));
|
||||
|
||||
QCOMPARE(main->outgoingCalls().size(), 2);
|
||||
QCOMPARE(main->costItems().at(2)->call()->callee(), inlined);
|
||||
QCOMPARE(main->costItems().at(3)->call()->callee(), func1);
|
||||
|
||||
testSimpleCostItem(main->costItems().last(), 1, 2);
|
||||
}
|
||||
|
||||
void CallgrindParserTests::testMultiCost()
|
||||
{
|
||||
QScopedPointer<const ParseData> data(parseDataFile(dataFile("multiCost.out")));
|
||||
QCOMPARE(data->functions().size(), 2);
|
||||
|
||||
QCOMPARE(data->positions(), QStringList() << "line");
|
||||
QCOMPARE(data->events(), QStringList() << "Ir" << "Time");
|
||||
|
||||
QCOMPARE(data->totalCost(0), quint64(4));
|
||||
QCOMPARE(data->totalCost(1), quint64(400));
|
||||
|
||||
const Function *main = data->functions().at(0);
|
||||
QCOMPARE(main->costItems().first()->costs(), QVector<quint64>() << 1 << 100);
|
||||
QCOMPARE(main->costItems().first()->positions(), QVector<quint64>() << 1);
|
||||
|
||||
QVERIFY(main->costItems().last()->call());
|
||||
QCOMPARE(main->costItems().last()->call()->destinations(), QVector<quint64>() << 1);
|
||||
}
|
||||
|
||||
void CallgrindParserTests::testMultiPos()
|
||||
{
|
||||
QScopedPointer<const ParseData> data(parseDataFile(dataFile("multiPos.out")));
|
||||
QCOMPARE(data->functions().size(), 2);
|
||||
|
||||
QCOMPARE(data->positions(), QStringList() << "line" << "memAddr");
|
||||
QCOMPARE(data->events(), QStringList() << "Ir");
|
||||
|
||||
QCOMPARE(data->totalCost(0), quint64(4));
|
||||
|
||||
const Function *main = data->functions().at(0);
|
||||
QCOMPARE(main->costItems().first()->costs(), QVector<quint64>() << 1);
|
||||
QCOMPARE(main->costItems().first()->positions(), QVector<quint64>() << 1 << 0x01);
|
||||
|
||||
QVERIFY(main->costItems().last()->call());
|
||||
QCOMPARE(main->costItems().last()->call()->destinations(), QVector<quint64>() << 1 << 0x04);
|
||||
}
|
||||
|
||||
void CallgrindParserTests::testMultiPosAndCost()
|
||||
{
|
||||
QScopedPointer<const ParseData> data(parseDataFile(dataFile("multiCostAndPos.out")));
|
||||
QCOMPARE(data->functions().size(), 2);
|
||||
|
||||
QCOMPARE(data->positions(), QStringList() << "line" << "memAddr");
|
||||
QCOMPARE(data->events(), QStringList() << "Ir" << "Time");
|
||||
|
||||
QCOMPARE(data->totalCost(0), quint64(4));
|
||||
QCOMPARE(data->totalCost(1), quint64(400));
|
||||
|
||||
const Function *main = data->functions().at(0);
|
||||
QCOMPARE(main->costItems().first()->costs(), QVector<quint64>() << 1 << 100);
|
||||
QCOMPARE(main->costItems().first()->positions(), QVector<quint64>() << 1 << 0x01);
|
||||
|
||||
QVERIFY(main->costItems().last()->call());
|
||||
QCOMPARE(main->costItems().last()->call()->destinations(), QVector<quint64>() << 1 << 0x04);
|
||||
}
|
||||
|
||||
const Function *findFunction(const QString &needle, const QVector<const Function *> &haystack)
|
||||
{
|
||||
foreach(const Function *function, haystack) {
|
||||
if (function->name() == needle) {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CallgrindParserTests::testCycle()
|
||||
{
|
||||
QScopedPointer<const ParseData> data(parseDataFile(dataFile("cycle.out")));
|
||||
QCOMPARE(data->functions().size(), 4);
|
||||
|
||||
const Function *main = data->functions().at(0);
|
||||
QCOMPARE(main->name(), QLatin1String("main"));
|
||||
QCOMPARE(main->inclusiveCost(0), quint64(80));
|
||||
QCOMPARE(main->selfCost(0), quint64(30));
|
||||
const Function *a = data->functions().at(1);
|
||||
QCOMPARE(a->name(), QLatin1String("A()"));
|
||||
QCOMPARE(a->inclusiveCost(0), quint64(50));
|
||||
QCOMPARE(a->selfCost(0), quint64(10));
|
||||
const Function *c = data->functions().at(2);
|
||||
QCOMPARE(c->name(), QLatin1String("C(bool)"));
|
||||
QCOMPARE(c->inclusiveCost(0), quint64(30));
|
||||
QCOMPARE(c->selfCost(0), quint64(20));
|
||||
const Function *b = data->functions().at(3);
|
||||
QCOMPARE(b->name(), QLatin1String("B(bool)"));
|
||||
QCOMPARE(b->inclusiveCost(0), quint64(30));
|
||||
QCOMPARE(b->selfCost(0), quint64(20));
|
||||
|
||||
QCOMPARE(data->functions(true).size(), 3);
|
||||
QCOMPARE(findFunction(QString("main"), data->functions(true)), main);
|
||||
QCOMPARE(findFunction(QString("A()"), data->functions(true)), a);
|
||||
const FunctionCycle *cycle = dynamic_cast<const FunctionCycle*>(findFunction(QString("cycle 1"), data->functions(true)));
|
||||
QVERIFY(cycle);
|
||||
QCOMPARE(cycle->called(), quint64(2));
|
||||
QCOMPARE(cycle->inclusiveCost(0), quint64(40));
|
||||
QCOMPARE(cycle->selfCost(0), quint64(40));
|
||||
}
|
||||
|
||||
void CallgrindParserTests::testRecursiveCycle()
|
||||
{
|
||||
QScopedPointer<const ParseData> data(parseDataFile(dataFile("recursiveCycle.out")));
|
||||
QCOMPARE(data->functions().size(), 5);
|
||||
|
||||
const Function *main = findFunction(QString("main"), data->functions());
|
||||
QVERIFY(main);
|
||||
QCOMPARE(main->inclusiveCost(0), quint64(70701765 + 3 + 4));
|
||||
QCOMPARE(data->totalCost(0), main->inclusiveCost(0));
|
||||
QCOMPARE(main->selfCost(0), quint64(3 + 4));
|
||||
const Function *a1 = findFunction(QString("A(int)"), data->functions());
|
||||
QVERIFY(a1);
|
||||
QCOMPARE(a1->inclusiveCost(0), quint64(700017 + 70001746 + 2));
|
||||
QCOMPARE(a1->selfCost(0), quint64(700017 + 2));
|
||||
const Function *a2 = findFunction(QString("A(int)'2"), data->functions());
|
||||
QVERIFY(a2);
|
||||
QCOMPARE(a2->inclusiveCost(0), quint64(35000846 + 1715042679 + 100));
|
||||
QCOMPARE(a2->selfCost(0), quint64(35000846 + 100));
|
||||
const Function *b1 = findFunction(QString("B(int)"), data->functions());
|
||||
QVERIFY(b1);
|
||||
QCOMPARE(b1->inclusiveCost(0), quint64(700014 + 69301730 + 2));
|
||||
QCOMPARE(b1->selfCost(0), quint64(700014 + 2));
|
||||
const Function *b2 = findFunction(QString("B(int)'2"), data->functions());
|
||||
QVERIFY(b2);
|
||||
QCOMPARE(b2->inclusiveCost(0), quint64(34300686 + 1680741895 + 98));
|
||||
QCOMPARE(b2->selfCost(0), quint64(34300686 + 98));
|
||||
|
||||
{ // cycle detection
|
||||
QCOMPARE(data->functions(true).size(), 4);
|
||||
QCOMPARE(findFunction(QString("main"), data->functions(true)), main);
|
||||
QCOMPARE(findFunction(QString("A(int)"), data->functions(true)), a1);
|
||||
QCOMPARE(findFunction(QString("B(int)"), data->functions(true)), b1);
|
||||
const FunctionCycle *cycle = dynamic_cast<const FunctionCycle*>(findFunction(QString("cycle 1"), data->functions(true)));
|
||||
QVERIFY(cycle);
|
||||
QCOMPARE(cycle->called(), quint64(1));
|
||||
const quint64 restCost = data->totalCost(0) - main->selfCost(0) - a1->selfCost(0) - b1->selfCost(0);
|
||||
QCOMPARE(cycle->inclusiveCost(0), restCost);
|
||||
QCOMPARE(cycle->selfCost(0), restCost);
|
||||
}
|
||||
}
|
||||
|
||||
void CallgrindParserTests::testRecursion()
|
||||
{
|
||||
QScopedPointer<const ParseData> data(parseDataFile(dataFile("recursion.out")));
|
||||
QCOMPARE(data->functions().size(), 3);
|
||||
QCOMPARE(data->totalCost(0), quint64(35700972));
|
||||
|
||||
const Function *main = findFunction(QString("main"), data->functions());
|
||||
QVERIFY(main);
|
||||
QCOMPARE(main->inclusiveCost(0), quint64(4 + 35700965 + 3));
|
||||
QCOMPARE(main->selfCost(0), quint64(4 + 3));
|
||||
|
||||
const Function *a1 = findFunction(QString("A(int)"), data->functions());
|
||||
QVERIFY(a1);
|
||||
QCOMPARE(a1->inclusiveCost(0), quint64(700010 + 35000946 + 2));
|
||||
QCOMPARE(a1->selfCost(0), quint64(700010 + 2));
|
||||
|
||||
const Function *a2 = findFunction(QString("A(int)'2"), data->functions());
|
||||
QVERIFY(a2);
|
||||
// inclusive cost must be the same as call-cost from a1
|
||||
QCOMPARE(a2->inclusiveCost(0), quint64(35000946));
|
||||
QCOMPARE(a2->selfCost(0), quint64(35000846));
|
||||
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(CallgrindParserTests)
|
66
tests/valgrind/callgrind/callgrindparsertests.h
Normal file
66
tests/valgrind/callgrind/callgrindparsertests.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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 CALLGRINDPARSERTESTS_H
|
||||
#define CALLGRINDPARSERTESTS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
#include <QDebug>
|
||||
|
||||
class CallgrindParserTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanup();
|
||||
|
||||
void testHeaderData();
|
||||
void testSimpleFunction();
|
||||
void testCallee();
|
||||
void testInlinedCalls();
|
||||
|
||||
void testMultiCost();
|
||||
void testMultiPos();
|
||||
void testMultiPosAndCost();
|
||||
|
||||
void testCycle();
|
||||
void testRecursiveCycle();
|
||||
|
||||
void testRecursion();
|
||||
};
|
||||
|
||||
#endif // CALLGRINDPARSERTESTS_H
|
19
tests/valgrind/callgrind/callgrindparsertests.pro
Normal file
19
tests/valgrind/callgrind/callgrindparsertests.pro
Normal file
@@ -0,0 +1,19 @@
|
||||
TEMPLATE = app
|
||||
TARGET = callgrindparsertests
|
||||
|
||||
macx:CONFIG -= app_bundle
|
||||
|
||||
QT += testlib network
|
||||
|
||||
DEFINES += "PARSERTESTS_DATA_DIR=\"\\\"$$PWD/data\\\"\""
|
||||
#enable extra debugging code
|
||||
DEFINES += "CALLGRINDPARSERTESTS"
|
||||
|
||||
!win32 {
|
||||
include(../../../qtcreator.pri)
|
||||
include(../../../src/libs/valgrind/valgrind.pri)
|
||||
}
|
||||
|
||||
SOURCES += callgrindparsertests.cpp
|
||||
|
||||
HEADERS += callgrindparsertests.h
|
44
tests/valgrind/callgrind/data/calleeFunctions.out
Normal file
44
tests/valgrind/callgrind/data/calleeFunctions.out
Normal file
@@ -0,0 +1,44 @@
|
||||
version: 1
|
||||
creator: callgrind-3.6.0.SVN-Debian
|
||||
pid: 2992
|
||||
cmd: ls
|
||||
part: 1
|
||||
|
||||
|
||||
desc: I1 cache:
|
||||
desc: D1 cache:
|
||||
desc: L2 cache:
|
||||
|
||||
desc: Timerange: Basic block 0 - 300089
|
||||
desc: Trigger: Program termination
|
||||
|
||||
positions: line
|
||||
events: Ir
|
||||
summary: 820
|
||||
|
||||
fl=(1) file1.c
|
||||
fn=(1) main
|
||||
16 20
|
||||
cfn=(2) func1
|
||||
calls=1 50
|
||||
16 400
|
||||
cfi=(2) file2.c
|
||||
cfn=(3) func2
|
||||
calls=3 20
|
||||
16 400
|
||||
17 10
|
||||
cfi=(2)
|
||||
cfn=(3)
|
||||
calls=3 20
|
||||
16 400
|
||||
|
||||
fn=(2)
|
||||
51 100
|
||||
cfl=(2)
|
||||
cfn=(3)
|
||||
calls=2 20
|
||||
51 300
|
||||
|
||||
fl=(2)
|
||||
fn=(3)
|
||||
20 700
|
11530
tests/valgrind/callgrind/data/callgrind.out.sample
Normal file
11530
tests/valgrind/callgrind/data/callgrind.out.sample
Normal file
File diff suppressed because it is too large
Load Diff
355
tests/valgrind/callgrind/data/callgrind.out.sample.annotated
Normal file
355
tests/valgrind/callgrind/data/callgrind.out.sample.annotated
Normal file
@@ -0,0 +1,355 @@
|
||||
## THIS IS AN ANNOTATED VERSION AND NOT SUPPOSED TO BE PARSED
|
||||
## for more information on the file format see:
|
||||
## http://kcachegrind.sourceforge.net/html/CallgrindFormat.html
|
||||
## and
|
||||
## http://valgrind.org/docs/manual/cg-manual.html#cg-manual.impl-details.file-format
|
||||
|
||||
#################
|
||||
# information about the app that was run
|
||||
##
|
||||
|
||||
version: 1
|
||||
creator: callgrind-3.6.0.SVN-Debian
|
||||
pid: 2992
|
||||
cmd: ls
|
||||
part: 1
|
||||
|
||||
#################
|
||||
# desc: type: value [Cachegrind]
|
||||
# This specifies various information for this dump.
|
||||
# For some types, the semantic is defined, but any description type is allowed.
|
||||
# Unknown types are ignored.
|
||||
#
|
||||
# From cachegrind man:
|
||||
#
|
||||
# The contents of the "desc:" lines are printed out at the top of the summary.
|
||||
# This is a generic way of providing simulation specific information,
|
||||
# e.g. for giving the cache configuration for cache simulation.
|
||||
#
|
||||
# Basically seems to be some information on what caches where found, how long the app ran and
|
||||
# why the callgrind file was created (app closed, died, callgrind_control --dump, ...)
|
||||
##
|
||||
|
||||
desc: I1 cache:
|
||||
desc: D1 cache:
|
||||
desc: L2 cache:
|
||||
|
||||
desc: Timerange: Basic block 0 - 300089
|
||||
desc: Trigger: Program termination
|
||||
|
||||
#################
|
||||
# the positions that get reported, whitespace separated list
|
||||
##
|
||||
positions: line
|
||||
|
||||
#################
|
||||
# events that where counted, whitespace separated list
|
||||
#
|
||||
# Ir == Instructions
|
||||
##
|
||||
events: Ir
|
||||
|
||||
#################
|
||||
# list of total count of events, in this case total cost of Ir events
|
||||
# Can be used for a progress bar
|
||||
##
|
||||
summary: 1434186
|
||||
|
||||
#################
|
||||
# ob: the object the function lives in
|
||||
# fl: the file where the function lives in
|
||||
# fn: the function
|
||||
#
|
||||
# the (NUM) stuff before that is used for string compression, each string gets only printed once,
|
||||
# afterwards only (3) or similar will be written
|
||||
#
|
||||
# the numbers below are the data columns, starting with the position(s) and then the event(s)
|
||||
# in our case it's just a pair of pos, event
|
||||
#
|
||||
# "If a cost line specifies less event counts than given in the "events" line,
|
||||
# the rest is assumed to be zero."
|
||||
#
|
||||
# "Note that regular cost lines always give self (also called exclusive)
|
||||
# cost of code at a given position."
|
||||
#
|
||||
# generally all this should be shown accumulated for the function.
|
||||
# if the file exists we might want to show a line-by-line analysis though.
|
||||
# furthermore note that positions size gets compressed by just wirting the absolute number once
|
||||
# than using relative values.
|
||||
##
|
||||
ob=(3) /lib/i686/cmov/libpthread-2.11.2.so
|
||||
fl=(131) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/nptl/../nptl/sysdeps/unix/sysv/linux/i386/i686/../i486/pthread_rwlock_unlock.S
|
||||
fn=(782) pthread_rwlock_unlock
|
||||
33 3
|
||||
+2 3
|
||||
+5 3
|
||||
+3 3
|
||||
+1 3
|
||||
+1 3
|
||||
+6 3
|
||||
+2 3
|
||||
+1 3
|
||||
+4 3
|
||||
+2 3
|
||||
+1 3
|
||||
+1 3
|
||||
+1 3
|
||||
+3 3
|
||||
+1 3
|
||||
+39 3
|
||||
+6 3
|
||||
+2 3
|
||||
+1 3
|
||||
+1 3
|
||||
+1 3
|
||||
|
||||
#################
|
||||
# here we don't have an object and the fn has no symbol, same as above otherwise
|
||||
##
|
||||
fl=(44) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/nptl/../nptl/sysdeps/unix/sysv/linux/i386/i686/../i486/sem_post.S
|
||||
fn=(196) 0x00004340
|
||||
170 11
|
||||
+1 11
|
||||
|
||||
#################
|
||||
# here we don't have an object, same as above otherwise
|
||||
#
|
||||
# also noteworthy is the c{ob,fi,fn} stuff, it shows the object, file, and function of a call
|
||||
# if they are not given for a calls=... line, they are assumed to be the same as the parent function.
|
||||
# The calls format is this:
|
||||
#
|
||||
# calls=(Call Count) (Destination position)
|
||||
# (Source position) (Inclusive cost of call)
|
||||
#
|
||||
# Another interesting point: The * for the position means: +-0, i.e. same position as before
|
||||
##
|
||||
fl=(43) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/nptl/nptl-init.c
|
||||
fn=(194) __pthread_initialize_minimal
|
||||
273 4
|
||||
+14 1
|
||||
+2 1
|
||||
-16 1
|
||||
+16 1
|
||||
-16 1
|
||||
+16 1
|
||||
-2 1
|
||||
-14 1
|
||||
cfi=(44)
|
||||
cfn=(196)
|
||||
calls=1 170
|
||||
* 2
|
||||
* 1
|
||||
+16 3
|
||||
cob=(1) /lib/ld-2.11.2.so
|
||||
cfi=(1) ???
|
||||
cfn=(198) _dl_sysinfo_int80
|
||||
calls=1 0
|
||||
* 2
|
||||
* 4
|
||||
+1 3
|
||||
+1 1
|
||||
+4 5
|
||||
+7 1
|
||||
+6 1
|
||||
-6 2
|
||||
+2 1
|
||||
+4 3
|
||||
cob=(1)
|
||||
cfi=(1)
|
||||
cfn=(198)
|
||||
calls=1 0
|
||||
* 2
|
||||
* 1
|
||||
+11 2
|
||||
-2 1
|
||||
+2 4
|
||||
cob=(1)
|
||||
cfi=(1)
|
||||
cfn=(198)
|
||||
calls=1 0
|
||||
* 2
|
||||
* 1
|
||||
+1 1
|
||||
-2 1
|
||||
+2 1
|
||||
+1 1
|
||||
+7 3
|
||||
+12 2
|
||||
-8 1
|
||||
+8 7
|
||||
cob=(1)
|
||||
cfi=(1)
|
||||
cfn=(198)
|
||||
calls=1 0
|
||||
* 2
|
||||
* 1
|
||||
+1 1
|
||||
-3 1
|
||||
+3 1
|
||||
+1 2
|
||||
+1 2
|
||||
+7 3
|
||||
+4 1
|
||||
fi=(156) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/nptl/../nptl/sysdeps/pthread/list.h
|
||||
49 2
|
||||
fe=(43)
|
||||
354 1
|
||||
fi=(156)
|
||||
49 1
|
||||
+1 1
|
||||
+2 1
|
||||
-1 1
|
||||
fe=(43)
|
||||
358 2
|
||||
+8 2
|
||||
-2 2
|
||||
+2 37
|
||||
+16 1
|
||||
-14 2
|
||||
-3 1
|
||||
+3 3
|
||||
cfi=(45) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/nptl/../sysdeps/unix/sysv/linux/i386/sigaction.c
|
||||
cfn=(200) __libc_sigaction
|
||||
calls=1 59
|
||||
* 93
|
||||
+3 1
|
||||
+3 1
|
||||
+8 1
|
||||
-11 1
|
||||
+1 1
|
||||
+2 3
|
||||
cfi=(45)
|
||||
cfn=(200)
|
||||
calls=1 59
|
||||
* 93
|
||||
+8 1
|
||||
fi=(157) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/nptl/../sysdeps/unix/sysv/linux/bits/sigset.h
|
||||
119 2
|
||||
fe=(43)
|
||||
382 4
|
||||
cob=(1)
|
||||
cfi=(1)
|
||||
cfn=(198)
|
||||
calls=1 0
|
||||
* 2
|
||||
* 1
|
||||
+5 3
|
||||
cob=(1)
|
||||
cfi=(36) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/elf/dl-tls.c
|
||||
cfn=(214) _dl_get_tls_static_info
|
||||
calls=1 -64
|
||||
* 14
|
||||
cob=(1)
|
||||
cfi=(46) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/elf/../sysdeps/i386/dl-trampoline.S
|
||||
cfn=(208) _dl_runtime_resolve
|
||||
calls=1 29
|
||||
* 1958
|
||||
+3 4
|
||||
+2 1
|
||||
+2 2
|
||||
+6 1
|
||||
-6 4
|
||||
+6 3
|
||||
cob=(4) /lib/i686/cmov/libc-2.11.2.so
|
||||
cfi=(48) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/resource/../sysdeps/unix/sysv/linux/i386/getrlimit.c
|
||||
cfn=(220) getrlimit@@GLIBC_2.2
|
||||
calls=1 41
|
||||
* 20
|
||||
cob=(1)
|
||||
cfi=(46)
|
||||
cfn=(208)
|
||||
calls=1 29
|
||||
* 1634
|
||||
-1 2
|
||||
+1 1
|
||||
-1 2
|
||||
+5 2
|
||||
+7 2
|
||||
cob=(4)
|
||||
cfi=(50) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/posix/../sysdeps/unix/sysv/linux/x86_64/sysconf.c
|
||||
cfn=(228) sysconf
|
||||
calls=1 33
|
||||
* 50
|
||||
cob=(1)
|
||||
cfi=(46)
|
||||
cfn=(208)
|
||||
calls=1 29
|
||||
* 1458
|
||||
+1 1
|
||||
+1 1
|
||||
-1 1
|
||||
+1 2
|
||||
+4 4
|
||||
+1 1
|
||||
+4 1
|
||||
cob=(4)
|
||||
cfi=(55) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/elf/dl-tsd.c
|
||||
cfn=(242) __libc_dl_error_tsd
|
||||
calls=1 51
|
||||
* 10
|
||||
cob=(1)
|
||||
cfi=(46)
|
||||
cfn=(208)
|
||||
calls=1 29
|
||||
* 1630
|
||||
* 3
|
||||
cob=(1)
|
||||
cfi=(2) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/elf/rtld.c
|
||||
cfn=(84) _dl_initial_error_catch_tsd
|
||||
calls=1 796
|
||||
* 9
|
||||
* 2
|
||||
+1 1
|
||||
+6 1
|
||||
+1 1
|
||||
-7 1
|
||||
+4 2
|
||||
+4 1
|
||||
-3 2
|
||||
+3 1
|
||||
+3 2
|
||||
+11 1
|
||||
fi=(58) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/nptl/../nptl/sysdeps/unix/sysv/linux/i386/smp.h
|
||||
40 1
|
||||
fe=(43)
|
||||
434 1
|
||||
+3 2
|
||||
+2 2
|
||||
+6 3
|
||||
cob=(4)
|
||||
cfi=(56) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/nptl/../nptl/sysdeps/unix/sysv/linux/libc_pthread_init.c
|
||||
cfn=(248) __libc_pthread_init
|
||||
calls=1 43
|
||||
* 458
|
||||
cob=(1)
|
||||
cfi=(46)
|
||||
cfn=(208)
|
||||
calls=1 29
|
||||
* 1630
|
||||
fi=(58)
|
||||
40 2
|
||||
cob=(4)
|
||||
cfi=(59) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/posix/../sysdeps/unix/syscall-template.S
|
||||
cfn=(258) uname
|
||||
calls=1 +42
|
||||
* 10
|
||||
cob=(1)
|
||||
cfi=(46)
|
||||
cfn=(208)
|
||||
calls=1 -11
|
||||
* 1377
|
||||
* 3
|
||||
+15 4
|
||||
cob=(4)
|
||||
cfi=(62) /build/buildd-eglibc_2.11.2-6-i386-6jE6oF/eglibc-2.11.2/string/../string/strstr.c
|
||||
cfn=(268) __GI_strstr
|
||||
calls=1 -2
|
||||
* 366
|
||||
cob=(1)
|
||||
cfi=(46)
|
||||
cfn=(208)
|
||||
calls=1 -26
|
||||
* 1474
|
||||
fe=(43)
|
||||
449 4
|
||||
+1 6
|
9482
tests/valgrind/callgrind/data/callgrind.out.sample.expected
Normal file
9482
tests/valgrind/callgrind/data/callgrind.out.sample.expected
Normal file
File diff suppressed because it is too large
Load Diff
45
tests/valgrind/callgrind/data/cycle.out
Normal file
45
tests/valgrind/callgrind/data/cycle.out
Normal file
@@ -0,0 +1,45 @@
|
||||
version: 1
|
||||
creator: callgrind-3.6.0.SVN-Debian
|
||||
pid: 3303
|
||||
cmd: ./a.out
|
||||
part: 1
|
||||
|
||||
|
||||
desc: I1 cache:
|
||||
desc: D1 cache:
|
||||
desc: L2 cache:
|
||||
|
||||
desc: Timerange: Basic block 0 - 718438
|
||||
desc: Trigger: Program termination
|
||||
|
||||
positions: line
|
||||
events: Ir
|
||||
summary: 80
|
||||
|
||||
fn=(470) main
|
||||
0 15
|
||||
cfn=(472) A()
|
||||
calls=1 0
|
||||
0 50
|
||||
0 15
|
||||
|
||||
fn=(472)
|
||||
0 10
|
||||
cfn=(474) B(bool)
|
||||
calls=1 0
|
||||
0 20
|
||||
cfn=(476) C(bool)
|
||||
calls=1 0
|
||||
0 20
|
||||
|
||||
fn=(476)
|
||||
0 20
|
||||
cfn=(474)
|
||||
calls=1 0
|
||||
0 10
|
||||
|
||||
fn=(474)
|
||||
0 20
|
||||
cfn=(476)
|
||||
calls=1 0
|
||||
0 10
|
40
tests/valgrind/callgrind/data/inlinedFunctions.out
Normal file
40
tests/valgrind/callgrind/data/inlinedFunctions.out
Normal file
@@ -0,0 +1,40 @@
|
||||
version: 1
|
||||
creator: callgrind-3.6.0.SVN-Debian
|
||||
pid: 2992
|
||||
cmd: ls
|
||||
part: 1
|
||||
|
||||
|
||||
desc: I1 cache:
|
||||
desc: D1 cache:
|
||||
desc: L2 cache:
|
||||
|
||||
desc: Timerange: Basic block 0 - 300089
|
||||
desc: Trigger: Program termination
|
||||
|
||||
positions: line
|
||||
events: Ir
|
||||
summary: 820
|
||||
|
||||
fl=(1) file1.c
|
||||
fn=(1) main
|
||||
1 1
|
||||
fi=(2) file.h
|
||||
2 1
|
||||
cfn=(2) Something::Inlined()
|
||||
calls=1 50
|
||||
1 400
|
||||
cfl=(3) file3.h
|
||||
cfn=(3) func1
|
||||
calls=1 50
|
||||
1 400
|
||||
fe=(1)
|
||||
1 2
|
||||
|
||||
fl=(2)
|
||||
fn=(2)
|
||||
20 400
|
||||
|
||||
fl=(3)
|
||||
fn=(3)
|
||||
20 400
|
32
tests/valgrind/callgrind/data/multiCost.out
Normal file
32
tests/valgrind/callgrind/data/multiCost.out
Normal file
@@ -0,0 +1,32 @@
|
||||
version: 1
|
||||
creator: callgrind-3.6.0.SVN-Debian
|
||||
pid: 2992
|
||||
cmd: ls
|
||||
part: 1
|
||||
|
||||
|
||||
desc: I1 cache:
|
||||
desc: D1 cache:
|
||||
desc: L2 cache:
|
||||
|
||||
desc: Timerange: Basic block 0 - 300089
|
||||
desc: Trigger: Program termination
|
||||
|
||||
positions: line
|
||||
events: Ir Time
|
||||
summary: 4 400
|
||||
|
||||
ob=(1) /my/object
|
||||
fl=(1) /my/file.cpp
|
||||
fn=(1) main
|
||||
1 1 100
|
||||
fi=(3) /my/file.h
|
||||
2 1 100
|
||||
fe=(1)
|
||||
3 1 100
|
||||
cfn=(2) func1
|
||||
calls=1 1
|
||||
4 1 100
|
||||
|
||||
fn=(2)
|
||||
1 1 100
|
32
tests/valgrind/callgrind/data/multiCostAndPos.out
Normal file
32
tests/valgrind/callgrind/data/multiCostAndPos.out
Normal file
@@ -0,0 +1,32 @@
|
||||
version: 1
|
||||
creator: callgrind-3.6.0.SVN-Debian
|
||||
pid: 2992
|
||||
cmd: ls
|
||||
part: 1
|
||||
|
||||
|
||||
desc: I1 cache:
|
||||
desc: D1 cache:
|
||||
desc: L2 cache:
|
||||
|
||||
desc: Timerange: Basic block 0 - 300089
|
||||
desc: Trigger: Program termination
|
||||
|
||||
positions: line memAddr
|
||||
events: Ir Time
|
||||
summary: 4 400
|
||||
|
||||
ob=(1) /my/object
|
||||
fl=(1) /my/file.cpp
|
||||
fn=(1) main
|
||||
1 0x01 1 100
|
||||
fi=(3) /my/file.h
|
||||
2 0x02 1 100
|
||||
fe=(1)
|
||||
3 0x03 1 100
|
||||
cfn=(2) func1
|
||||
calls=1 1 0x04
|
||||
4 0x05 1 100
|
||||
|
||||
fn=(2)
|
||||
1 0x04 1 100
|
33
tests/valgrind/callgrind/data/multiPos.out
Normal file
33
tests/valgrind/callgrind/data/multiPos.out
Normal file
@@ -0,0 +1,33 @@
|
||||
version: 1
|
||||
creator: callgrind-3.6.0.SVN-Debian
|
||||
pid: 2992
|
||||
cmd: ls
|
||||
part: 1
|
||||
|
||||
|
||||
desc: I1 cache:
|
||||
desc: D1 cache:
|
||||
desc: L2 cache:
|
||||
|
||||
desc: Timerange: Basic block 0 - 300089
|
||||
desc: Trigger: Program termination
|
||||
|
||||
positions: line memAddr
|
||||
events: Ir
|
||||
summary: 4
|
||||
|
||||
|
||||
ob=(1) /my/object
|
||||
fl=(1) /my/file.cpp
|
||||
fn=(1) main
|
||||
1 0x01 1
|
||||
fi=(3) /my/file.h
|
||||
2 0x02 1
|
||||
fe=(1)
|
||||
3 0x3 1
|
||||
cfn=(2) func1
|
||||
calls=1 1 0x04
|
||||
4 0x05 1
|
||||
|
||||
fn=(2)
|
||||
1 0x04 1
|
38
tests/valgrind/callgrind/data/recursion.out
Normal file
38
tests/valgrind/callgrind/data/recursion.out
Normal file
@@ -0,0 +1,38 @@
|
||||
version: 1
|
||||
creator: callgrind-3.6.0.SVN-Debian
|
||||
pid: 19824
|
||||
cmd: ./a.out
|
||||
part: 1
|
||||
|
||||
|
||||
desc: I1 cache:
|
||||
desc: D1 cache:
|
||||
desc: L2 cache:
|
||||
|
||||
desc: Timerange: Basic block 0 - 10318874
|
||||
desc: Trigger: Program termination
|
||||
|
||||
positions: line
|
||||
events: Ir
|
||||
summary: 35700972
|
||||
|
||||
|
||||
fn=(470) main
|
||||
0 4
|
||||
cfn=(472) A(int)
|
||||
calls=1 0
|
||||
0 35700965
|
||||
0 3
|
||||
|
||||
fn=(472)
|
||||
0 700010
|
||||
cfn=(473) A(int)'2
|
||||
calls=1 0
|
||||
0 35000946
|
||||
0 2
|
||||
|
||||
fn=(473)
|
||||
0 35000846
|
||||
cfn=(473)
|
||||
calls=49 0
|
||||
0 857523079
|
52
tests/valgrind/callgrind/data/recursiveCycle.out
Normal file
52
tests/valgrind/callgrind/data/recursiveCycle.out
Normal file
@@ -0,0 +1,52 @@
|
||||
version: 1
|
||||
creator: callgrind-3.6.0.SVN-Debian
|
||||
pid: 19824
|
||||
cmd: ./a.out
|
||||
part: 1
|
||||
|
||||
|
||||
desc: I1 cache:
|
||||
desc: D1 cache:
|
||||
desc: L2 cache:
|
||||
|
||||
desc: Timerange: Basic block 0 - 10318874
|
||||
desc: Trigger: Program termination
|
||||
|
||||
positions: line
|
||||
events: Ir
|
||||
summary: 70701772
|
||||
|
||||
fn=(470) main
|
||||
0 4
|
||||
cfn=(472) A(int)
|
||||
calls=1 0
|
||||
0 70701765
|
||||
0 3
|
||||
|
||||
fn=(472)
|
||||
0 700017
|
||||
cfn=(474) B(int)
|
||||
calls=1 0
|
||||
0 70001746
|
||||
0 2
|
||||
|
||||
fn=(473) A(int)'2
|
||||
0 35000846
|
||||
cfn=(475) B(int)'2
|
||||
calls=49 0
|
||||
0 1715042679
|
||||
0 100
|
||||
|
||||
fn=(474)
|
||||
0 700014
|
||||
cfn=(473)
|
||||
calls=1 0
|
||||
0 69301730
|
||||
0 2
|
||||
|
||||
fn=(475)
|
||||
0 34300686
|
||||
cfn=(473)
|
||||
calls=49 0
|
||||
0 1680741895
|
||||
0 98
|
43
tests/valgrind/callgrind/data/simpleFunction.out
Normal file
43
tests/valgrind/callgrind/data/simpleFunction.out
Normal file
@@ -0,0 +1,43 @@
|
||||
version: 1
|
||||
creator: callgrind-3.6.0.SVN-Debian
|
||||
pid: 2992
|
||||
cmd: ls
|
||||
part: 1
|
||||
|
||||
|
||||
desc: I1 cache:
|
||||
desc: D1 cache:
|
||||
desc: L2 cache:
|
||||
|
||||
desc: Timerange: Basic block 0 - 300089
|
||||
desc: Trigger: Program termination
|
||||
|
||||
positions: line
|
||||
events: Ir
|
||||
summary: 1434186
|
||||
|
||||
|
||||
ob=(1) /my/object
|
||||
fl=(1) /my/file.cpp
|
||||
fn=(1) myFunction
|
||||
1 1
|
||||
+2 1
|
||||
* 3
|
||||
-2 1
|
||||
5 4
|
||||
fi=(3) /my/file3.h
|
||||
1 5
|
||||
fe=(1)
|
||||
7 5
|
||||
|
||||
fn=(2) myFunction2
|
||||
1 1
|
||||
|
||||
fl=(2) /my/file2.cpp
|
||||
fn=(4) myFunction4
|
||||
1 1
|
||||
|
||||
ob=(2) /my/object2
|
||||
fl=(1)
|
||||
fn=(3) myFunction3
|
||||
1 1
|
242
tests/valgrind/callgrind/modeltest.cpp
Normal file
242
tests/valgrind/callgrind/modeltest.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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 "modeltest.h"
|
||||
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <QtGui/QApplication>
|
||||
#include <QtGui/QTableView>
|
||||
#include <QtGui/QDesktopWidget>
|
||||
#include <QtGui/QVBoxLayout>
|
||||
#include <QtGui/QMenu>
|
||||
#include <QtGui/QSortFilterProxyModel>
|
||||
#include <QtGui/QStandardItemModel>
|
||||
#include <QtGui/QPushButton>
|
||||
#include <QtGui/QSpinBox>
|
||||
#include <QtGui/QComboBox>
|
||||
#include <QtGui/QMainWindow>
|
||||
#include <QtGui/QDockWidget>
|
||||
#include <QtGui/QToolButton>
|
||||
|
||||
#include <callgrind/callgrindcostview.h>
|
||||
#include <callgrind/callgrindcostdelegate.h>
|
||||
#include <callgrind/callgrindvisualisation.h>
|
||||
#include <callgrind/callgrindwidgethandler.h>
|
||||
|
||||
#include <valgrind/callgrind/callgrindparsedata.h>
|
||||
#include <valgrind/callgrind/callgrindparser.h>
|
||||
#include <valgrind/callgrind/callgrinddatamodel.h>
|
||||
#include <valgrind/callgrind/callgrindfunction.h>
|
||||
#include <valgrind/callgrind/callgrindcallmodel.h>
|
||||
#include <valgrind/callgrind/callgrindcostitem.h>
|
||||
#include <valgrind/callgrind/callgrindfunctioncall.h>
|
||||
#include <valgrind/callgrind/callgrindproxymodel.h>
|
||||
|
||||
|
||||
using namespace Valgrind::Callgrind;
|
||||
using namespace Callgrind::Internal;
|
||||
|
||||
QTextStream qerr(stderr);
|
||||
|
||||
void usage() {
|
||||
qerr << "modeltest CALLGRINDFILE ..." << endl;
|
||||
}
|
||||
|
||||
ModelTestWidget::ModelTestWidget(CallgrindWidgetHandler *handler)
|
||||
: m_format(0)
|
||||
, m_event(0)
|
||||
, m_handler(handler)
|
||||
{
|
||||
QVBoxLayout *l = new QVBoxLayout;
|
||||
setLayout(l);
|
||||
|
||||
QHBoxLayout *h = new QHBoxLayout;
|
||||
l->addLayout(h);
|
||||
l->addWidget(handler->flatView());
|
||||
|
||||
m_handler->populateActions(h);
|
||||
|
||||
m_format = new QComboBox;
|
||||
m_format->addItem("absolute", CostDelegate::FormatAbsolute);
|
||||
m_format->addItem("relative", CostDelegate::FormatRelative);
|
||||
m_format->addItem("rel. to parent", CostDelegate::FormatRelativeToParent);
|
||||
connect(m_format, SIGNAL(activated(int)),
|
||||
this, SLOT(formatChanged(int)));
|
||||
h->addWidget(m_format);
|
||||
|
||||
QDoubleSpinBox *minimumCost = new QDoubleSpinBox;
|
||||
minimumCost->setToolTip("Minimum cost");
|
||||
minimumCost->setRange(0, 1);
|
||||
minimumCost->setDecimals(4);
|
||||
minimumCost->setSingleStep(0.01);
|
||||
connect(minimumCost, SIGNAL(valueChanged(double)),
|
||||
m_handler->proxyModel(), SLOT(setMinimumInclusiveCostRatio(double)));
|
||||
minimumCost->setValue(0.0001);
|
||||
h->addWidget(minimumCost);
|
||||
|
||||
m_handler->flatView()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_handler->flatView(), SIGNAL(customContextMenuRequested(QPoint)),
|
||||
this, SLOT(showViewContextMenu(QPoint)));
|
||||
|
||||
resize(qMin(qApp->desktop()->width(), 1024), 600);
|
||||
}
|
||||
|
||||
ModelTestWidget::~ModelTestWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void ModelTestWidget::formatChanged(int index)
|
||||
{
|
||||
CostDelegate::CostFormat format = static_cast<CostDelegate::CostFormat>(m_format->itemData(index).toInt());
|
||||
m_handler->setCostFormat(format);
|
||||
}
|
||||
|
||||
void ModelTestWidget::showViewContextMenu(const QPoint &pos)
|
||||
{
|
||||
const QModelIndex idx = m_handler->flatView()->indexAt(pos);
|
||||
if (!idx.isValid())
|
||||
return;
|
||||
|
||||
QMenu menu;
|
||||
QAction *showCostItems = menu.addAction("show cost items");
|
||||
|
||||
const ParseData* data = m_handler->dataModel()->parseData();
|
||||
|
||||
QAction *ret = menu.exec(m_handler->flatView()->mapToGlobal(pos));
|
||||
if (ret == showCostItems) {
|
||||
///TODO: put this into a reusable class?
|
||||
const Function *func = idx.data(DataModel::FunctionRole).value<const Function *>();
|
||||
Q_ASSERT(func);
|
||||
|
||||
QTableView *view = new QTableView();
|
||||
view->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
const int rows = func->costItems().size();
|
||||
const int columns = data->events().size() + data->positions().size() + 2;
|
||||
QStandardItemModel *model = new QStandardItemModel(rows, columns, view);
|
||||
int headerColumn = 0;
|
||||
foreach (const QString &event, data->events()) {
|
||||
model->setHeaderData(headerColumn++, Qt::Horizontal, event);
|
||||
}
|
||||
const int lastEventColumn = headerColumn;
|
||||
foreach (const QString &pos, data->positions()) {
|
||||
model->setHeaderData(headerColumn++, Qt::Horizontal, pos);
|
||||
}
|
||||
const int lastPosColumn = headerColumn;
|
||||
model->setHeaderData(headerColumn++, Qt::Horizontal, "Call");
|
||||
model->setHeaderData(headerColumn++, Qt::Horizontal, "Differring File");
|
||||
Q_ASSERT(headerColumn == columns);
|
||||
|
||||
QVector<quint64> totalCosts;
|
||||
totalCosts.fill(0, data->events().size());
|
||||
for(int row = 0; row < rows; ++row) {
|
||||
const CostItem *item = func->costItems().at(row);
|
||||
for(int column = 0; column < columns; ++column) {
|
||||
QVariant value;
|
||||
if (column < lastEventColumn) {
|
||||
value = item->cost(column);
|
||||
totalCosts[column] += item->cost(column);
|
||||
} else if (column < lastPosColumn) {
|
||||
value = item->position(column - lastEventColumn);
|
||||
} else if (column == lastPosColumn) {
|
||||
if (item->call())
|
||||
value = item->call()->callee()->name();
|
||||
} else {
|
||||
value = item->differingFile();
|
||||
}
|
||||
model->setData(model->index(row, column), value);
|
||||
}
|
||||
}
|
||||
QStringList totalCostsStrings;
|
||||
for(int i = 0; i < totalCosts.size(); ++i) {
|
||||
totalCostsStrings << QString("%1: %2").arg(totalCosts.at(i)).arg(data->events().at(i));
|
||||
}
|
||||
view->setWindowTitle(totalCostsStrings.join(QLatin1String(", ")));
|
||||
view->setModel(model);
|
||||
view->show();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
if (app.arguments().count() < 2) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
///TODO: multi-part callgrind files
|
||||
QFile file(app.arguments().at(1));
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qerr << "could not open callgrind file for reading:" << file.fileName() << file.errorString();
|
||||
return 3;
|
||||
}
|
||||
|
||||
Parser p;
|
||||
p.parse(&file);
|
||||
|
||||
ParseData *data = p.takeData();
|
||||
if (!data) {
|
||||
qerr << "invalid callgrind file:" << file.fileName() << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
QMainWindow window;
|
||||
|
||||
CallgrindWidgetHandler *handler = new CallgrindWidgetHandler(&window);
|
||||
|
||||
ModelTestWidget *widget = new ModelTestWidget(handler);
|
||||
widget->setWindowTitle(file.fileName());
|
||||
window.setCentralWidget(widget);
|
||||
|
||||
QDockWidget *callerDock = new QDockWidget("callers", &window);
|
||||
callerDock->setWidget(handler->callersView());
|
||||
window.addDockWidget(Qt::RightDockWidgetArea, callerDock);
|
||||
|
||||
QDockWidget *calleeDock = new QDockWidget("callees", &window);
|
||||
calleeDock->setWidget(handler->calleesView());
|
||||
window.addDockWidget(Qt::RightDockWidgetArea, calleeDock);
|
||||
|
||||
QDockWidget *widgetDock = new QDockWidget("visualisation", &window);
|
||||
widgetDock->setWidget(handler->visualisation());
|
||||
window.addDockWidget(Qt::BottomDockWidgetArea, widgetDock);
|
||||
|
||||
handler->setParseData(data);
|
||||
|
||||
window.show();
|
||||
return app.exec();
|
||||
}
|
76
tests/valgrind/callgrind/modeltest.h
Normal file
76
tests/valgrind/callgrind/modeltest.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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 MODELTEST_H
|
||||
#define MODELTEST_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QComboBox;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Valgrind {
|
||||
namespace Callgrind {
|
||||
class ParseData;
|
||||
class Function;
|
||||
class CallModel;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Callgrind {
|
||||
namespace Internal {
|
||||
class CallgrindWidgetHandler;
|
||||
}
|
||||
}
|
||||
|
||||
class ModelTestWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ModelTestWidget(Callgrind::Internal::CallgrindWidgetHandler *handler);
|
||||
virtual ~ModelTestWidget();
|
||||
|
||||
public slots:
|
||||
void showViewContextMenu(const QPoint &pos);
|
||||
|
||||
void formatChanged(int);
|
||||
|
||||
public:
|
||||
QComboBox *m_format;
|
||||
QComboBox *m_event;
|
||||
Callgrind::Internal::CallgrindWidgetHandler *m_handler;
|
||||
};
|
||||
|
||||
#endif // MODELTEST_H
|
35
tests/valgrind/callgrind/modeltest.pro
Normal file
35
tests/valgrind/callgrind/modeltest.pro
Normal file
@@ -0,0 +1,35 @@
|
||||
TEMPLATE = app
|
||||
TARGET = modeltest
|
||||
|
||||
macx:CONFIG -= app_bundle
|
||||
|
||||
!win32 {
|
||||
include(../../../qtcreator.pri)
|
||||
include(../../../src/plugins/callgrind/callgrind.pri)
|
||||
}
|
||||
|
||||
SRCDIR = $$IDE_SOURCE_TREE/src
|
||||
|
||||
SOURCES += \
|
||||
modeltest.cpp \
|
||||
$$SRCDIR/plugins/callgrind/callgrindcostdelegate.cpp \
|
||||
$$SRCDIR/plugins/callgrind/callgrindhelper.cpp \
|
||||
$$SRCDIR/plugins/callgrind/callgrindcostview.cpp \
|
||||
$$SRCDIR/plugins/callgrind/callgrindnamedelegate.cpp \
|
||||
$$SRCDIR/plugins/callgrind/callgrindwidgethandler.cpp \
|
||||
$$SRCDIR/plugins/callgrind/callgrindvisualisation.cpp \
|
||||
|
||||
HEADERS += \
|
||||
modeltest.h \
|
||||
$$SRCDIR/plugins/callgrind/callgrindcostdelegate.h \
|
||||
$$SRCDIR/plugins/callgrind/callgrindcostview.h \
|
||||
$$SRCDIR/plugins/callgrind/callgrindhelper.h \
|
||||
$$SRCDIR/plugins/callgrind/callgrindnamedelegate.h \
|
||||
$$SRCDIR/plugins/callgrind/callgrindwidgethandler.h \
|
||||
$$SRCDIR/plugins/callgrind/callgrindvisualisation.h \
|
||||
|
||||
LIBS += -L$$IDE_PLUGIN_PATH/Nokia
|
||||
|
||||
INCLUDEPATH *= $$IDE_BUILD_TREE/src/plugins/coreplugin # for ide_version.h
|
||||
|
||||
DEFINES += "DISABLE_CALLGRIND_WORKAROUNDS"
|
@@ -1,3 +1,3 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += memcheck
|
||||
SUBDIRS += memcheck callgrind
|
||||
|
Reference in New Issue
Block a user