<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtCore/QtCore>
#include <QtTest/QtTest>

#ifdef QT_NAMESPACE
#define STRINGIFY_HELPER(s) #s
#define STRINGIFY(s) STRINGIFY_HELPER(s)
QString ns = STRINGIFY(QT_NAMESPACE) + QString("::");
#else
QString ns;
#endif

class tst_Symbols: public QObject
{
    Q_OBJECT
private slots:
    void prefix();
    void globalObjects();
};

/* Computes the line number from a symbol */
static QString symbolToLine(const QString &symbol, const QString &lib)
{
    // nm outputs the symbol name, the type, the address and the size
    QRegExp re("global constructors keyed to ([a-zA-Z_0-9.]*) (.) ([0-f]*) ([0-f]*)");
    if (re.indexIn(symbol) == -1)
        return QString();

    // address and symbolSize are in hex. Convert to integers
    bool ok;
    int symbolAddress = re.cap(3).toInt(&ok, 16);
    if (!ok)
        return QString();
    int symbolSize = re.cap(4).toInt(&ok, 16);
    if (!ok)
        return QString();

    // now, find the start address, which is the address - size
    QString startAddress = QString::number(symbolAddress - symbolSize, 16);

    QProcess proc;
    proc.start("addr2line", QStringList() << "-e" << lib << startAddress);
    if (!proc.waitForFinished())
        return QString();

    QString result = QString::fromLocal8Bit(proc.readLine());
    result.chop(1); // chop tailing newline
    return result;
}

/* This test searches through all Qt libraries and searches for symbols
   starting with "global constructors keyed to "

   These indicate static global objects, which should not be used in shared
   libraries - use Q_GLOBAL_STATIC instead.
*/
void tst_Symbols::globalObjects()
{
#ifndef Q_OS_LINUX
    QSKIP("Linux-specific test", SkipAll);
#endif
    QSKIP("Test disabled, we're not fixing these issues in this Qt version", SkipAll);

    // these are regexps for global objects that are allowed in Qt
    QStringList whitelist = QStringList()
        // ignore qInitResources - they are safe to use
        << "^_Z[0-9]*qInitResources_"
        << "qrc_.*\\.cpp"
        // ignore qRegisterGuiVariant - it's a safe fallback to register GUI Variants
        << "qRegisterGuiVariant";

    bool isFailed = false;

    QDir dir(QLibraryInfo::location(QLibraryInfo::LibrariesPath), "*.so");
    QStringList files = dir.entryList();
    QVERIFY(!files.isEmpty());

    foreach (QString lib, files) {
        if (lib == "libQtCLucene.so") {
            // skip this library, it's 3rd-party C++
            continue;
        }
        if (lib == "libQt3Support.so") {
            // we're not going to fix these issues anyway, so skip this library
            continue;
        }

        QProcess proc;
        proc.start("nm",
           QStringList() << "-C" << "--format=posix"
                         << dir.absolutePath() + "/" + lib);
        QVERIFY(proc.waitForFinished());
        QCOMPARE(proc.exitCode(), 0);
        QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString());

        QStringList symbols = QString::fromLocal8Bit(proc.readAll()).split("\n");
        QVERIFY(!symbols.isEmpty());
        foreach (QString symbol, symbols) {
            if (symbol.isEmpty())
                continue;

            if (!symbol.startsWith("global constructors keyed to "))
                continue;

            QRegExp re("global constructors keyed to ([a-zA-Z_0-9.]*)");
            QVERIFY(re.indexIn(symbol) != -1);

            QString cap = re.cap(1);

            bool whitelisted = false;
            foreach (QString white, whitelist) {
                if (cap.indexOf(QRegExp(white)) != -1) {
                    whitelisted = true;
                    break;
                }
            }
            if (whitelisted)
                continue;

            QString line = symbolToLine(symbol, dir.absolutePath() + "/" + lib);

            if (cap.contains('.'))
                QWARN(qPrintable("Static global object(s) found in " + lib + " in file " + cap + " (" + line + ")"));
            else
                QWARN(qPrintable("Static global object found in " + lib + " near symbol " + cap + " (" + line + ")"));

            isFailed = true;
        }
    }

    if (isFailed) {
#if QT_VERSION >= 0x040600
        QVERIFY2(!isFailed, "Libraries contain static global objects. See Debug output above.");
#else
        QSKIP("Libraries contains static global objects. See Debug output above. [These errors cannot be fixed in 4.5 in time]", SkipSingle);
#endif
    }
}

void tst_Symbols::prefix()
{
#if defined(QT_CROSS_COMPILED)
    QSKIP("Probably no compiler on the target", SkipAll);
#elif defined(Q_OS_LINUX)
    QStringList qtTypes;
    qtTypes << "QString" << "QChar" << "QWidget" << "QObject" << "QVariant" << "QList"
            << "QMap" << "QHash" << "QVector" << "QRect" << "QSize" << "QPoint"
            << "QTextFormat" << "QTextLength" << "QPen" << "QFont" << "QIcon"
            << "QPixmap" << "QImage" << "QRegion" << "QPolygon";
    QStringList qAlgorithmFunctions;
    qAlgorithmFunctions << "qBinaryFind" << "qLowerBound" << "qUpperBound"
                        << "qAbs" << "qMin" << "qMax" << "qBound" << "qSwap"
                        << "qHash" << "qDeleteAll" << "qCopy" << "qSort";

    QStringList exceptionalSymbols;
    exceptionalSymbols << "XRectangle::~XRectangle()"
                       << "XChar2b::~XChar2b()"
                       << "XPoint::~XPoint()"
                       << "glyph_metrics_t::"; // #### Qt 4.2

    QStringList stupidCSymbols;
    stupidCSymbols << "Add_Glyph_Property"
                   << "Check_Property"
                   << "Coverage_Index"
                   << "Get_Class"
                   << "Get_Device"
                   << "rcsid3"
                   << "sfnt_module_class"
                   << "t1cid_driver_class"
                   << "t42_driver_class"
                   << "winfnt_driver_class"
                   << "pshinter_module_class"
                   << "psnames_module_class"
                   ;

    QHash<QString,QStringList> excusedPrefixes;
    excusedPrefixes[QString()] =
        QStringList() << "Ui_Q"; // uic generated, like Ui_QPrintDialog

    excusedPrefixes["QtCore"] =
        QStringList() << "hb_"
                      << "HB_"
                      // zlib symbols, for -qt-zlib ;(
                      << "deflate"
                      << "compress"
                      << "uncompress"
                      << "adler32"
                      << "gz"
                      << "inflate"
                      << "zlib"
                      << "zError"
                      << "get_crc_table"
                      << "crc32";

    excusedPrefixes["QtGui"] =
        QStringList() << "ftglue_"
                      << "Load_"
                      << "otl_"
                      << "TT_"
                      << "tt_"
                      << "t1_"
                      << "Free_"
                      << "FT_"
                      << "FTC_"
                      << "ft_"
                      << "ftc_"
                      << "af_autofitter"
                      << "af_dummy"
                      << "af_latin"
                      << "autofit_"
                      << "XPanorami"
                      << "Xinerama"
                      << "bdf_"
                      << "ccf_"
                      << "gray_raster"
                      << "pcf_"
                      << "cff_"
                      << "otv_"
                      << "pfr_"
                      << "ps_"
                      << "psaux"
                      << "png_";

    excusedPrefixes["QtSql"] =
        QStringList() << "sqlite3";

    excusedPrefixes["QtWebKit"] =
        QStringList() << "WebCore::"
                      << "KJS::"
                      << "kjs"
                      << "kJS"
                      << "JS"
//                      << "OpaqueJS"
                      << "WTF"
                      << "wtf_"
                      << "SVG::"
                      << "NPN_"
                      << "cti"  // ctiTrampoline and ctiVMThrowTrampoline from the JIT
#ifdef QT_NAMESPACE
                      << "QWeb" // Webkit is only 'namespace aware'
#endif
        ;

    excusedPrefixes["phonon"] =
        QStringList() << ns + "Phonon";

    QDir dir(qgetenv("QTDIR") + "/lib", "*.so");
    QStringList files = dir.entryList();
    QVERIFY(!files.isEmpty());

    bool isFailed = false;
    foreach (QString lib, files) {
        if (lib.contains("Designer") || lib.contains("QtCLucene") || lib.contains("XmlPatternsSDK"))
            continue;

        bool isPhonon = lib.contains("phonon");

        QProcess proc;
        proc.start("nm",
           QStringList() << "-g" << "-C" << "-D" << "--format=posix"
                         << "--defined-only" << dir.absolutePath() + "/" + lib);
        QVERIFY(proc.waitForFinished());
        QCOMPARE(proc.exitCode(), 0);
        QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString());

        QStringList symbols = QString::fromLocal8Bit(proc.readAll()).split("\n");
        QVERIFY(!symbols.isEmpty());
        foreach (QString symbol, symbols) {
            if (symbol.isEmpty())
                continue;

            if (symbol.startsWith("unsigned "))
                // strip modifiers
                symbol = symbol.mid(symbol.indexOf(' ') + 1);
            if (symbol.startsWith("long long ")) {
                symbol = symbol.mid(10);
            } else if (symbol.startsWith("bool ") || symbol.startsWith("bool* ")
                || symbol.startsWith("char ") || symbol.startsWith("char* ")
                || symbol.startsWith("int ") || symbol.startsWith("int* ") || symbol.startsWith("int*&")
                || symbol.startsWith("short") || symbol.startsWith("long ")
                || symbol.startsWith("void ") || symbol.startsWith("void* ")
                || symbol.startsWith("double ") || symbol.startsWith("double* ")
                || symbol.startsWith("float ") || symbol.startsWith("float* ")) {
                // partial templates have the return type in their demangled name, strip it
                symbol = symbol.mid(symbol.indexOf(' ') + 1);
            }
            if (symbol.startsWith("const ") || symbol.startsWith("const* ") ||
                symbol.startsWith("const& ")) {
                // strip modifiers
                symbol = symbol.mid(symbol.indexOf(' ') + 1);
            }

            if (symbol.startsWith("_") || symbol.startsWith("std::"))
                continue;
            if (symbol.startsWith("vtable ") || symbol.startsWith("VTT for ") ||
                symbol.startsWith("construction vtable for"))
                continue;
            if (symbol.startsWith("typeinfo "))
                continue;
            if (symbol.startsWith("non-virtual thunk ") || symbol.startsWith("virtual thunk"))
                continue;
            if (symbol.startsWith(ns + "operator"))
                continue;
            if (symbol.startsWith("guard variable for "))
                continue;
            if (symbol.contains("(" + ns + "QTextStream"))
                // QTextStream is excused.
                continue;
            if (symbol.contains("(" + ns + "Q3TextStream"))
                // Q3TextStream is excused.
                continue;
            if (symbol.startsWith(ns + "bitBlt") || symbol.startsWith(ns + "copyBlt"))
                // you're excused, too
                continue;

            bool symbolOk = false;

            QHash<QString,QStringList>::ConstIterator it = excusedPrefixes.constBegin();
            for ( ; it != excusedPrefixes.constEnd(); ++it) {
                if (!lib.contains(it.key()))
                    continue;
                foreach (QString prefix, it.value())
                    if (symbol.startsWith(prefix)) {
                        symbolOk = true;
                        break;
                    }
            }

            if (symbolOk)
                continue;

            foreach (QString cSymbolPattern, stupidCSymbols)
                if (symbol.contains(cSymbolPattern)) {
                    symbolOk = true;
                    break;
                }

            if (symbolOk)
                continue;

            QStringList fields = symbol.split(' ');
            // the last two fields are address and size and the third last field is the symbol type
            QVERIFY(fields.count() > 3);
            QString type = fields.at(fields.count() - 3);
            // weak symbol
            if (type == QLatin1String("W")) {
                if (symbol.contains("qAtomic"))
                    continue;

                if (symbol.contains("fstat")
                    || symbol.contains("lstat")
                    || symbol.contains("stat64")
                   )
                    continue;

                foreach (QString acceptedPattern, qAlgorithmFunctions + exceptionalSymbols)
                    if (symbol.contains(acceptedPattern)) {
                        symbolOk = true;
                        break;
                    }

                if (symbolOk)
                    continue;

                QString plainSymbol;
                for (int i = 0; i < fields.count() - 3; ++i) {
                    if (i > 0)
                        plainSymbol += QLatin1Char(' ');
                    plainSymbol += fields.at(i);
                }
                foreach (QString qtType, qtTypes)
                    if (plainSymbol.contains(qtType)) {
                        symbolOk = true;
                        break;
                    }

                if (symbolOk)
                    continue;
            }

            QString prefix = ns + "q";
            if (!symbol.startsWith(prefix, Qt::CaseInsensitive)
                && !(isPhonon && symbol.startsWith("Phonon"))) {
                qDebug("symbol in '%s' does not start with prefix '%s': '%s'",
                    qPrintable(lib), qPrintable(prefix), qPrintable(symbol));
                isFailed = true;
            }
        }
    }

