From 97cbfb95bbcebe06484edaeb446e52bec04626f3 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 19 Feb 2015 12:16:09 +0100 Subject: [PATCH] Add manual test code to check generated code size Change-Id: Idc65f441c3af09f1fcd5cff646cc7b472aed1770 Reviewed-by: Christian Stenger Reviewed-by: hjk --- tests/manual/manual.pro | 3 +- tests/manual/shootout/README | 2 + tests/manual/shootout/shootout.pro | 3 + tests/manual/shootout/tst_codesize.cpp | 322 +++++++++++++++++++++++++ 4 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 tests/manual/shootout/README create mode 100644 tests/manual/shootout/shootout.pro create mode 100644 tests/manual/shootout/tst_codesize.cpp diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index 037484193f3..96654718406 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -4,7 +4,8 @@ SUBDIRS= \ fakevim \ debugger \ subdir_proparser \ -utils +utils \ +shootout unix { # Uses popen diff --git a/tests/manual/shootout/README b/tests/manual/shootout/README new file mode 100644 index 00000000000..abb259cb59d --- /dev/null +++ b/tests/manual/shootout/README @@ -0,0 +1,2 @@ +This "test" is to experiment with code constructs to +get an impression on code size etc. diff --git a/tests/manual/shootout/shootout.pro b/tests/manual/shootout/shootout.pro new file mode 100644 index 00000000000..67be07b4eb4 --- /dev/null +++ b/tests/manual/shootout/shootout.pro @@ -0,0 +1,3 @@ +QT += testlib + +SOURCES += tst_codesize.cpp diff --git a/tests/manual/shootout/tst_codesize.cpp b/tests/manual/shootout/tst_codesize.cpp new file mode 100644 index 00000000000..ee1048d3075 --- /dev/null +++ b/tests/manual/shootout/tst_codesize.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include +#include +#include +#include + +#include + +using namespace std; + +enum Flags +{ + NoFlags = 0, + Optimize = 1 << 1, + DebugInfo = 1 << 2 +}; + +struct Case +{ + Case() {} + Case(const QByteArray &f, const QByteArray &c) + : file(f), code(c) + {} + + QByteArray file; + QByteArray code; + QByteArray gist; + QByteArray extraCxxFlags; + bool useExceptions; +}; + +struct Suite +{ + Suite() : flags(0) {} + + QByteArray title; + int flags; + QByteArray cmd; + QVector cases; +}; + +Q_DECLARE_METATYPE(Case) +Q_DECLARE_METATYPE(Suite) + +struct TempStuff +{ + TempStuff() : buildTemp(QLatin1String("qt_tst_codesize")) + { + buildPath = QDir::currentPath() + QLatin1Char('/') + buildTemp.path(); + buildTemp.setAutoRemove(false); + QVERIFY(!buildPath.isEmpty()); + } + + QByteArray input; + QTemporaryDir buildTemp; + QString buildPath; +}; + +class tst_CodeSize : public QObject +{ + Q_OBJECT + +public: + tst_CodeSize() + { + m_makeBinary = "make"; + m_keepTemp = false; + m_forceKeepTemp = false; + } + +private slots: + void initTestCase(); + void codesize(); + void codesize_data(); + void init(); + void cleanup(); + +private: + void disarm() { t->buildTemp.setAutoRemove(!keepTemp()); } + bool keepTemp() const { return m_keepTemp || m_forceKeepTemp; } + TempStuff *t; + QByteArray m_qmakeBinary; + QProcessEnvironment m_env; + QString m_makeBinary; + bool m_keepTemp; + bool m_forceKeepTemp; +}; + +void tst_CodeSize::initTestCase() +{ + m_qmakeBinary = qgetenv("QTC_QMAKE_PATH_FOR_TEST"); + if (m_qmakeBinary.isEmpty()) + m_qmakeBinary = "qmake"; + qDebug() << "QMake : " << m_qmakeBinary.constData(); + + m_forceKeepTemp = qgetenv("QTC_KEEP_TEMP_FOR_TEST").toInt(); + qDebug() << "Force keep temp : " << m_forceKeepTemp; +} + +void tst_CodeSize::init() +{ + t = new TempStuff(); +} + +void tst_CodeSize::cleanup() +{ + if (!t->buildTemp.autoRemove()) { + QFile logger(t->buildPath + QLatin1String("/input.txt")); + logger.open(QIODevice::ReadWrite); + logger.write(t->input); + } + delete t; +} + +void tst_CodeSize::codesize() +{ + QFETCH(Suite, suite); + static int suiteCount = 0; + ++suiteCount; + + QFile bigPro(t->buildPath + QLatin1String("/doit.pro")); + QVERIFY(bigPro.open(QIODevice::ReadWrite)); + bigPro.write("\nTEMPLATE = subdirs\n"); + bigPro.write("\nCONFIG += ordered\n"); + + QFile mainPro(t->buildPath + "/main.pro"); + QVERIFY(mainPro.open(QIODevice::ReadWrite)); + mainPro.write("\n\nSOURCES += main.cpp"); + + QFile mainCpp(t->buildPath + QLatin1String("/main.cpp")); + QVERIFY(mainCpp.open(QIODevice::ReadWrite)); + mainCpp.write("\n" + "int main()\n" + "{\n" + "}\n"); + mainCpp.close(); + + foreach (const Case &c, suite.cases) { + QByteArray caseProName = c.file + ".pro"; + bigPro.write("\nSUBDIRS += " + caseProName); + mainPro.write("\nLIBS += -l" + c.file); + + QFile casePro(t->buildPath + '/' + caseProName); + QVERIFY(casePro.open(QIODevice::ReadWrite)); + casePro.write("\nTEMPLATE = lib"); + casePro.write("\nTARGET = " + c.file + "\n"); + casePro.write("\nCONFIG += staticlib\n"); + casePro.write("\nCONFIG += c++11\n"); + casePro.write("\nCONFIG -= app_bundle\n"); + + if (suite.flags & Optimize) { + casePro.write("\nCONFIG -= debug"); + casePro.write("\nCONFIG += release"); + } else { + casePro.write("\nCONFIG -= release"); + casePro.write("\nCONFIG += debug"); + } + + casePro.write("\nSOURCES += " + c.file + ".cpp"); + if (!c.extraCxxFlags.isEmpty()) + casePro.write("\nQMAKE_CXXFLAGS += " + c.extraCxxFlags); + + QFile caseCpp(t->buildPath + QLatin1Char('/') + QLatin1String(c.file + ".cpp")); + QVERIFY(caseCpp.open(QIODevice::ReadWrite)); + QByteArray fullCode = c.code; + caseCpp.write(fullCode); + caseCpp.close(); + } + + mainPro.write("\nLIBS += -L$$OUT_PWD"); + mainPro.close(); + + bigPro.write("\nSUBDIRS += main.pro"); + bigPro.close(); + + QProcess qmake; + qmake.setWorkingDirectory(t->buildPath); + QString cmd = QString::fromLatin1(m_qmakeBinary + " -r doit.pro"); + //qDebug() << "Starting qmake: " << cmd; + qmake.start(cmd); +// QVERIFY(qmake.waitForFinished()); + qmake.waitForFinished(); + QByteArray output = qmake.readAllStandardOutput(); + QByteArray error = qmake.readAllStandardError(); + //qDebug() << "stdout: " << output; + if (!error.isEmpty()) { qDebug() << error; QVERIFY(false); } + + QProcess make; + make.setWorkingDirectory(t->buildPath); + make.setProcessEnvironment(m_env); + + make.start(m_makeBinary, QStringList()); + QVERIFY(make.waitForFinished()); + output = make.readAllStandardOutput(); + error = make.readAllStandardError(); + //qDebug() << "stdout: " << output; + if (make.exitCode()) { + qDebug() << error; +// qDebug() << "\n------------------ CODE --------------------"; +// qDebug() << fullCode; +// qDebug() << "\n------------------ CODE --------------------"; + qDebug() << ".pro: " << qPrintable(bigPro.fileName()); + } + + cout << "\n################################################################\n\n" + << suite.title.data(); + + bool ok = true; + int i = 0; + foreach (const Case &c, suite.cases) { + ++i; + cout << "\n\n===================== VARIANT " << suiteCount << '.' << i << ' ' + << " ================================" + << "\n\nCode: " << c.gist.data() + << "\nOptimized: " << ((suite.flags & Optimize) ? "Yes" : "No") + << "\nExtra CXX Flags: " << c.extraCxxFlags.data(); + QByteArray finalCommand = suite.cmd + ' ' + c.file + ".o"; + QProcess final; + final.setWorkingDirectory(t->buildPath); + final.setProcessEnvironment(m_env); + cout << "\nCommand: " << finalCommand.data() + << "\n\n--------------------- OUTPUT " << suiteCount << '.' << i << ' ' + << " ---------------------------------\n\n"; + final.start(finalCommand); + QVERIFY(final.waitForFinished()); + QByteArray stdOut = final.readAllStandardOutput(); + QByteArray stdErr = final.readAllStandardError(); + + cout << stdOut.data(); + if (!stdErr.isEmpty()) { + ok = false; + qDebug() << "ERR: " << stdErr; + break; + } + } + cout << "\n#################################################################\n"; + + QVERIFY(ok); + disarm(); +} + +void tst_CodeSize::codesize_data() +{ + QTest::addColumn("suite"); + + Suite s; + s.flags = Optimize; +// FIXME: Cannot be hardcoded. Assume matching qmake for now. +#ifdef Q_CC_MSVC + s.cmd = "dumpbin /DISASM /SECTION:.text$mn"; +#else + s.cmd = "objdump -D -j.text"; +#endif + s.title = "This 'test' compares different approaches to return something \n" + "like an immutable string from a function."; + Case c; + c.file = "latin1string"; + c.gist = "QString f1() { return QLatin1String(\"foo\"); }\n"; + c.code = "#include \n" + c.gist; + s.cases.append(c); + + c.file = "qstringliteral"; + c.gist = "QString f2() { return QStringLiteral(\"foo\"); }\n"; + c.code = "#include \n" + c.gist; + s.cases.append(c); + + c.file = "std_string"; + c.gist = "std::string f3() { return \"foo\"; }\n"; + c.code = "#include \n" + c.gist; + s.cases.append(c); + + c.file = "std_string_2"; + c.gist = "std::string f4() { return \"foo\"; }\n"; + c.code = "#include \n" + c.gist; + c.extraCxxFlags = "-fno-stack-protector"; + s.cases.append(c); + + c.file = "char_pointer"; + c.gist = "const char *f5() { return \"foo\"; }\n"; + c.code = c.gist; + s.cases.append(c); + + QTest::newRow("return string (no stack protector)") << s; +} + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + tst_CodeSize test; + return QTest::qExec(&test, argc, argv); +} + +#include "tst_codesize.moc"