#  if defined(Q_OS_LINUX) && defined(Q_CC_INTEL)
    QEXPECT_FAIL("", "linux-icc* incorrectly exports some QtWebkit symbols, waiting for a fix from Intel.", Continue);
#  endif
    QVERIFY2(!isFailed, "Libraries contain non-prefixed symbols. See Debug output :)");
#else
    QSKIP("Linux-specific test", SkipAll);
#endif
}

QTEST_MAIN(tst_Symbols)
#include "tst_symbols.moc"
<commit_msg>Fixed failure of tst_symbols when Qt is configured with -qtnamespace<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtCore/QtCore>
#include <QtTest/QtTest>

#ifdef QT_NAMESPACE
#define STRINGIFY_HELPER(s) #s
#define STRINGIFY(s) STRINGIFY_HELPER(s)
QString ns = STRINGIFY(QT_NAMESPACE) + QString("::");
#else
QString ns;
#endif

class tst_Symbols: public QObject
{
    Q_OBJECT
private slots:
    void prefix();
    void globalObjects();
};

/* Computes the line number from a symbol */
static QString symbolToLine(const QString &symbol, const QString &lib)
{
    // nm outputs the symbol name, the type, the address and the size
    QRegExp re("global constructors keyed to ([a-zA-Z_0-9.]*) (.) ([0-f]*) ([0-f]*)");
    if (re.indexIn(symbol) == -1)
        return QString();

    // address and symbolSize are in hex. Convert to integers
    bool ok;
    int symbolAddress = re.cap(3).toInt(&ok, 16);
    if (!ok)
        return QString();
    int symbolSize = re.cap(4).toInt(&ok, 16);
    if (!ok)
        return QString();

    // now, find the start address, which is the address - size
    QString startAddress = QString::number(symbolAddress - symbolSize, 16);

    QProcess proc;
    proc.start("addr2line", QStringList() << "-e" << lib << startAddress);
    if (!proc.waitForFinished())
        return QString();

    QString result = QString::fromLocal8Bit(proc.readLine());
    result.chop(1); // chop tailing newline
    return result;
}

/* This test searches through all Qt libraries and searches for symbols
   starting with "global constructors keyed to "

   These indicate static global objects, which should not be used in shared
   libraries - use Q_GLOBAL_STATIC instead.
*/
void tst_Symbols::globalObjects()
{
#ifndef Q_OS_LINUX
    QSKIP("Linux-specific test", SkipAll);
#endif
    QSKIP("Test disabled, we're not fixing these issues in this Qt version", SkipAll);

    // these are regexps for global objects that are allowed in Qt
    QStringList whitelist = QStringList()
        // ignore qInitResources - they are safe to use
        << "^_Z[0-9]*qInitResources_"
        << "qrc_.*\\.cpp"
        // ignore qRegisterGuiVariant - it's a safe fallback to register GUI Variants
        << "qRegisterGuiVariant";

    bool isFailed = false;

    QDir dir(QLibraryInfo::location(QLibraryInfo::LibrariesPath), "*.so");
    QStringList files = dir.entryList();
    QVERIFY(!files.isEmpty());

    foreach (QString lib, files) {
        if (lib == "libQtCLucene.so") {
            // skip this library, it's 3rd-party C++
            continue;
        }
        if (lib == "libQt3Support.so") {
            // we're not going to fix these issues anyway, so skip this library
            continue;
        }

        QProcess proc;
        proc.start("nm",
           QStringList() << "-C" << "--format=posix"
                         << dir.absolutePath() + "/" + lib);
        QVERIFY(proc.waitForFinished());
        QCOMPARE(proc.exitCode(), 0);
        QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString());

        QStringList symbols = QString::fromLocal8Bit(proc.readAll()).split("\n");
        QVERIFY(!symbols.isEmpty());
        foreach (QString symbol, symbols) {
            if (symbol.isEmpty())
                continue;

            if (!symbol.startsWith("global constructors keyed to "))
                continue;

            QRegExp re("global constructors keyed to ([a-zA-Z_0-9.]*)");
            QVERIFY(re.indexIn(symbol) != -1);

            QString cap = re.cap(1);

            bool whitelisted = false;
            foreach (QString white, whitelist) {
                if (cap.indexOf(QRegExp(white)) != -1) {
                    whitelisted = true;
                    break;
                }
            }
            if (whitelisted)
                continue;

            QString line = symbolToLine(symbol, dir.absolutePath() + "/" + lib);

            if (cap.contains('.'))
                QWARN(qPrintable("Static global object(s) found in " + lib + " in file " + cap + " (" + line + ")"));
            else
                QWARN(qPrintable("Static global object found in " + lib + " near symbol " + cap + " (" + line + ")"));

            isFailed = true;
        }
    }

    if (isFailed) {
#if QT_VERSION >= 0x040600
        QVERIFY2(!isFailed, "Libraries contain static global objects. See Debug output above.");
#else
        QSKIP("Libraries contains static global objects. See Debug output above. [These errors cannot be fixed in 4.5 in time]", SkipSingle);
#endif
    }
}

void tst_Symbols::prefix()
{
#if defined(QT_CROSS_COMPILED)
    QSKIP("Probably no compiler on the target", SkipAll);
#elif defined(Q_OS_LINUX)
    QStringList qtTypes;
    qtTypes << "QString" << "QChar" << "QWidget" << "QObject" << "QVariant" << "QList"
            << "QMap" << "QHash" << "QVector" << "QRect" << "QSize" << "QPoint"
            << "QTextFormat" << "QTextLength" << "QPen" << "QFont" << "QIcon"
            << "QPixmap" << "QImage" << "QRegion" << "QPolygon";
    QStringList qAlgorithmFunctions;
    qAlgorithmFunctions << "qBinaryFind" << "qLowerBound" << "qUpperBound"
                        << "qAbs" << "qMin" << "qMax" << "qBound" << "qSwap"
                        << "qHash" << "qDeleteAll" << "qCopy" << "qSort";

    QStringList exceptionalSymbols;
    exceptionalSymbols << "XRectangle::~XRectangle()"
                       << "XChar2b::~XChar2b()"
                       << "XPoint::~XPoint()"
                       << "glyph_metrics_t::"; // #### Qt 4.2

    QStringList stupidCSymbols;
    stupidCSymbols << "Add_Glyph_Property"
                   << "Check_Property"
                   << "Coverage_Index"
                   << "Get_Class"
                   << "Get_Device"
                   << "rcsid3"
                   << "sfnt_module_class"
                   << "t1cid_driver_class"
                   << "t42_driver_class"
                   << "winfnt_driver_class"
                   << "pshinter_module_class"
                   << "psnames_module_class"
                   // C symbols from Qt
                   << "qt_addObject"
                   << "qt_removeObject"
                   << "qt_startup_hook"
                   ;

    QHash<QString,QStringList> excusedPrefixes;
    excusedPrefixes[QString()] =
        QStringList() << "Ui_Q"; // uic generated, like Ui_QPrintDialog

    excusedPrefixes["QtCore"] =
        QStringList() << "hb_"
                      << "HB_"
                      // zlib symbols, for -qt-zlib ;(
                      << "deflate"
                      << "compress"
                      << "uncompress"
                      << "adler32"
                      << "gz"
                      << "inflate"
                      << "zlib"
                      << "zError"
                      << "get_crc_table"
                      << "crc32";

    excusedPrefixes["QtGui"] =
        QStringList() << "ftglue_"
                      << "Load_"
                      << "otl_"
                      << "TT_"
                      << "tt_"
                      << "t1_"
                      << "Free_"
                      << "FT_"
                      << "FTC_"
                      << "ft_"
                      << "ftc_"
                      << "af_autofitter"
                      << "af_dummy"
                      << "af_latin"
                      << "autofit_"
                      << "XPanorami"
                      << "Xinerama"
                      << "bdf_"
                      << "ccf_"
                      << "gray_raster"
                      << "pcf_"
                      << "cff_"
                      << "otv_"
                      << "pfr_"
                      << "ps_"
                      << "psaux"
                      << "png_";

    excusedPrefixes["QtSql"] =
        QStringList() << "sqlite3";

    excusedPrefixes["QtWebKit"] =
        QStringList() << "WebCore::"
                      << "KJS::"
                      << "kjs"
                      << "kJS"
                      << "JS"
//                      << "OpaqueJS"
                      << "WTF"
                      << "wtf_"
                      << "SVG::"
                      << "NPN_"
                      << "cti"  // ctiTrampoline and ctiVMThrowTrampoline from the JIT
#ifdef QT_NAMESPACE
                      << "QWeb" // Webkit is only 'namespace aware'
                      << "qWeb"
                      << "qt"
                      << "QGraphicsWebView"
                      << "operator"
#endif
        ;

    excusedPrefixes["phonon"] =
        QStringList() << ns + "Phonon";

    QDir dir(qgetenv("QTDIR") + "/lib", "*.so");
    QStringList files = dir.entryList();
    QVERIFY(!files.isEmpty());

    bool isFailed = false;
    foreach (QString lib, files) {
        if (lib.contains("Designer") || lib.contains("QtCLucene") || lib.contains("XmlPatternsSDK"))
            continue;

        bool isPhonon = lib.contains("phonon");

        QProcess proc;
        proc.start("nm",
           QStringList() << "-g" << "-C" << "-D" << "--format=posix"
                         << "--defined-only" << dir.absolutePath() + "/" + lib);
        QVERIFY(proc.waitForFinished());
        QCOMPARE(proc.exitCode(), 0);
        QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString());

        QStringList symbols = QString::fromLocal8Bit(proc.readAll()).split("\n");
        QVERIFY(!symbols.isEmpty());
        foreach (QString symbol, symbols) {
            if (symbol.isEmpty())
                continue;

            if (symbol.startsWith("unsigned "))
                // strip modifiers
                symbol = symbol.mid(symbol.indexOf(' ') + 1);
            if (symbol.startsWith("long long ")) {
                symbol = symbol.mid(10);
            } else if (symbol.startsWith("bool ") || symbol.startsWith("bool* ")
                || symbol.startsWith("char ") || symbol.startsWith("char* ")
                || symbol.startsWith("int ") || symbol.startsWith("int* ") || symbol.startsWith("int*&")
                || symbol.startsWith("short") || symbol.startsWith("long ")
                || symbol.startsWith("void ") || symbol.startsWith("void* ")
                || symbol.startsWith("double ") || symbol.startsWith("double* ")
                || symbol.startsWith("float ") || symbol.startsWith("float* ")) {
                // partial templates have the return type in their demangled name, strip it
                symbol = symbol.mid(symbol.indexOf(' ') + 1);
            }
            if (symbol.startsWith("const ") || symbol.startsWith("const* ") ||
                symbol.startsWith("const& ")) {
                // strip modifiers
                symbol = symbol.mid(symbol.indexOf(' ') + 1);
            }

            if (symbol.mid(symbol.indexOf(' ')+1).startsWith("std::"))
                continue;
            if (symbol.startsWith("_") || symbol.startsWith("std::"))
                continue;
            if (symbol.startsWith("vtable ") || symbol.startsWith("VTT for ") ||
                symbol.startsWith("construction vtable for"))
                continue;
            if (symbol.startsWith("typeinfo "))
                continue;
            if (symbol.startsWith("non-virtual thunk ") || symbol.startsWith("virtual thunk"))
                continue;
            if (symbol.startsWith(ns + "operator"))
                continue;
            if (symbol.startsWith("operator new") || symbol.startsWith("operator delete"))
                continue;
            if (symbol.startsWith("guard variable for "))
                continue;
            if (symbol.contains("(" + ns + "QTextStream"))
                // QTextStream is excused.
                continue;
            if (symbol.contains("(" + ns + "Q3TextStream"))
                // Q3TextStream is excused.
                continue;
            if (symbol.startsWith(ns + "bitBlt") || symbol.startsWith(ns + "copyBlt"))
                // you're excused, too
                continue;

            bool symbolOk = false;

            QHash<QString,QStringList>::ConstIterator it = excusedPrefixes.constBegin();
            for ( ; it != excusedPrefixes.constEnd(); ++it) {
                if (!lib.contains(it.key()))
                    continue;
                foreach (QString prefix, it.value())
                    if (symbol.startsWith(prefix)) {
                        symbolOk = true;
                        break;
                    }
            }

            if (symbolOk)
                continue;

            foreach (QString cSymbolPattern, stupidCSymbols)
                if (symbol.contains(cSymbolPattern)) {
                    symbolOk = true;
                    break;
                }

            if (symbolOk)
                continue;

            QStringList fields = symbol.split(' ');
            // the last two fields are address and size and the third last field is the symbol type
            QVERIFY(fields.count() > 3);
            QString type = fields.at(fields.count() - 3);
            // weak symbol
            if (type == QLatin1String("W")) {
                if (symbol.contains("qAtomic"))
                    continue;

                if (symbol.contains("fstat")
                    || symbol.contains("lstat")
                    || symbol.contains("stat64")
                   )
                    continue;

                foreach (QString acceptedPattern, qAlgorithmFunctions + exceptionalSymbols)
                    if (symbol.contains(acceptedPattern)) {
                        symbolOk = true;
                        break;
                    }

                if (symbolOk)
                    continue;

                QString plainSymbol;
                for (int i = 0; i < fields.count() - 3; ++i) {
                    if (i > 0)
                        plainSymbol += QLatin1Char(' ');
                    plainSymbol += fields.at(i);
                }
                foreach (QString qtType, qtTypes)
                    if (plainSymbol.contains(qtType)) {
                        symbolOk = true;
                        break;
                    }

                if (symbolOk)
                    continue;
            }

            QString prefix = ns + "q";
            if (!symbol.startsWith(prefix, Qt::CaseInsensitive)
                && !(isPhonon && symbol.startsWith("Phonon"))) {
                qDebug("symbol in '%s' does not start with prefix '%s': '%s'",
                    qPrintable(lib), qPrintable(prefix), qPrintable(symbol));
                isFailed = true;
            }
        }
    }

#  if defined(Q_OS_LINUX) && defined(Q_CC_INTEL)
    QEXPECT_FAIL("", "linux-icc* incorrectly exports some QtWebkit symbols, waiting for a fix from Intel.", Continue);
#  endif
    QVERIFY2(!isFailed, "Libraries contain non-prefixed symbols. See Debug output :)");
#else
    QSKIP("Linux-specific test", SkipAll);
#endif
}

QTEST_MAIN(tst_Symbols)
#include "tst_symbols.moc"
<|endoftext|>


<commit_before>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */

#include <deltamain/InsertHash.hpp>

#include <gtest/gtest.h>

using namespace tell::store;
using namespace tell::store::deltamain;

namespace {

class InsertTableTest : public ::testing::Test {
protected:
    InsertTableTest()
            : mTable(128),
              mElement1(0x1u), mElement2(0x2u), mElement3(0x3u) {
    }

    InsertTable mTable;

    uint64_t mElement1;
    uint64_t mElement2;
    uint64_t mElement3;
};

/**
 * @class InsertTable
 * @test Check if a simple get after insert returns the element
 */
TEST_F(InsertTableTest, insertAndGet) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));

    EXPECT_EQ(&mElement1, mTable.get(10u));
}

/**
 * @class InsertTable
 * @test Check if multiple get and inserts return the correct elements
 */
TEST_F(InsertTableTest, insertAndGetMultiple) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));
    EXPECT_TRUE(mTable.insert(11u, &mElement2));
    EXPECT_TRUE(mTable.insert(138u, &mElement3));

    EXPECT_EQ(&mElement1, mTable.get(10u));
    EXPECT_EQ(&mElement2, mTable.get(11u));
    EXPECT_EQ(&mElement3, mTable.get(138u));
}

/**
 * @class InsertTable
 * @test Check if inserting a duplicate fails
 */
TEST_F(InsertTableTest, insertDuplicate) {
    void* actualData = nullptr;
    EXPECT_TRUE(mTable.insert(10u, &mElement1, &actualData));
    EXPECT_EQ(nullptr, actualData);

    EXPECT_FALSE(mTable.insert(10u, &mElement2, &actualData));
    EXPECT_EQ(&mElement1, actualData);

    EXPECT_EQ(&mElement1, mTable.get(10u));
}

/**
 * @class InsertTable
 * @test Check if removing an element works correctly
 */
TEST_F(InsertTableTest, remove) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));
    EXPECT_EQ(&mElement1, mTable.get(10u));

    EXPECT_TRUE(mTable.remove(10u, &mElement1));
    EXPECT_EQ(nullptr, mTable.get(10u));
}

/**
 * @class InsertTable
 * @test Check if removing a changed element is prevented
 */
TEST_F(InsertTableTest, removeChanged) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));

    void* actualData = nullptr;
    EXPECT_FALSE(mTable.remove(10u, &mElement2, &actualData));
    EXPECT_EQ(&mElement1, actualData);
    EXPECT_EQ(&mElement1, mTable.get(10u));
}

/**
 * @class InsertTable
 * @test Check if updating an element works
 */
TEST_F(InsertTableTest, update) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));

    EXPECT_TRUE(mTable.update(10u, &mElement1, &mElement2));
    EXPECT_EQ(&mElement2, mTable.get(10u));
}

/**
 * @class InsertTable
 * @test Check if updating a changed element is prevented
 */
TEST_F(InsertTableTest, updateChanged) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));

    void* actualData = nullptr;
    EXPECT_FALSE(mTable.update(10u, &mElement3, &mElement2, &actualData));
    EXPECT_EQ(&mElement1, actualData);
    EXPECT_EQ(&mElement1, mTable.get(10u));
}

}
<commit_msg>Extend testcase to cover inserts after removes<commit_after>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */

#include <deltamain/InsertHash.hpp>

#include <gtest/gtest.h>

using namespace tell::store;
using namespace tell::store::deltamain;

namespace {

class InsertTableTest : public ::testing::Test {
protected:
    InsertTableTest()
            : mTable(128),
              mElement1(0x1u), mElement2(0x2u), mElement3(0x3u) {
    }

    InsertTable mTable;

    uint64_t mElement1;
    uint64_t mElement2;
    uint64_t mElement3;
};

/**
 * @class InsertTable
 * @test Check if a simple get after insert returns the element
 */
TEST_F(InsertTableTest, insertAndGet) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));

    EXPECT_EQ(&mElement1, mTable.get(10u));
}

/**
 * @class InsertTable
 * @test Check if multiple get and inserts return the correct elements
 */
TEST_F(InsertTableTest, insertAndGetMultiple) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));
    EXPECT_TRUE(mTable.insert(11u, &mElement2));
    EXPECT_TRUE(mTable.insert(138u, &mElement3));

    EXPECT_EQ(&mElement1, mTable.get(10u));
    EXPECT_EQ(&mElement2, mTable.get(11u));
    EXPECT_EQ(&mElement3, mTable.get(138u));
}

/**
 * @class InsertTable
 * @test Check if inserting a duplicate fails
 */
TEST_F(InsertTableTest, insertDuplicate) {
    void* actualData = nullptr;
    EXPECT_TRUE(mTable.insert(10u, &mElement1, &actualData));
    EXPECT_EQ(nullptr, actualData);

    EXPECT_FALSE(mTable.insert(10u, &mElement2, &actualData));
    EXPECT_EQ(&mElement1, actualData);

    EXPECT_EQ(&mElement1, mTable.get(10u));
}

/**
 * @class InsertTable
 * @test Check if removing an element and inserting another one works correctly
 */
TEST_F(InsertTableTest, removeAndInsert) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));
    EXPECT_EQ(&mElement1, mTable.get(10u));

    EXPECT_TRUE(mTable.remove(10u, &mElement1));
    EXPECT_EQ(nullptr, mTable.get(10u));

    EXPECT_TRUE(mTable.insert(10u, &mElement2));
    EXPECT_EQ(&mElement2, mTable.get(10u));
}

/**
 * @class InsertTable
 * @test Check if removing a changed element is prevented
 */
TEST_F(InsertTableTest, removeChanged) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));

    void* actualData = nullptr;
    EXPECT_FALSE(mTable.remove(10u, &mElement2, &actualData));
    EXPECT_EQ(&mElement1, actualData);
    EXPECT_EQ(&mElement1, mTable.get(10u));
}

/**
 * @class InsertTable
 * @test Check if updating an element works
 */
TEST_F(InsertTableTest, update) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));

    EXPECT_TRUE(mTable.update(10u, &mElement1, &mElement2));
    EXPECT_EQ(&mElement2, mTable.get(10u));
}

/**
 * @class InsertTable
 * @test Check if updating a changed element is prevented
 */
TEST_F(InsertTableTest, updateChanged) {
    EXPECT_TRUE(mTable.insert(10u, &mElement1));

    void* actualData = nullptr;
    EXPECT_FALSE(mTable.update(10u, &mElement3, &mElement2, &actualData));
    EXPECT_EQ(&mElement1, actualData);
    EXPECT_EQ(&mElement1, mTable.get(10u));
}

}
<|endoftext|>


<commit_before>//
// XXX
//  - workers should have a uwq
//  - pin workers

#include "types.h"
#include "amd64.h"
#include "kernel.hh"
#include "cpu.hh"
#include "gc.hh"
#include "percpu.hh"
#include "spinlock.h"
#include "condvar.h"
#include "proc.hh"
#include "uwq.hh"
#include "vm.hh"
#include "kalloc.hh"
#include "bits.hh"
extern "C" {
#include "kern_c.h"
}

int
uwq_trywork(void)
{
  u64 i, k;

  // A "random" victim CPU
  k = rdtsc();
  for (i = 0; i < NCPU; i++) {
    u64 j = (i+k) % NCPU;

    if (j == mycpuid())
      continue;
    struct cpu *c = &cpus[j];

    scoped_gc_epoch xgc();
    barrier();
    struct proc *p = c->proc;
    if (p == nullptr || p->uwq == nullptr)
      continue;
    uwq* uwq = p->uwq;

    if (uwq->haswork()) {
      if (uwq->trywork())
        return 1;
      // XXX(sbw) start a worker thread..
      break;
    }
  }

  return 0;
}

long
sys_wqwait(void)
{
  uwq_worker* w = myproc()->worker;
  if (w == nullptr)
    return -1;

  return w->wait();
}

//
// uwq_worker
//
long
uwq_worker::wait(void)
{
  acquire(&lock);
  uwq->tryexit(this);
  cv_sleep(&cv, &lock);
  uwq->tryexit(this);
  release(&lock);
  return 0;
}

//
// uwq
//
uwq*
uwq::alloc(vmap* vmap, filetable *ftable)
{
  padded_length* len;
  uwq* u;

  len = (padded_length*) ksalloc(slab_userwq);
  if (len == nullptr)
    return nullptr;

  ftable->incref();
  vmap->incref();

  u = new uwq(vmap, ftable, len);
  if (u == nullptr) {
    ftable->decref();
    vmap->decref();
    ksfree(slab_userwq, len);
    return nullptr;
  }
  u->inc();

  if (mapkva(vmap->pml4, (char*)len, USERWQ, USERWQSIZE)) {
    ftable->decref();
    vmap->decref();
    ksfree(slab_userwq, len);
    u->dec();
    return nullptr;
  }

  return u;
}

uwq::uwq(vmap* vmap, filetable *ftable, padded_length *len)
  : rcu_freed("uwq"),
    vmap_(vmap), ftable_(ftable), len_(len),
    uentry_(0), ustack_(UWQSTACK), uref_(0)
{
  for (int i = 0; i < NCPU; i++)
    len_[i].v_ = 0;

  initlock(&lock_, "uwq_lock", 0);

  // XXX(sbw) move to uwq_worker constructor
  for (int i = 0; i < NCPU; i++) {
    initlock(&worker_[i].lock, "worker_lock", 0);
    initcondvar(&worker_[i].cv, "worker_cv");
    worker_[i].uwq = this;
    worker_[i].running = false;
    worker_[i].proc = nullptr;
  }
}

uwq::~uwq(void)
{
  if (len_ != nullptr)
    ksfree(slab_userwq, len_);
  vmap_->decref();
  ftable_->decref();
}

void
uwq::tryexit(uwq_worker *w)
{
  if (ref() == 0) {
    if (--uref_ == 0)
      gc_delayed(this);
    release(&w->lock);
    w->proc = nullptr;
    exit();
  }
}

bool
uwq::haswork(void)
{
  if (len_ == nullptr)
    return false;

  for (int i = 0; i < NCPU; i++) {
    if (len_[i].v_ > 0) {
      return true;
    }
  }
  return false;
}

bool
uwq::trywork(void)
{
  scoped_acquire lock0(&lock_);

  if (ref() == 0)
    return false;

  int slot = -1;
  for (int i = 0; i < NCPU; i++) {
    if (worker_[i].running)
      continue;
    else if (worker_[i].proc != nullptr) {
      scoped_acquire lock1(&worker_[i].lock);
      proc* p = worker_[i].proc;

      panic("uwq::trywork: untested");
      acquire(&p->lock);
      p->cpu_pin = 1;
      p->cpuid = mycpuid();
      release(&p->lock);

      worker_[i].running = true;
      cv_wakeup(&worker_[i].cv);
      return true;
    } else if (slot == -1) {
      slot = i;
    }
  }

  if (slot != -1) {
    proc* p = allocworker();
    if (p != nullptr) {
      ++uref_;
      p->worker = &worker_[slot];
      worker_[slot].proc = p;
      worker_[slot].running = true;

      acquire(&p->lock);
      p->cpu_pin = 1;
      p->cpuid = mycpuid();
      addrun(p);
      release(&p->lock);

      return true;
    }
  }

  return nullptr;
}

void
uwq::finish(void)
{
  bool gcnow = true;

  scoped_acquire lock0(&lock_);
  for (int i = 0; i < NCPU; i++) {
    if (worker_[i].proc != nullptr) {
      gcnow = false;
      acquire(&worker_[i].lock);
      cv_wakeup(&worker_[i].cv);
      release(&worker_[i].lock);
    }
  }

  if (gcnow)
    gc_delayed(this);
}

void
uwq::onzero() const
{
  uwq *u = (uwq*)this;
  u->finish();
}

void*
uwq::buffer(void)
{
  return (void*)len_;
}

void
uwq::setuentry(uptr uentry)
{
  uentry_ = uentry;
}

proc*
uwq::allocworker(void)
{
  uptr uentry = uentry_;

  if (uentry == 0)
    return nullptr;

  proc* p = allocproc();
  if (p == nullptr)
    return nullptr;
  safestrcpy(p->name, "uwq_worker", sizeof(p->name));

  // finishproc will drop these refs
  vmap_->incref();
  ftable_->incref();

  p->vmap = vmap_;
  p->ftable = ftable_;

  struct vmnode *vmn;
  if ((vmn = new vmnode(UWQSTACKPAGES)) == nullptr) {
    finishproc(p);
    return nullptr;
  }

  uptr stacktop = ustack_ + (UWQSTACKPAGES*PGSIZE);
  if (vmap_->insert(vmn, ustack_, 1) < 0) {
    delete vmn;
    finishproc(p);
    return nullptr;
  }
  ustack_ += (UWQSTACKPAGES*PGSIZE);

  p->tf->rsp = stacktop - 8;
  p->tf->rip = uentry;
  p->tf->cs = UCSEG | 0x3;
  p->tf->ds = UDSEG | 0x3;
  p->tf->ss = p->tf->ds;
  p->tf->rflags = FL_IF;

  return p;
}
<commit_msg>Don't pin workers<commit_after>//
// XXX
//  - workers should have a uwq
//  - pin workers

#include "types.h"
#include "amd64.h"
#include "kernel.hh"
#include "cpu.hh"
#include "gc.hh"
#include "percpu.hh"
#include "spinlock.h"
#include "condvar.h"
#include "proc.hh"
#include "uwq.hh"
#include "vm.hh"
#include "kalloc.hh"
#include "bits.hh"
extern "C" {
#include "kern_c.h"
}

int
uwq_trywork(void)
{
  u64 i, k;

  // A "random" victim CPU
  k = rdtsc();
  for (i = 0; i < NCPU; i++) {
    u64 j = (i+k) % NCPU;

    if (j == mycpuid())
      continue;
    struct cpu *c = &cpus[j];

    scoped_gc_epoch xgc();
    barrier();
    struct proc *p = c->proc;
    if (p == nullptr || p->uwq == nullptr)
      continue;
    uwq* uwq = p->uwq;

    if (uwq->haswork()) {
      if (uwq->trywork())
        return 1;
      // XXX(sbw) start a worker thread..
      break;
    }
  }

  return 0;
}

long
sys_wqwait(void)
{
  uwq_worker* w = myproc()->worker;
  if (w == nullptr)
    return -1;

  return w->wait();
}

//
// uwq_worker
//
long
uwq_worker::wait(void)
{
  acquire(&lock);
  uwq->tryexit(this);
  cv_sleep(&cv, &lock);
  uwq->tryexit(this);
  release(&lock);
  return 0;
}

//
// uwq
//
uwq*
uwq::alloc(vmap* vmap, filetable *ftable)
{
  padded_length* len;
  uwq* u;

  len = (padded_length*) ksalloc(slab_userwq);
  if (len == nullptr)
    return nullptr;

  ftable->incref();
  vmap->incref();

  u = new uwq(vmap, ftable, len);
  if (u == nullptr) {
    ftable->decref();
    vmap->decref();
    ksfree(slab_userwq, len);
    return nullptr;
  }
  u->inc();

  if (mapkva(vmap->pml4, (char*)len, USERWQ, USERWQSIZE)) {
    ftable->decref();
    vmap->decref();
    ksfree(slab_userwq, len);
    u->dec();
    return nullptr;
  }

  return u;
}

uwq::uwq(vmap* vmap, filetable *ftable, padded_length *len)
  : rcu_freed("uwq"),
    vmap_(vmap), ftable_(ftable), len_(len),
    uentry_(0), ustack_(UWQSTACK), uref_(0)
{
  for (int i = 0; i < NCPU; i++)
    len_[i].v_ = 0;

  initlock(&lock_, "uwq_lock", 0);

  // XXX(sbw) move to uwq_worker constructor
  for (int i = 0; i < NCPU; i++) {
    initlock(&worker_[i].lock, "worker_lock", 0);
    initcondvar(&worker_[i].cv, "worker_cv");
    worker_[i].uwq = this;
    worker_[i].running = false;
    worker_[i].proc = nullptr;
  }
}

uwq::~uwq(void)
{
  if (len_ != nullptr)
    ksfree(slab_userwq, len_);
  vmap_->decref();
  ftable_->decref();
}

void
uwq::tryexit(uwq_worker *w)
{
  if (ref() == 0) {
    if (--uref_ == 0)
      gc_delayed(this);
    release(&w->lock);
    w->proc = nullptr;
    exit();
  }
}

bool
uwq::haswork(void)
{
  if (len_ == nullptr)
    return false;

  for (int i = 0; i < NCPU; i++) {
    if (len_[i].v_ > 0) {
      return true;
    }
  }
  return false;
}

bool
uwq::trywork(void)
{
  scoped_acquire lock0(&lock_);

  if (ref() == 0)
    return false;

  int slot = -1;
  for (int i = 0; i < NCPU; i++) {
    if (worker_[i].running)
      continue;
    else if (worker_[i].proc != nullptr) {
      scoped_acquire lock1(&worker_[i].lock);
      proc* p = worker_[i].proc;

      panic("uwq::trywork: untested");
      acquire(&p->lock);
      p->cpuid = mycpuid();
      release(&p->lock);

      worker_[i].running = true;
      cv_wakeup(&worker_[i].cv);
      return true;
    } else if (slot == -1) {
      slot = i;
    }
  }

  if (slot != -1) {
    proc* p = allocworker();
    if (p != nullptr) {
      ++uref_;
      p->worker = &worker_[slot];
      worker_[slot].proc = p;
      worker_[slot].running = true;

      acquire(&p->lock);
      p->cpuid = mycpuid();
      addrun(p);
      release(&p->lock);

      return true;
    }
  }

  return nullptr;
}

void
uwq::finish(void)
{
  bool gcnow = true;

  scoped_acquire lock0(&lock_);
  for (int i = 0; i < NCPU; i++) {
    if (worker_[i].proc != nullptr) {
      gcnow = false;
      acquire(&worker_[i].lock);
      cv_wakeup(&worker_[i].cv);
      release(&worker_[i].lock);
    }
  }

  if (gcnow)
    gc_delayed(this);
}

void
uwq::onzero() const
{
  uwq *u = (uwq*)this;
  u->finish();
}

void*
uwq::buffer(void)
{
  return (void*)len_;
}

void
uwq::setuentry(uptr uentry)
{
  uentry_ = uentry;
}

proc*
uwq::allocworker(void)
{
  uptr uentry = uentry_;

  if (uentry == 0)
    return nullptr;

  proc* p = allocproc();
  if (p == nullptr)
    return nullptr;
  safestrcpy(p->name, "uwq_worker", sizeof(p->name));

  // finishproc will drop these refs
  vmap_->incref();
  ftable_->incref();

  p->vmap = vmap_;
  p->ftable = ftable_;

  struct vmnode *vmn;
  if ((vmn = new vmnode(UWQSTACKPAGES)) == nullptr) {
    finishproc(p);
    return nullptr;
  }

  uptr stacktop = ustack_ + (UWQSTACKPAGES*PGSIZE);
  if (vmap_->insert(vmn, ustack_, 1) < 0) {
    delete vmn;
    finishproc(p);
    return nullptr;
  }
  ustack_ += (UWQSTACKPAGES*PGSIZE);

  p->tf->rsp = stacktop - 8;
  p->tf->rip = uentry;
  p->tf->cs = UCSEG | 0x3;
  p->tf->ds = UDSEG | 0x3;
  p->tf->ss = p->tf->ds;
  p->tf->rflags = FL_IF;

  return p;
}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
//
// BlockchainDownload.cpp
//
// Copyright (c) 2014 Eric Lombrozo
//
// All Rights Reserved.

#include "BlockchainDownload.h"

#include <logger/logger.h>

#include <thread>
#include <chrono>

using namespace CoinQ::Network;
using namespace std;

BlockchainDownload::BlockchainDownload(const CoinQ::CoinParams& coinParams, bool bCheckProofOfWork) :
    m_coinParams(coinParams),
    m_bCheckProofOfWork(bCheckProofOfWork),
    m_bStarted(false),
    m_bIOServiceStarted(false),
    m_work(m_ioService),
    m_bConnected(false),
    m_peer(m_ioService)
{
    // Select hash functions
    Coin::CoinBlockHeader::setHashFunc(m_coinParams.block_header_hash_function());
    Coin::CoinBlockHeader::setPOWHashFunc(m_coinParams.block_header_pow_hash_function());

    // Subscribe peer handlers
    m_peer.subscribeOpen([&](CoinQ::Peer& /*peer*/)
    {
        LOGGER(trace) << "BlockchainDownload - Peer connection opened." << endl;
        m_bConnected = true;
        notifyOpen();
        try
        {
            if (m_blockTree.isEmpty())
            {
                m_blockTree.setGenesisBlock(m_coinParams.genesis_block());
                m_peer.getBlocks(m_locatorHashes, m_hashStop);
            }
            else
            {
                m_peer.getBlocks(m_blockTree.getLocatorHashes(-1));
            }
        }
        catch (const std::exception& e)
        {
            LOGGER(error) << "BlockchainDownload - Peer::getBlocks error - " << e.what();
            // TODO: propagate code
            notifyBlockTreeError(e.what(), -1);
        }
    });

    m_peer.subscribeClose([this](CoinQ::Peer& /*peer*/)
    {
        if (m_bConnected) { stop(); }
        notifyClose();
    });

    m_peer.subscribeTimeout([&](CoinQ::Peer& /*peer*/)
    {
        notifyTimeout();
    });

    m_peer.subscribeConnectionError([&](CoinQ::Peer& /*peer*/, const std::string& error, int code)
    {
        notifyConnectionError(error, code);
    });

    m_peer.subscribeProtocolError([&](CoinQ::Peer& /*peer*/, const std::string& error, int code)
    {
        notifyProtocolError(error, code);
    });

    m_peer.subscribeInv([&](CoinQ::Peer& peer, const Coin::Inventory& inv)
    {
        if (!m_bConnected) return;
        LOGGER(trace) << "Received inventory message:" << std::endl << inv.toIndentedString(2) << std::endl;

        using namespace Coin;
        GetDataMessage getData;
        for (auto& item: inv.items)
        {
            switch (item.itemType)
            {
            case MSG_BLOCK:
                getData.items.push_back(InventoryItem(MSG_BLOCK, item.hash));
                break;
            default:
                break;
            }
        }

        if (!getData.items.empty())
        {
            try
            {
                const unsigned char* hash = getData.items[getData.items.size() - 1].hash;
                m_lastRequestedBlockHash = bytes_t(hash, hash + 32);
                m_peer.send(getData);
            }
            catch (const exception& e)
            {
                LOGGER(error) << "BlockchainDownload - Peer::send() failed: " << e.what() << endl;
                m_lastRequestedBlockHash.clear();
                notifyConnectionError(e.what(), -1);
            }
        }
    });

    m_peer.subscribeBlock([&](CoinQ::Peer& /*peer*/, const Coin::CoinBlock& block)
    {
        m_lastReceivedBlockHash = block.hash();
        LOGGER(trace) << "BlockchainDownload - Received block: " << m_lastReceivedBlockHash.getHex() << endl;

        try
        {
            m_blockTree.insertHeader(block.blockHeader);
        }
        catch (const exception& e)
        {
            LOGGER(error) << "BlockchainDownload - Failed to insert header: " << e.what() << endl;
            notifyBlockTreeError(e.what(), -1);
        }

        notifyBlock(block);

        try
        {
            m_peer.getBlocks(m_blockTree.getLocatorHashes(-1));
        }
        catch (const exception& e)
        {
            LOGGER(error) << "BlockchainDownload - Failed to request blocks: " << e.what() << endl;
            notifyConnectionError(e.what(), -1);
        }
    });
}

BlockchainDownload::~BlockchainDownload()
{
    stop();
}

void BlockchainDownload::start(const string& host, const string& port, const vector<uchar_vector>& locatorHashes, const uchar_vector& hashStop)
{
    {
        if (m_bStarted) throw runtime_error("BlockchainDownload - already started.");
        boost::lock_guard<boost::mutex> lock(m_startMutex);
        if (m_bStarted) throw runtime_error("BlockchainDownload - already started.");

        LOGGER(trace) << "BlockchainDownload::start(" << host << ", " << port << ")" << std::endl;

        startIOServiceThread();
        m_bStarted = true;

        string port_ = port.empty() ? m_coinParams.default_port() : port;
        m_peer.set(host, port_, m_coinParams.magic_bytes(), m_coinParams.protocol_version(), "Wallet v0.1", 0, false);

        if (locatorHashes.empty())
        {
            m_locatorHashes.clear();
            m_locatorHashes.push_back(uchar_vector(32, 0));
        }
        else
        {
            m_locatorHashes = locatorHashes;
        }

        m_hashStop = hashStop;

        LOGGER(trace) << "Starting peer " << host << ":" << port_ << "..." << endl;
        m_peer.start();
        LOGGER(trace) << "Peer started." << endl;
    }

    notifyStarted();
}

void BlockchainDownload::start(const string& host, int port, const vector<uchar_vector>& locatorHashes, const uchar_vector& hashStop)
{
    std::stringstream ssport;
    ssport << port;
    start(host, ssport.str(), locatorHashes, hashStop);
}

void BlockchainDownload::stop()
{
    {
        if (!m_bStarted) return;
        boost::lock_guard<boost::mutex> lock(m_startMutex);
        if (!m_bStarted) return;

        m_bConnected = false;
        m_peer.stop();
        stopIOServiceThread();

        m_bStarted = false;
    }

    notifyStopped();
}

void BlockchainDownload::startIOServiceThread()
{
    if (m_bIOServiceStarted) throw std::runtime_error("BlockchainDownload - io service already started.");
    boost::lock_guard<boost::mutex> lock(m_ioServiceMutex);
    if (m_bIOServiceStarted) throw std::runtime_error("BlockchainDownload - io service already started.");

    LOGGER(trace) << "Starting IO service thread..." << endl;
    m_bIOServiceStarted = true;
    m_ioServiceThread = boost::thread(boost::bind(&CoinQ::io_service_t::run, &m_ioService));
    LOGGER(trace) << "IO service thread started." << endl;
}

void BlockchainDownload::stopIOServiceThread()
{
    if (!m_bIOServiceStarted) return;
    boost::lock_guard<boost::mutex> lock(m_ioServiceMutex);
    if (!m_bIOServiceStarted) return;

    LOGGER(trace) << "Stopping IO service thread..." << endl;
    m_ioService.stop();
    m_ioServiceThread.join();
    m_ioService.reset();
    m_bIOServiceStarted = false;
    LOGGER(trace) << "IO service thread stopped." << endl;
}

<commit_msg>Use the first requested block as the genesis block for now. TODO: Split off locator hash functionality from block tree class.<commit_after>///////////////////////////////////////////////////////////////////////////////
//
// BlockchainDownload.cpp
//
// Copyright (c) 2014 Eric Lombrozo
//
// All Rights Reserved.

#include "BlockchainDownload.h"

#include <logger/logger.h>

#include <thread>
#include <chrono>

using namespace CoinQ::Network;
using namespace std;

BlockchainDownload::BlockchainDownload(const CoinQ::CoinParams& coinParams, bool bCheckProofOfWork) :
    m_coinParams(coinParams),
    m_bCheckProofOfWork(bCheckProofOfWork),
    m_bStarted(false),
    m_bIOServiceStarted(false),
    m_work(m_ioService),
    m_bConnected(false),
    m_peer(m_ioService)
{
    // Select hash functions
    Coin::CoinBlockHeader::setHashFunc(m_coinParams.block_header_hash_function());
    Coin::CoinBlockHeader::setPOWHashFunc(m_coinParams.block_header_pow_hash_function());

    // Subscribe peer handlers
    m_peer.subscribeOpen([&](CoinQ::Peer& /*peer*/)
    {
        LOGGER(trace) << "BlockchainDownload - Peer connection opened." << endl;
        m_bConnected = true;
        notifyOpen();
        try
        {
            if (m_blockTree.isEmpty())
            {
  //              m_blockTree.setGenesisBlock(m_coinParams.genesis_block());
                m_peer.getBlocks(m_locatorHashes, m_hashStop);
            }
            else
            {
                m_peer.getBlocks(m_blockTree.getLocatorHashes(-1));
            }
        }
        catch (const std::exception& e)
        {
            LOGGER(error) << "BlockchainDownload - Peer::getBlocks error - " << e.what();
            // TODO: propagate code
            notifyBlockTreeError(e.what(), -1);
        }
    });

    m_peer.subscribeClose([this](CoinQ::Peer& /*peer*/)
    {
        if (m_bConnected) { stop(); }
        notifyClose();
    });

    m_peer.subscribeTimeout([&](CoinQ::Peer& /*peer*/)
    {
        notifyTimeout();
    });

    m_peer.subscribeConnectionError([&](CoinQ::Peer& /*peer*/, const std::string& error, int code)
    {
        notifyConnectionError(error, code);
    });

    m_peer.subscribeProtocolError([&](CoinQ::Peer& /*peer*/, const std::string& error, int code)
    {
        notifyProtocolError(error, code);
    });

    m_peer.subscribeInv([&](CoinQ::Peer& peer, const Coin::Inventory& inv)
    {
        if (!m_bConnected) return;
        LOGGER(trace) << "Received inventory message:" << std::endl << inv.toIndentedString(2) << std::endl;

        using namespace Coin;
        GetDataMessage getData;
        for (auto& item: inv.items)
        {
            switch (item.itemType)
            {
            case MSG_BLOCK:
                getData.items.push_back(InventoryItem(MSG_BLOCK, item.hash));
                break;
            default:
                break;
            }
        }

        if (!getData.items.empty())
        {
            try
            {
                const unsigned char* hash = getData.items[getData.items.size() - 1].hash;
                m_lastRequestedBlockHash = bytes_t(hash, hash + 32);
                m_peer.send(getData);
            }
            catch (const exception& e)
            {
                LOGGER(error) << "BlockchainDownload - Peer::send() failed: " << e.what() << endl;
                m_lastRequestedBlockHash.clear();
                notifyConnectionError(e.what(), -1);
            }
        }
    });

    m_peer.subscribeBlock([&](CoinQ::Peer& /*peer*/, const Coin::CoinBlock& block)
    {
        m_lastReceivedBlockHash = block.hash();
        LOGGER(trace) << "BlockchainDownload - Received block: " << m_lastReceivedBlockHash.getHex() << endl;

        try
        {
            if (m_blockTree.isEmpty()) { m_blockTree.setGenesisBlock(block.blockHeader); }
            m_blockTree.insertHeader(block.blockHeader);
        }
        catch (const exception& e)
        {
            LOGGER(error) << "BlockchainDownload - Failed to insert header: " << e.what() << endl;
            notifyBlockTreeError(e.what(), -1);
        }

        notifyBlock(block);

        try
        {
            m_peer.getBlocks(m_blockTree.getLocatorHashes(-1));
        }
        catch (const exception& e)
        {
            LOGGER(error) << "BlockchainDownload - Failed to request blocks: " << e.what() << endl;
            notifyConnectionError(e.what(), -1);
        }
    });
}

BlockchainDownload::~BlockchainDownload()
{
    stop();
}

void BlockchainDownload::start(const string& host, const string& port, const vector<uchar_vector>& locatorHashes, const uchar_vector& hashStop)
{
    {
        if (m_bStarted) throw runtime_error("BlockchainDownload - already started.");
        boost::lock_guard<boost::mutex> lock(m_startMutex);
        if (m_bStarted) throw runtime_error("BlockchainDownload - already started.");

        LOGGER(trace) << "BlockchainDownload::start(" << host << ", " << port << ")" << std::endl;

        startIOServiceThread();
        m_bStarted = true;

        string port_ = port.empty() ? m_coinParams.default_port() : port;
        m_peer.set(host, port_, m_coinParams.magic_bytes(), m_coinParams.protocol_version(), "Wallet v0.1", 0, false);

        if (locatorHashes.empty())
        {
            m_locatorHashes.clear();
            m_locatorHashes.push_back(uchar_vector(32, 0));
        }
        else
        {
            m_locatorHashes = locatorHashes;
        }

        m_hashStop = hashStop;

        LOGGER(trace) << "Starting peer " << host << ":" << port_ << "..." << endl;
        m_peer.start();
        LOGGER(trace) << "Peer started." << endl;
    }

    notifyStarted();
}

void BlockchainDownload::start(const string& host, int port, const vector<uchar_vector>& locatorHashes, const uchar_vector& hashStop)
{
    std::stringstream ssport;
    ssport << port;
    start(host, ssport.str(), locatorHashes, hashStop);
}

void BlockchainDownload::stop()
{
    {
        if (!m_bStarted) return;
        boost::lock_guard<boost::mutex> lock(m_startMutex);
        if (!m_bStarted) return;

        m_bConnected = false;
        m_peer.stop();
        stopIOServiceThread();

        m_bStarted = false;
    }

    notifyStopped();
}

void BlockchainDownload::startIOServiceThread()
{
    if (m_bIOServiceStarted) throw std::runtime_error("BlockchainDownload - io service already started.");
    boost::lock_guard<boost::mutex> lock(m_ioServiceMutex);
    if (m_bIOServiceStarted) throw std::runtime_error("BlockchainDownload - io service already started.");

    LOGGER(trace) << "Starting IO service thread..." << endl;
    m_bIOServiceStarted = true;
    m_ioServiceThread = boost::thread(boost::bind(&CoinQ::io_service_t::run, &m_ioService));
    LOGGER(trace) << "IO service thread started." << endl;
}

void BlockchainDownload::stopIOServiceThread()
{
    if (!m_bIOServiceStarted) return;
    boost::lock_guard<boost::mutex> lock(m_ioServiceMutex);
    if (!m_bIOServiceStarted) return;

    LOGGER(trace) << "Stopping IO service thread..." << endl;
    m_ioService.stop();
    m_ioServiceThread.join();
    m_ioService.reset();
    m_bIOServiceStarted = false;
    LOGGER(trace) << "IO service thread stopped." << endl;
}

<|endoftext|>


<commit_before>#include "viewer.h"

#include <cstdlib>
#include <ctime>

#include <GL/gl.h>
#include <SDL.h>

#include "base/Log.h"
#include "base/Guard.h"

namespace viewer {

namespace {
const float MINNOTE = 100;
const float MAXNOTE = 300;
}

int Viewer::run() {
  typedef base::PtrGuard<SDL_Surface, SDL_FreeSurface> SurfaceGuard;
  typedef base::RunGuard<SDL_Quit> SDLQuitGuard;

  if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) != 0) {
    VIEWER_LOG_ERROR << SDL_GetError() << VIEWER_LOG_END;
    return 1;
  }
  SDLQuitGuard sdlQuitGuard;

  SDL_WM_SetCaption("viewer", "viewer");

  SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

  SurfaceGuard screen(SDL_SetVideoMode(1024, 600, 24, SDL_OPENGL));
  if (!screen) {
    VIEWER_LOG_ERROR << SDL_GetError() << VIEWER_LOG_END;
    return 1;
  }

#if 0
  SDL_WM_ToggleFullScreen(screen.get());
#endif

  try {
    m_driver = new video::Driver(screen->w, screen->h);
  } catch (const video::TextException& e) {
    VIEWER_LOG_ERROR << e.what() << VIEWER_LOG_END;
    return 1;
  }

  m_width = screen->w;
  m_height = screen->h;

  // Seed random number generator
  srand(time(NULL));

  Uint32 last_time;
  Uint32 fps = 120;

  // Initialize time difference counter
  last_time = SDL_GetTicks();

  // Main loop
  for (;;) {
    Uint32 this_time = SDL_GetTicks();

    SDL_Event event;
    while (SDL_PollEvent(&event)) {
      switch (event.type) {
      case SDL_QUIT:
	goto end;
	break;
      case SDL_MOUSEBUTTONDOWN: {
	double ypos = m_height - event.button.y;
        double note = ypos / (m_height / (MAXNOTE - MINNOTE)) + MINNOTE;
        std::cerr << "note = " << note << std::endl;
      } break;
      default:
	break;
      }
    }

    try {
      // TODO Fix wraparound for time difference
      tick(this_time - last_time);
    } catch (const std::exception& e) {
      VIEWER_LOG_ERROR << e.what() << VIEWER_LOG_END;
      return 1;
    }

    m_driver->beginScene();
    draw();
    m_driver->endScene();

    last_time = this_time;

    Sint32 sleep_time = (1000 / fps) - (SDL_GetTicks() - last_time);
    if (sleep_time > 0) {
      SDL_Delay(sleep_time);
    }
  }

 end:
  return 0;
}

void Viewer::draw()
{
  std::lock_guard<std::mutex> lockGuard(mutex());

  const double noteWidth = 2;
  // const double noteHeight = 20;

  double position = 0;
  for (auto note : cb()) {
    if (note.f0 != 0) {  // TODO float compare

      double ypos = (note.f0 - MINNOTE) * (m_height / (MAXNOTE - MINNOTE));

      m_driver->draw2DRectangle(position, ypos - 1, position + noteWidth,
                                ypos + 1, video::Color(255, 255, 255, 255));

      ypos = note.rms * (m_height / 3 / 24000.0) + (2.0 * m_height / 3);

      m_driver->draw2DRectangle(position, ypos - 1, position + noteWidth,
                                ypos + 1, video::Color(0, 100, 100, 255));
    }

    position += noteWidth;
  }

  // G3
  {
    double ypos = (196 - MINNOTE) * (m_height / (MAXNOTE - MINNOTE));
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(255, 0, 0, 255));
  }

  // A3 - reach
  {
    double ypos = (220 - MINNOTE) * (m_height / (MAXNOTE - MINNOTE));
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // C4 - wow!
  {
    double ypos = (262 - MINNOTE) * (m_height / (MAXNOTE - MINNOTE));
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // C3 - don't go here
  {
    double ypos = (131 - MINNOTE) * (m_height / (MAXNOTE - MINNOTE));
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

}
}
<commit_msg>noteToPos<commit_after>#include "viewer.h"

#include <cstdlib>
#include <ctime>

#include <GL/gl.h>
#include <SDL.h>

#include "base/Log.h"
#include "base/Guard.h"

namespace viewer {

namespace {
const float MINNOTE = 100;
const float MAXNOTE = 300;
}

int Viewer::run() {
  typedef base::PtrGuard<SDL_Surface, SDL_FreeSurface> SurfaceGuard;
  typedef base::RunGuard<SDL_Quit> SDLQuitGuard;

  if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) != 0) {
    VIEWER_LOG_ERROR << SDL_GetError() << VIEWER_LOG_END;
    return 1;
  }
  SDLQuitGuard sdlQuitGuard;

  SDL_WM_SetCaption("viewer", "viewer");

  SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

  SurfaceGuard screen(SDL_SetVideoMode(1024, 600, 24, SDL_OPENGL));
  if (!screen) {
    VIEWER_LOG_ERROR << SDL_GetError() << VIEWER_LOG_END;
    return 1;
  }

#if 0
  SDL_WM_ToggleFullScreen(screen.get());
#endif

  try {
    m_driver = new video::Driver(screen->w, screen->h);
  } catch (const video::TextException& e) {
    VIEWER_LOG_ERROR << e.what() << VIEWER_LOG_END;
    return 1;
  }

  m_width = screen->w;
  m_height = screen->h;

  // Seed random number generator
  srand(time(NULL));

  Uint32 last_time;
  Uint32 fps = 120;

  // Initialize time difference counter
  last_time = SDL_GetTicks();

  // Main loop
  for (;;) {
    Uint32 this_time = SDL_GetTicks();

    SDL_Event event;
    while (SDL_PollEvent(&event)) {
      switch (event.type) {
      case SDL_QUIT:
	goto end;
	break;
      case SDL_MOUSEBUTTONDOWN: {
	double ypos = m_height - event.button.y;
        double note = ypos / (m_height / (MAXNOTE - MINNOTE)) + MINNOTE;
        std::cerr << "note = " << note << std::endl;
      } break;
      default:
	break;
      }
    }

    try {
      // TODO Fix wraparound for time difference
      tick(this_time - last_time);
    } catch (const std::exception& e) {
      VIEWER_LOG_ERROR << e.what() << VIEWER_LOG_END;
      return 1;
    }

    m_driver->beginScene();
    draw();
    m_driver->endScene();

    last_time = this_time;

    Sint32 sleep_time = (1000 / fps) - (SDL_GetTicks() - last_time);
    if (sleep_time > 0) {
      SDL_Delay(sleep_time);
    }
  }

 end:
  return 0;
}

void Viewer::draw()
{
  std::lock_guard<std::mutex> lockGuard(mutex());

  auto noteToPos = [this](double note) {
    return (note - MINNOTE) * (m_height / (MAXNOTE - MINNOTE));
  };

  const double noteWidth = 2;
  // const double noteHeight = 20;

  double position = 0;
  for (auto note : cb()) {
    if (note.f0 != 0) {  // TODO float compare

      double ypos = noteToPos(note.f0);

      m_driver->draw2DRectangle(position, ypos - 1, position + noteWidth,
                                ypos + 1, video::Color(255, 255, 255, 255));

      ypos = note.rms * (m_height / 3 / 24000.0) + (2.0 * m_height / 3);

      m_driver->draw2DRectangle(position, ypos - 1, position + noteWidth,
                                ypos + 1, video::Color(0, 100, 100, 255));
    }

    position += noteWidth;
  }

  // G3
  {
    double ypos = noteToPos(196);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(255, 0, 0, 255));
  }

  // A3 - reach
  {
    double ypos = noteToPos(220);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // C4 - wow!
  {
    double ypos = noteToPos(262);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // C3 - don't go here
  {
    double ypos = noteToPos(131);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

}
}
<|endoftext|>


<commit_before>#include "StdAfx.h"
#include "pe_hash_database.h"

bool pe_hash_database::_is_mz(FILE* stream)
{
	char twochars[2];
	if( (fread( &twochars, 1, 2, stream) == 2) && twochars[0] == 'M' && twochars[1] == 'Z' )
	{
		fseek( stream, 0, SEEK_SET);
		return true;
	}
	fseek( stream, 0, SEEK_SET);
	return false;
}

pe_hash_database::pe_hash_database(char* clean_database_name)
{
	InitializeCriticalSectionAndSpinCount(&_lock, 0x00000400);
	EnterCriticalSection( &_lock );

	// Build the full database name
	_clean_database_path = new char[ strlen(clean_database_name) + 1 ];
	strcpy( _clean_database_path, clean_database_name );

	_clean_hashes.rehash(0x10000);

	// Open and read in the database of clean hashes if it exists.
	printf("Loading clean hash database from '%s'.\r\n", clean_database_name);
	FILE* fh = fopen( _clean_database_path, "rb" );
	unsigned __int64 hash;
	if( fh )
	{
		// Read in the database
		while( !feof( fh ) )
		{
			 if( fread( &hash, sizeof(unsigned __int64), 1, fh ) == 1 )
			 {
				_clean_hashes.insert( hash );
			 }
		}
		fclose(fh);

		printf("Loaded %i clean hashes from database.\r\n", _clean_hashes.size());
	}
	else
	{
		if( PathFileExistsA( _clean_database_path ) )
		{
			// Do not continue if the database exists, but we failed to open it. This is to protect
			// the contents of the database from being overwritten if we save the now empty database
			// successfully.
			PrintLastError(L"Failed to open existing hash database. Terminating.");
			exit(-1);
		}

		printf("Did not find an existing clean hash database, using an empty one.\r\n");
	}

	LeaveCriticalSection( &_lock );
}

int pe_hash_database::count()
{
	EnterCriticalSection( &_lock );
	int result = _clean_hashes.size();
	LeaveCriticalSection( &_lock );
	return result;
}

bool pe_hash_database::clear_database()
{
	EnterCriticalSection( &_lock );
	_clean_hashes.clear();
	LeaveCriticalSection( &_lock );
	return true;
}

bool pe_hash_database::add_hashes(unordered_set<unsigned __int64> hashes)
{
	EnterCriticalSection( &_lock );

	for (unordered_set<unsigned __int64>::iterator it=hashes.begin(); it!=hashes.end(); it++)
	{
		if( *it != 0 && _clean_hashes.count( *it ) == 0 )
		{
			_clean_hashes.insert( *it );
		}
	}

	LeaveCriticalSection( &_lock );

	return true;
}

bool pe_hash_database::add_folder( char* dir_name, WCHAR* filter, bool recursively )
{
	// Expand the environment names in the directory
	char dir_name_expanded[PATH_MAX + 1];
	if (ExpandEnvironmentStringsA(dir_name, dir_name_expanded, PATH_MAX) < PATH_MAX)
	{
		DIR *dir;
		struct dirent *ent;
		dir = opendir(dir_name_expanded);
		if (dir != NULL)
		{
			/* print all the files and directories within directory */
			while ((ent = readdir(dir)) != NULL) {
				// Convert the path to wchar format
				wchar_t* result = new wchar_t[strlen(ent->d_name) + 1];

				if (result != NULL)
				{
					for (int i = 0; i < strlen(ent->d_name); i++)
						result[i] = ent->d_name[i];
					result[strlen(ent->d_name)] = 0;

					if ((ent->d_type & DT_DIR))
					{
						// Process this subdirectory if recursive flag is on
						if (recursively && wcscmp(result, L".") != 0 && wcscmp(result, L"..") != 0)
						{
							// Build the directory path
							char* directory = new char[strlen(dir_name_expanded) + strlen(ent->d_name) + 2];
							sprintf_s(directory, strlen(dir_name_expanded) + strlen(ent->d_name) + 2, "%s\\%s", dir_name_expanded, ent->d_name);

							add_folder(directory, filter, recursively);

							// Cleanup
							delete[] directory;
						}
					}
					else {
						// Check if this filename is a match to the specified pattern
						if (PathMatchSpec(result, filter))
						{
							// Process this file
							int length = wcslen(result) + strlen(dir_name_expanded) + 1;
							char* filename = new char[length + 1];
							filename[length] = 0;
							sprintf(filename, "%s\\%S", dir_name_expanded, result);

							// Processes the specified file
							FILE* fh = fopen(filename, "rb");
							if (fh != NULL)
							{
								if (_is_mz(fh))
								{
									fclose(fh);

									add_file(filename);
								}
								else
									fclose(fh);
							}
							else {
								// Error
								fprintf(stderr, "Error opening file %s: %s.\r\n", filename, strerror(errno));
							}
							delete[] filename;
						}
					}
					delete[] result;
				}
				else
				{
					fprintf(stderr, "Failed to allocate memory block of size %i for filename: %s.\r\n", ent->d_namlen + 1, strerror(errno));
				}
			}
			closedir(dir);
			return true;
		}
		else {
			fprintf(stderr, "Unable to open directory %s: %s.\r\n", dir_name_expanded, strerror(errno));
		}
	}
	return false;
}

bool pe_hash_database::remove_folder( char* dir_name, WCHAR* filter, bool recursively )
{
	// Expand the environment names in the directory
	char* dir_name_expanded = new char[1000];
	ExpandEnvironmentStringsA( dir_name, dir_name_expanded, 1000 );

	DIR *dir;
	struct dirent *ent;
	dir = opendir (dir_name_expanded);
	if (dir != NULL)
	{
		/* print all the files and directories within directory */
		while ((ent = readdir (dir)) != NULL) {
			// Convert the path to wchar format
			wchar_t* result = new wchar_t[ent->d_namlen + 1];

			if( result != NULL )
			{
				for( int i = 0; i < ent->d_namlen; i++ )
					result[i] = ent->d_name[i];
				result[ent->d_namlen] = 0;

				if( (ent->d_type & DT_DIR) )
				{
					// Process this subdirectory if recursive flag is on
					if( recursively && wcscmp(result, L".") != 0 && wcscmp(result, L"..") != 0  )
					{
						// Build the directory path
						char* directory = new char[strlen(dir_name_expanded) + strlen(ent->d_name) + 2];
						sprintf(directory, "%s/%s", dir_name_expanded, ent->d_name);

						remove_folder( directory, filter, recursively );

						// Cleanup
						delete[] directory;
					}
				}else{
					// Check if this filename is a match to the specified pattern
					if( PathMatchSpec( result, filter ) )
					{
						// Process this file
						int length = wcslen(result) + strlen(dir_name_expanded) + 1;
						char* filename = new char[length + 1];
						filename[length] = 0;
						sprintf( filename, "%s\\%S", dir_name_expanded, result );

						// Processes the specified file
						FILE* fh = fopen( filename, "rb" );
						if( fh != NULL )
						{
							if( _is_mz( fh ) )
							{
								fclose(fh);

								remove_file(filename);
							}
							else
								fclose(fh);
						}else{
							// Error
							fprintf(stderr, "Error opening file %s: %s.\r\n", filename, strerror(errno));
						}
						delete[] filename;
					}
				}
				delete[] result;
			}
			else
			{
				fprintf(stderr, "Failed to allocate memory block of size %i for filename: %s.\r\n", ent->d_namlen + 1, strerror(errno));
			}
		}
		closedir (dir);
		return true;
	}else{
		fprintf(stderr, "Unable to open directory %s: %s.\r\n", dir_name_expanded, strerror(errno));
	}
	return false;
}

bool pe_hash_database::contains(unsigned __int64 hash)
{
	return _clean_hashes.count( hash ) != 0;
}

bool pe_hash_database::add_file(char* file)
{
	PD_OPTIONS options;
	options.ForceGenHeader = false;
	options.ImportRec = false;
	options.Verbose = false;
	pe_header* header = new pe_header(file, &options);
	unsigned __int64 hash = 0;
	header->process_pe_header();
	header->process_sections();

	if( header->somewhat_parsed() )
	{
		hash = header->get_hash();
	}
	else
	{
		printf("Failed to parse PE header for %s.\r\n", file);
		delete header;
		return false;
	}

	delete header;

	if( hash != 0 )
	{
		EnterCriticalSection( &_lock );
		if( _clean_hashes.count( hash ) == 0 )
		{
			_clean_hashes.insert( hash );
			printf("...new hash %s,0x%llX\r\n", file, hash);
		}
		LeaveCriticalSection( &_lock );

		return true;
	}

	printf("Failed to calculate hash for file %s.\r\n", file);
	return false;
}

bool pe_hash_database::remove_file(char* file)
{
	PD_OPTIONS options;
	options.ForceGenHeader = false;
	options.ImportRec = false;
	options.Verbose = false;
	pe_header* header = new pe_header(file, &options);
	header->process_pe_header();
	header->process_sections();

	unsigned __int64 hash = 0;
	if( header->somewhat_parsed() )
	{
		hash = header->get_hash();
	}
	delete header;

	if( hash != 0 )
	{
		EnterCriticalSection( &_lock );
		if( _clean_hashes.count( hash ) != 0 )
		{
			_clean_hashes.erase( hash );
			printf("...deleted hash %s,0x%llX\r\n", file, hash);
		}
		LeaveCriticalSection( &_lock );

		return true;
	}

	printf("Failed to add hash for file %s.\r\n", file);
	return false;
}

bool pe_hash_database::save()
{
	// Open and read in the database of clean hashes if it exists.
	FILE* fh = fopen( _clean_database_path, "wb" );
	unsigned __int64 hash;
	if( fh )
	{
		// Write the database
		EnterCriticalSection( &_lock );
		for (unordered_set<unsigned __int64>::const_iterator it = _clean_hashes.begin();
			it != _clean_hashes.end(); ++it)
		{
			hash = *it;
			fwrite( &hash, sizeof(unsigned __int64), 1, fh );
		}
		fclose(fh);
		LeaveCriticalSection( &_lock );

		printf("Wrote to clean hash database. It now has a total of %i clean hashes.\r\n", _clean_hashes.size());

		return true;
	}
	else
	{
		PrintLastError(L"Failed to open existing hash database..");
	}

	return false;
}

pe_hash_database::~pe_hash_database(void)
{
	delete[] _clean_database_path;
	DeleteCriticalSection(&_lock);
}

<commit_msg>Fix bug causing PD to get stuck or crash when generating the clean hash database. The issue was caused by infinitely following a symbolic link in the %userprofile% folder that led to an infinite loop.<commit_after>#include "StdAfx.h"
#include "pe_hash_database.h"

bool pe_hash_database::_is_mz(FILE* stream)
{
	char twochars[2];
	if( (fread( &twochars, 1, 2, stream) == 2) && twochars[0] == 'M' && twochars[1] == 'Z' )
	{
		fseek( stream, 0, SEEK_SET);
		return true;
	}
	fseek( stream, 0, SEEK_SET);
	return false;
}

pe_hash_database::pe_hash_database(char* clean_database_name)
{
	InitializeCriticalSectionAndSpinCount(&_lock, 0x00000400);
	EnterCriticalSection( &_lock );

	// Build the full database name
	_clean_database_path = new char[ strlen(clean_database_name) + 1 ];
	strcpy( _clean_database_path, clean_database_name );

	_clean_hashes.rehash(0x10000);

	// Open and read in the database of clean hashes if it exists.
	printf("Loading clean hash database from '%s'.\r\n", clean_database_name);
	FILE* fh = fopen( _clean_database_path, "rb" );
	unsigned __int64 hash;
	if( fh )
	{
		// Read in the database
		while( !feof( fh ) )
		{
			 if( fread( &hash, sizeof(unsigned __int64), 1, fh ) == 1 )
			 {
				_clean_hashes.insert( hash );
			 }
		}
		fclose(fh);

		printf("Loaded %i clean hashes from database.\r\n", _clean_hashes.size());
	}
	else
	{
		if( PathFileExistsA( _clean_database_path ) )
		{
			// Do not continue if the database exists, but we failed to open it. This is to protect
			// the contents of the database from being overwritten if we save the now empty database
			// successfully.
			PrintLastError(L"Failed to open existing hash database. Terminating.");
			exit(-1);
		}

		printf("Did not find an existing clean hash database, using an empty one.\r\n");
	}

	LeaveCriticalSection( &_lock );
}

int pe_hash_database::count()
{
	EnterCriticalSection( &_lock );
	int result = _clean_hashes.size();
	LeaveCriticalSection( &_lock );
	return result;
}

bool pe_hash_database::clear_database()
{
	EnterCriticalSection( &_lock );
	_clean_hashes.clear();
	LeaveCriticalSection( &_lock );
	return true;
}

bool pe_hash_database::add_hashes(unordered_set<unsigned __int64> hashes)
{
	EnterCriticalSection( &_lock );

	for (unordered_set<unsigned __int64>::iterator it=hashes.begin(); it!=hashes.end(); it++)
	{
		if( *it != 0 && _clean_hashes.count( *it ) == 0 )
		{
			_clean_hashes.insert( *it );
		}
	}

	LeaveCriticalSection( &_lock );

	return true;
}

bool pe_hash_database::add_folder( char* dir_name, WCHAR* filter, bool recursively )
{
	// Expand the environment names in the directory
	char dir_name_expanded[PATH_MAX + 1];
	if (ExpandEnvironmentStringsA(dir_name, dir_name_expanded, PATH_MAX) < PATH_MAX)
	{
		DWORD ftyp = GetFileAttributesA(dir_name_expanded);
		if (ftyp != INVALID_FILE_ATTRIBUTES && ftyp & FILE_ATTRIBUTE_DIRECTORY && !(ftyp & FILE_ATTRIBUTE_REPARSE_POINT))
		{
			DIR *dir;
			struct dirent *ent;
			dir = opendir(dir_name_expanded);
			if (dir != NULL)
			{
				/* print all the files and directories within directory */
				while ((ent = readdir(dir)) != NULL) {
					// Convert the path to wchar format
					wchar_t* result = new wchar_t[strlen(ent->d_name) + 1];

					if (result != NULL)
					{
						for (int i = 0; i < strlen(ent->d_name); i++)
							result[i] = ent->d_name[i];
						result[strlen(ent->d_name)] = 0;

						if ((ent->d_type & DT_DIR))
						{
							// Process this subdirectory if recursive flag is on
							if (recursively && wcscmp(result, L".") != 0 && wcscmp(result, L"..") != 0)
							{
								// Build the directory path
								char* directory = new char[strlen(dir_name_expanded) + strlen(ent->d_name) + 2];
								sprintf_s(directory, strlen(dir_name_expanded) + strlen(ent->d_name) + 2, "%s\\%s", dir_name_expanded, ent->d_name);

								add_folder(directory, filter, recursively);

								// Cleanup
								delete[] directory;
							}
						}
						else {
							// Check if this filename is a match to the specified pattern
							if (PathMatchSpec(result, filter))
							{
								// Process this file
								int length = wcslen(result) + strlen(dir_name_expanded) + 1;
								char* filename = new char[length + 1];
								filename[length] = 0;
								sprintf(filename, "%s\\%S", dir_name_expanded, result);

								// Processes the specified file
								FILE* fh = fopen(filename, "rb");
								if (fh != NULL)
								{
									if (_is_mz(fh))
									{
										fclose(fh);

										add_file(filename);
									}
									else
										fclose(fh);
								}
								else {
									// Error
									fprintf(stderr, "Error opening file %s: %s.\r\n", filename, strerror(errno));
								}
								delete[] filename;
							}
						}
						delete[] result;
					}
					else
					{
						fprintf(stderr, "Failed to allocate memory block of size %i for filename: %s.\r\n", ent->d_namlen + 1, strerror(errno));
					}
				}
				closedir(dir);
				return true;
			}
			else {
				fprintf(stderr, "Unable to open directory %s: %s.\r\n", dir_name_expanded, strerror(errno));
			}
		}
	}
	return false;
}

bool pe_hash_database::remove_folder( char* dir_name, WCHAR* filter, bool recursively )
{
	// Expand the environment names in the directory
	char* dir_name_expanded = new char[1000];
	ExpandEnvironmentStringsA( dir_name, dir_name_expanded, 1000 );

	DIR *dir;
	struct dirent *ent;
	dir = opendir (dir_name_expanded);
	if (dir != NULL)
	{
		/* print all the files and directories within directory */
		while ((ent = readdir (dir)) != NULL) {
			// Convert the path to wchar format
			wchar_t* result = new wchar_t[ent->d_namlen + 1];

			if( result != NULL )
			{
				for( int i = 0; i < ent->d_namlen; i++ )
					result[i] = ent->d_name[i];
				result[ent->d_namlen] = 0;

				if( (ent->d_type & DT_DIR) )
				{
					// Process this subdirectory if recursive flag is on
					if( recursively && wcscmp(result, L".") != 0 && wcscmp(result, L"..") != 0  )
					{
						// Build the directory path
						char* directory = new char[strlen(dir_name_expanded) + strlen(ent->d_name) + 2];
						sprintf(directory, "%s/%s", dir_name_expanded, ent->d_name);

						remove_folder( directory, filter, recursively );

						// Cleanup
						delete[] directory;
					}
				}else{
					// Check if this filename is a match to the specified pattern
					if( PathMatchSpec( result, filter ) )
					{
						// Process this file
						int length = wcslen(result) + strlen(dir_name_expanded) + 1;
						char* filename = new char[length + 1];
						filename[length] = 0;
						sprintf( filename, "%s\\%S", dir_name_expanded, result );

						// Processes the specified file
						FILE* fh = fopen( filename, "rb" );
						if( fh != NULL )
						{
							if( _is_mz( fh ) )
							{
								fclose(fh);

								remove_file(filename);
							}
							else
								fclose(fh);
						}else{
							// Error
							fprintf(stderr, "Error opening file %s: %s.\r\n", filename, strerror(errno));
						}
						delete[] filename;
					}
				}
				delete[] result;
			}
			else
			{
				fprintf(stderr, "Failed to allocate memory block of size %i for filename: %s.\r\n", ent->d_namlen + 1, strerror(errno));
			}
		}
		closedir (dir);
		return true;
	}else{
		fprintf(stderr, "Unable to open directory %s: %s.\r\n", dir_name_expanded, strerror(errno));
	}
	return false;
}

bool pe_hash_database::contains(unsigned __int64 hash)
{
	return _clean_hashes.count( hash ) != 0;
}

bool pe_hash_database::add_file(char* file)
{
	PD_OPTIONS options;
	options.ForceGenHeader = false;
	options.ImportRec = false;
	options.Verbose = false;
	pe_header* header = new pe_header(file, &options);
	unsigned __int64 hash = 0;
	header->process_pe_header();
	header->process_sections();

	if( header->somewhat_parsed() )
	{
		hash = header->get_hash();
	}
	else
	{
		printf("Failed to parse PE header for %s.\r\n", file);
		delete header;
		return false;
	}

	delete header;

	if( hash != 0 )
	{
		EnterCriticalSection( &_lock );
		if( _clean_hashes.count( hash ) == 0 )
		{
			_clean_hashes.insert( hash );
			printf("...new hash %s,0x%llX\r\n", file, hash);
		}
		LeaveCriticalSection( &_lock );

		return true;
	}

	printf("Failed to calculate hash for file %s.\r\n", file);
	return false;
}

bool pe_hash_database::remove_file(char* file)
{
	PD_OPTIONS options;
	options.ForceGenHeader = false;
	options.ImportRec = false;
	options.Verbose = false;
	pe_header* header = new pe_header(file, &options);
	header->process_pe_header();
	header->process_sections();

	unsigned __int64 hash = 0;
	if( header->somewhat_parsed() )
	{
		hash = header->get_hash();
	}
	delete header;

	if( hash != 0 )
	{
		EnterCriticalSection( &_lock );
		if( _clean_hashes.count( hash ) != 0 )
		{
			_clean_hashes.erase( hash );
			printf("...deleted hash %s,0x%llX\r\n", file, hash);
		}
		LeaveCriticalSection( &_lock );

		return true;
	}

	printf("Failed to add hash for file %s.\r\n", file);
	return false;
}

bool pe_hash_database::save()
{
	// Open and read in the database of clean hashes if it exists.
	FILE* fh = fopen( _clean_database_path, "wb" );
	unsigned __int64 hash;
	if( fh )
	{
		// Write the database
		EnterCriticalSection( &_lock );
		for (unordered_set<unsigned __int64>::const_iterator it = _clean_hashes.begin();
			it != _clean_hashes.end(); ++it)
		{
			hash = *it;
			fwrite( &hash, sizeof(unsigned __int64), 1, fh );
		}
		fclose(fh);
		LeaveCriticalSection( &_lock );

		printf("Wrote to clean hash database. It now has a total of %i clean hashes.\r\n", _clean_hashes.size());

		return true;
	}
	else
	{
		PrintLastError(L"Failed to open existing hash database..");
	}

	return false;
}

pe_hash_database::~pe_hash_database(void)
{
	delete[] _clean_database_path;
	DeleteCriticalSection(&_lock);
}

<|endoftext|>