<commit_before>/*=====================================================================

QGroundControl Open Source Ground Control Station

(c) 2009 - 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Definition of UDP connection (server) for unmanned vehicles
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QTimer>
#include <QList>
#include <QDebug>
#include <QMutexLocker>
#include <QNetworkProxy>
#include <iostream>

#include "UDPLink.h"
#include "QGC.h"
#include <QHostInfo>

static bool is_ip(const QString& address)
{
    int a,b,c,d;
    if (sscanf(address.toStdString().c_str(), "%d.%d.%d.%d", &a, &b, &c, &d) != 4)
        return false;
    return true;
}

static QString get_ip_address(const QString& address)
{
    if(is_ip(address))
        return address;
    // Need to look it up
    QHostInfo info = QHostInfo::fromName(address);
    if (info.error() == QHostInfo::NoError)
    {
        QList<QHostAddress> hostAddresses = info.addresses();
        QHostAddress address;
        for (int i = 0; i < hostAddresses.size(); i++)
        {
            // Exclude all IPv6 addresses
            if (!hostAddresses.at(i).toString().contains(":"))
            {
                return hostAddresses.at(i).toString();
            }
        }
    }
    return QString("");
}

UDPLink::UDPLink(UDPConfiguration* config)
    : _socket(NULL)
    , _connectState(false)
{
    Q_ASSERT(config != NULL);
    _config = config;
    _config->setLink(this);

    // We're doing it wrong - because the Qt folks got the API wrong:
    // http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
    moveToThread(this);

    qDebug() << "UDP Created " << _config->name();
}

UDPLink::~UDPLink()
{
    // Disconnect link from configuration
    _config->setLink(NULL);
    _disconnect();
    // Tell the thread to exit
    quit();
    // Wait for it to exit
    wait();
    this->deleteLater();
}

/**
 * @brief Runs the thread
 *
 **/
void UDPLink::run()
{
    _hardwareConnect();
    exec();
}

void UDPLink::_restartConnection()
{
    if(this->isConnected())
    {
        _disconnect();
        _connect();
    }
}

QString UDPLink::getName() const
{
    return _config->name();
}

void UDPLink::addHost(const QString& host)
{
    _config->addHost(host);
}

void UDPLink::removeHost(const QString& host)
{
    _config->removeHost(host);
}

#define UDPLINK_DEBUG 0

void UDPLink::writeBytes(const char* data, qint64 size)
{
    // Broadcast to all connected systems
    QString host;
    int port;
    if(_config->firstHost(host, port)) {
        do {
            if(UDPLINK_DEBUG) {
                QString bytes;
                QString ascii;
                for (int i=0; i<size; i++)
                {
                    unsigned char v = data[i];
                    bytes.append(QString().sprintf("%02x ", v));
                    if (data[i] > 31 && data[i] < 127)
                    {
                        ascii.append(data[i]);
                    }
                    else
                    {
                        ascii.append(219);
                    }
                }
                qDebug() << "Sent" << size << "bytes to" << host << ":" << port << "data:";
                qDebug() << bytes;
                qDebug() << "ASCII:" << ascii;
            }
            QHostAddress currentHost(host);
            _socket->writeDatagram(data, size, currentHost, (quint16)port);
            // Log the amount and time written out for future data rate calculations.
            QMutexLocker dataRateLocker(&dataRateMutex);
            logDataRateToBuffer(outDataWriteAmounts, outDataWriteTimes, &outDataIndex, size, QDateTime::currentMSecsSinceEpoch());
        } while (_config->nextHost(host, port));
    }
}

/**
 * @brief Read a number of bytes from the interface.
 **/
void UDPLink::readBytes()
{
    while (_socket->hasPendingDatagrams())
    {
        QByteArray datagram;
        datagram.resize(_socket->pendingDatagramSize());

        QHostAddress sender;
        quint16 senderPort;
        _socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);

        // FIXME TODO Check if this method is better than retrieving the data by individual processes
        emit bytesReceived(this, datagram);

        // Log this data reception for this timestep
        QMutexLocker dataRateLocker(&dataRateMutex);
        logDataRateToBuffer(inDataWriteAmounts, inDataWriteTimes, &inDataIndex, datagram.length(), QDateTime::currentMSecsSinceEpoch());

//        // Echo data for debugging purposes
//        std::cerr << __FILE__ << __LINE__ << "Received datagram:" << std::endl;
//        int i;
//        for (i=0; i<s; i++)
//        {
//            unsigned int v=data[i];
//            fprintf(stderr,"%02x ", v);
//        }
//        std::cerr << std::endl;

        // TODO This doesn't validade the sender. Anything sending UDP packets to this port gets
        // added to the list and will start receiving datagrams from here. Even a port scanner
        // would trigger this.
        // Add host to broadcast list if not yet present, or update its port
        _config->addHost(sender.toString(), (int)senderPort);
    }
}

/**
 * @brief Disconnect the connection.
 *
 * @return True if connection has been disconnected, false if connection couldn't be disconnected.
 **/
bool UDPLink::_disconnect(void)
{
    this->quit();
    this->wait();
    if (_socket) {
        // Make sure delete happen on correct thread
        _socket->deleteLater();
        _socket = NULL;
        emit disconnected();
    }
    // TODO When would this ever return false?
    _connectState = false;
    return !_connectState;
}

/**
 * @brief Connect the connection.
 *
 * @return True if connection has been established, false if connection couldn't be established.
 **/
bool UDPLink::_connect(void)
{
    if(this->isRunning())
    {
        this->quit();
        this->wait();
    }
    // TODO When would this ever return false?
    bool connected = true;
    start(HighPriority);
    return connected;
}

bool UDPLink::_hardwareConnect()
{
    QHostAddress host = QHostAddress::Any;
    _socket = new QUdpSocket();
    _socket->setProxy(QNetworkProxy::NoProxy);
    _connectState = _socket->bind(host, _config->localPort());
    QObject::connect(_socket, SIGNAL(readyRead()), this, SLOT(readBytes()));
    if (_connectState) {
        emit connected();
    }
    return _connectState;
}

/**
 * @brief Check if connection is active.
 *
 * @return True if link is connected, false otherwise.
 **/
bool UDPLink::isConnected() const
{
    return _connectState;
}

qint64 UDPLink::getConnectionSpeed() const
{
    return 54000000; // 54 Mbit
}

qint64 UDPLink::getCurrentInDataRate() const
{
    return 0;
}

qint64 UDPLink::getCurrentOutDataRate() const
{
    return 0;
}

//--------------------------------------------------------------------------
//-- UDPConfiguration

UDPConfiguration::UDPConfiguration(const QString& name) : LinkConfiguration(name)
{
    _localPort = QGC_UDP_LOCAL_PORT;
}

UDPConfiguration::UDPConfiguration(UDPConfiguration* source) : LinkConfiguration(source)
{
    _localPort = source->localPort();
    _hosts.clear();
    QString host;
    int port;
    if(source->firstHost(host, port)) {
        do {
            addHost(host, port);
        } while(source->nextHost(host, port));
    }
}

void UDPConfiguration::copyFrom(LinkConfiguration *source)
{
    LinkConfiguration::copyFrom(source);
    UDPConfiguration* usource = dynamic_cast<UDPConfiguration*>(source);
    Q_ASSERT(usource != NULL);
    _localPort = usource->localPort();
    QString host;
    int port;
    if(usource->firstHost(host, port)) {
        do {
            addHost(host, port);
        } while(usource->nextHost(host, port));
    }
}

/**
 * @param host Hostname in standard formatt, e.g. localhost:14551 or 192.168.1.1:14551
 */
void UDPConfiguration::addHost(const QString& host)
{
    // Handle x.x.x.x:p
    if (host.contains(":"))
    {
        addHost(host.split(":").first(), host.split(":").last().toInt());
    }
    // If no port, use default
    else
    {
        addHost(host, (int)_localPort);
    }
}

void UDPConfiguration::addHost(const QString& host, int port)
{
    QMutexLocker locker(&_confMutex);
    if(_hosts.contains(host)) {
        if(_hosts[host] != port) {
            _hosts[host] = port;
        }
    } else {
        QString ipAdd = get_ip_address(host);
        if(ipAdd.isEmpty()) {
            qWarning() << "UDP:" << "Could not resolve" << host;
        } else {
            _hosts[ipAdd] = port;
            qDebug() << "UDP:" << "Adding Host:" << ipAdd << ":" << port;
        }
    }
}

void UDPConfiguration::removeHost(const QString& host)
{
    QMutexLocker locker(&_confMutex);
    QString tHost = host;
    if (tHost.contains(":")) {
        tHost = tHost.split(":").first();
    }
    tHost = tHost.trimmed();
    QMap<QString, int>::iterator i = _hosts.find(tHost);
    if(i != _hosts.end()) {
        _hosts.erase(i);
    }
}

bool UDPConfiguration::firstHost(QString& host, int& port)
{
    _confMutex.lock();
    _it = _hosts.begin();
    if(_it == _hosts.end()) {
        _confMutex.unlock();
        return false;
    }
    _confMutex.unlock();
    return nextHost(host, port);
}

bool UDPConfiguration::nextHost(QString& host, int& port)
{
    QMutexLocker locker(&_confMutex);
    if(_it != _hosts.end()) {
        host = _it.key();
        port = _it.value();
        _it++;
        return true;
    }
    return false;
}

void UDPConfiguration::setLocalPort(quint16 port)
{
    _localPort = port;
}

void UDPConfiguration::saveSettings(QSettings& settings, const QString& root)
{
    _confMutex.lock();
    settings.beginGroup(root);
    settings.setValue("port", (int)_localPort);
    settings.setValue("hostCount", _hosts.count());
    int index = 0;
    QMap<QString, int>::const_iterator it = _hosts.begin();
    while(it != _hosts.end()) {
        QString hkey = QString("host%1").arg(index);
        settings.setValue(hkey, it.key());
        QString pkey = QString("port%1").arg(index);
        settings.setValue(pkey, it.value());
        it++;
        index++;
    }
    settings.endGroup();
    _confMutex.unlock();
}

void UDPConfiguration::loadSettings(QSettings& settings, const QString& root)
{
    _confMutex.lock();
    _hosts.clear();
    _confMutex.unlock();
    settings.beginGroup(root);
    _localPort = (quint16)settings.value("port", QGC_UDP_LOCAL_PORT).toUInt();
    int hostCount = settings.value("hostCount", 0).toInt();
    for(int i = 0; i < hostCount; i++) {
        QString hkey = QString("host%1").arg(i);
        QString pkey = QString("port%1").arg(i);
        if(settings.contains(hkey) && settings.contains(pkey)) {
            addHost(settings.value(hkey).toString(), settings.value(pkey).toInt());
        }
    }
    settings.endGroup();
}

void UDPConfiguration::updateSettings()
{
    if(_link) {
        UDPLink* ulink = dynamic_cast<UDPLink*>(_link);
        if(ulink) {
            ulink->_restartConnection();
        }
    }
}
<commit_msg>Fixing UDP connections issues.<commit_after>/*=====================================================================

QGroundControl Open Source Ground Control Station

(c) 2009 - 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Definition of UDP connection (server) for unmanned vehicles
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QTimer>
#include <QList>
#include <QDebug>
#include <QMutexLocker>
#include <QNetworkProxy>
#include <iostream>

#include "UDPLink.h"
#include "QGC.h"
#include <QHostInfo>

static bool is_ip(const QString& address)
{
    int a,b,c,d;
    if (sscanf(address.toStdString().c_str(), "%d.%d.%d.%d", &a, &b, &c, &d) != 4)
        return false;
    return true;
}

static QString get_ip_address(const QString& address)
{
    if(is_ip(address))
        return address;
    // Need to look it up
    QHostInfo info = QHostInfo::fromName(address);
    if (info.error() == QHostInfo::NoError)
    {
        QList<QHostAddress> hostAddresses = info.addresses();
        QHostAddress address;
        for (int i = 0; i < hostAddresses.size(); i++)
        {
            // Exclude all IPv6 addresses
            if (!hostAddresses.at(i).toString().contains(":"))
            {
                return hostAddresses.at(i).toString();
            }
        }
    }
    return QString("");
}

UDPLink::UDPLink(UDPConfiguration* config)
    : _socket(NULL)
    , _connectState(false)
{
    Q_ASSERT(config != NULL);
    _config = config;
    _config->setLink(this);

    // We're doing it wrong - because the Qt folks got the API wrong:
    // http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
    moveToThread(this);

    qDebug() << "UDP Created " << _config->name();
}

UDPLink::~UDPLink()
{
    // Disconnect link from configuration
    _config->setLink(NULL);
    _disconnect();
    // Tell the thread to exit
    quit();
    // Wait for it to exit
    wait();
    this->deleteLater();
}

/**
 * @brief Runs the thread
 *
 **/
void UDPLink::run()
{
    _hardwareConnect();
    exec();
}

void UDPLink::_restartConnection()
{
    if(this->isConnected())
    {
        _disconnect();
        _connect();
    }
}

QString UDPLink::getName() const
{
    return _config->name();
}

void UDPLink::addHost(const QString& host)
{
    _config->addHost(host);
}

void UDPLink::removeHost(const QString& host)
{
    _config->removeHost(host);
}

#define UDPLINK_DEBUG 0

void UDPLink::writeBytes(const char* data, qint64 size)
{
    // Broadcast to all connected systems
    QString host;
    int port;
    if(_config->firstHost(host, port)) {
        do {
            if(UDPLINK_DEBUG) {
                QString bytes;
                QString ascii;
                for (int i=0; i<size; i++)
                {
                    unsigned char v = data[i];
                    bytes.append(QString().sprintf("%02x ", v));
                    if (data[i] > 31 && data[i] < 127)
                    {
                        ascii.append(data[i]);
                    }
                    else
                    {
                        ascii.append(219);
                    }
                }
                qDebug() << "Sent" << size << "bytes to" << host << ":" << port << "data:";
                qDebug() << bytes;
                qDebug() << "ASCII:" << ascii;
            }
            QHostAddress currentHost(host);
            _socket->writeDatagram(data, size, currentHost, (quint16)port);
            // Log the amount and time written out for future data rate calculations.
            QMutexLocker dataRateLocker(&dataRateMutex);
            logDataRateToBuffer(outDataWriteAmounts, outDataWriteTimes, &outDataIndex, size, QDateTime::currentMSecsSinceEpoch());
        } while (_config->nextHost(host, port));
    }
}

/**
 * @brief Read a number of bytes from the interface.
 **/
void UDPLink::readBytes()
{
    while (_socket->hasPendingDatagrams())
    {
        QByteArray datagram;
        datagram.resize(_socket->pendingDatagramSize());

        QHostAddress sender;
        quint16 senderPort;
        _socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);

        // FIXME TODO Check if this method is better than retrieving the data by individual processes
        emit bytesReceived(this, datagram);

        // Log this data reception for this timestep
        QMutexLocker dataRateLocker(&dataRateMutex);
        logDataRateToBuffer(inDataWriteAmounts, inDataWriteTimes, &inDataIndex, datagram.length(), QDateTime::currentMSecsSinceEpoch());

//        // Echo data for debugging purposes
//        std::cerr << __FILE__ << __LINE__ << "Received datagram:" << std::endl;
//        int i;
//        for (i=0; i<s; i++)
//        {
//            unsigned int v=data[i];
//            fprintf(stderr,"%02x ", v);
//        }
//        std::cerr << std::endl;

        // TODO This doesn't validade the sender. Anything sending UDP packets to this port gets
        // added to the list and will start receiving datagrams from here. Even a port scanner
        // would trigger this.
        // Add host to broadcast list if not yet present, or update its port
        _config->addHost(sender.toString(), (int)senderPort);
    }
}

/**
 * @brief Disconnect the connection.
 *
 * @return True if connection has been disconnected, false if connection couldn't be disconnected.
 **/
bool UDPLink::_disconnect(void)
{
    this->quit();
    this->wait();
    if (_socket) {
        // Make sure delete happen on correct thread
        _socket->close();
        _socket->deleteLater();
        _socket = NULL;
        emit disconnected();
    }
    // TODO When would this ever return false?
    _connectState = false;
    return !_connectState;
}

/**
 * @brief Connect the connection.
 *
 * @return True if connection has been established, false if connection couldn't be established.
 **/
bool UDPLink::_connect(void)
{
    if(this->isRunning())
    {
        this->quit();
        this->wait();
    }
    // TODO When would this ever return false?
    bool connected = true;
    start(HighPriority);
    return connected;
}

bool UDPLink::_hardwareConnect()
{
    QHostAddress host = QHostAddress::Any;
    _socket = new QUdpSocket();
    _socket->setProxy(QNetworkProxy::NoProxy);
    _connectState = _socket->bind(host, _config->localPort(), QAbstractSocket::ReuseAddressHint);
    if (_connectState) {
        QObject::connect(_socket, SIGNAL(readyRead()), this, SLOT(readBytes()));
        emit connected();
    } else {
        emit communicationError("UDP Link Error", "Error binding UDP port");
    }
    return _connectState;
}

/**
 * @brief Check if connection is active.
 *
 * @return True if link is connected, false otherwise.
 **/
bool UDPLink::isConnected() const
{
    return _connectState;
}

qint64 UDPLink::getConnectionSpeed() const
{
    return 54000000; // 54 Mbit
}

qint64 UDPLink::getCurrentInDataRate() const
{
    return 0;
}

qint64 UDPLink::getCurrentOutDataRate() const
{
    return 0;
}

//--------------------------------------------------------------------------
//-- UDPConfiguration

UDPConfiguration::UDPConfiguration(const QString& name) : LinkConfiguration(name)
{
    _localPort = QGC_UDP_LOCAL_PORT;
}

UDPConfiguration::UDPConfiguration(UDPConfiguration* source) : LinkConfiguration(source)
{
    _localPort = source->localPort();
    _hosts.clear();
    QString host;
    int port;
    if(source->firstHost(host, port)) {
        do {
            addHost(host, port);
        } while(source->nextHost(host, port));
    }
}

void UDPConfiguration::copyFrom(LinkConfiguration *source)
{
    LinkConfiguration::copyFrom(source);
    UDPConfiguration* usource = dynamic_cast<UDPConfiguration*>(source);
    Q_ASSERT(usource != NULL);
    _localPort = usource->localPort();
    QString host;
    int port;
    if(usource->firstHost(host, port)) {
        do {
            addHost(host, port);
        } while(usource->nextHost(host, port));
    }
}

/**
 * @param host Hostname in standard formatt, e.g. localhost:14551 or 192.168.1.1:14551
 */
void UDPConfiguration::addHost(const QString& host)
{
    // Handle x.x.x.x:p
    if (host.contains(":"))
    {
        addHost(host.split(":").first(), host.split(":").last().toInt());
    }
    // If no port, use default
    else
    {
        addHost(host, (int)_localPort);
    }
}

void UDPConfiguration::addHost(const QString& host, int port)
{
    QMutexLocker locker(&_confMutex);
    if(_hosts.contains(host)) {
        if(_hosts[host] != port) {
            _hosts[host] = port;
        }
    } else {
        QString ipAdd = get_ip_address(host);
        if(ipAdd.isEmpty()) {
            qWarning() << "UDP:" << "Could not resolve" << host;
        } else {
            _hosts[ipAdd] = port;
            qDebug() << "UDP:" << "Adding Host:" << ipAdd << ":" << port;
        }
    }
}

void UDPConfiguration::removeHost(const QString& host)
{
    QMutexLocker locker(&_confMutex);
    QString tHost = host;
    if (tHost.contains(":")) {
        tHost = tHost.split(":").first();
    }
    tHost = tHost.trimmed();
    QMap<QString, int>::iterator i = _hosts.find(tHost);
    if(i != _hosts.end()) {
        _hosts.erase(i);
    }
}

bool UDPConfiguration::firstHost(QString& host, int& port)
{
    _confMutex.lock();
    _it = _hosts.begin();
    if(_it == _hosts.end()) {
        _confMutex.unlock();
        return false;
    }
    _confMutex.unlock();
    return nextHost(host, port);
}

bool UDPConfiguration::nextHost(QString& host, int& port)
{
    QMutexLocker locker(&_confMutex);
    if(_it != _hosts.end()) {
        host = _it.key();
        port = _it.value();
        _it++;
        return true;
    }
    return false;
}

void UDPConfiguration::setLocalPort(quint16 port)
{
    _localPort = port;
}

void UDPConfiguration::saveSettings(QSettings& settings, const QString& root)
{
    _confMutex.lock();
    settings.beginGroup(root);
    settings.setValue("port", (int)_localPort);
    settings.setValue("hostCount", _hosts.count());
    int index = 0;
    QMap<QString, int>::const_iterator it = _hosts.begin();
    while(it != _hosts.end()) {
        QString hkey = QString("host%1").arg(index);
        settings.setValue(hkey, it.key());
        QString pkey = QString("port%1").arg(index);
        settings.setValue(pkey, it.value());
        it++;
        index++;
    }
    settings.endGroup();
    _confMutex.unlock();
}

void UDPConfiguration::loadSettings(QSettings& settings, const QString& root)
{
    _confMutex.lock();
    _hosts.clear();
    _confMutex.unlock();
    settings.beginGroup(root);
    _localPort = (quint16)settings.value("port", QGC_UDP_LOCAL_PORT).toUInt();
    int hostCount = settings.value("hostCount", 0).toInt();
    for(int i = 0; i < hostCount; i++) {
        QString hkey = QString("host%1").arg(i);
        QString pkey = QString("port%1").arg(i);
        if(settings.contains(hkey) && settings.contains(pkey)) {
            addHost(settings.value(hkey).toString(), settings.value(pkey).toInt());
        }
    }
    settings.endGroup();
}

void UDPConfiguration::updateSettings()
{
    if(_link) {
        UDPLink* ulink = dynamic_cast<UDPLink*>(_link);
        if(ulink) {
            ulink->_restartConnection();
        }
    }
}
<|endoftext|>


<commit_before>#include "widget.h"
#include "ui_widget.h"
#include "settingswidget.h"
#include "settings.h"
#include "progressdialog.h"
#include <QMessageBox>
#include <QCloseEvent>
#include <QFileDialog>
#include <QFile>
#include <QtSvg/QSvgGenerator>
#include <QPainter>
#include <QThreadPool>
#include <QDebug>
#include <QMouseEvent>
#include <QInputDialog>

using namespace std;

void Widget::closeEvent(QCloseEvent *event)
{
    running = false;
    event->accept();
    exit(0);
}

void Widget::autofocusClicked()
{
    QRect focus = computeAutofocusFitness(generated);

    // Draw the focus rect
    QPixmap newpic = QPixmap::fromImage(pic);
    QPainter paint;
    paint.begin(&newpic);
    QPen pen(Qt::SolidLine);
    pen.setWidth(3*max((float)width/ui->imgBest->width(), (float)height/ui->imgBest->height()));
    pen.setColor(QColor(200,0,0,150));
    paint.setPen(pen);
    paint.drawRect(focus);
    paint.end();
    ui->imgOriginal->clear();
    ui->imgOriginal->setPixmap(newpic);

    // Set the focus coords
    FOCUS_LEFT = min(focus.x(),focus.right())*100/newpic.width();
    FOCUS_RIGHT = max(focus.x(),focus.right())*100/newpic.width();
    FOCUS_TOP = min(focus.y(),focus.bottom())*100/newpic.height();
    FOCUS_BOTTOM = max(focus.y(),focus.bottom())*100/newpic.height();
    if (FOCUS_LEFT == 100)
        FOCUS_LEFT = 99;
    if (FOCUS_TOP == 100)
        FOCUS_TOP = 99;
    if (FOCUS_RIGHT <= FOCUS_LEFT)
        FOCUS_RIGHT = FOCUS_LEFT+1;
    if (FOCUS_BOTTOM <= FOCUS_TOP)
        FOCUS_BOTTOM = FOCUS_TOP+1;
}

void Widget::openImageClicked()
{
    QString filename = QFileDialog::getOpenFileName(this,"Open Image","", "Images (*.png *.gif *.jpg *.jpeg *.bmp)");
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly))
        return;

    running = false;

    pic.loadFromData(file.readAll());
    pic = pic.convertToFormat(QImage::Format_RGB32);
    ui->imgOriginal->setPixmap(QPixmap::fromImage(pic));

    // Update our data
    width = pic.width();
    height = pic.height();

    // Create a blank pic the size of the original
    generated = QImage(width, height, QImage::Format_ARGB32);
    generated.fill(QColor(255,255,255));
    ui->imgBest->setPixmap(QPixmap::fromImage(generated));

    FOCUS_LEFT=0;
    FOCUS_RIGHT=100;
    FOCUS_TOP=0;
    FOCUS_BOTTOM=100;

    generation = 0;
    ui->generationLabel->setNum(0);
    ui->polysLabel->setNum(0);
    polys.clear();
    fitness = computeFitness(generated);
    updateGuiFitness();
    file.close();
}

void Widget::saveImageClicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save Image"),"",
                                                    "Images (*.png *.jpg)");
    if (fileName.isEmpty())
        return;
    generated.save(fileName);
}

void Widget::importDnaClicked()
{
    unsigned dnaHeight, dnaWidth;
    QString fileName = QFileDialog::getOpenFileName(this, tr("Import DNA"),"",
                                                    "DNA (*.dna)");
    if (fileName.isEmpty())
        return;
    polys.clear();
    QFile file(fileName);
    file.open(QIODevice::ReadOnly);
    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_4_0);
    in >> generation;
    in >> dnaWidth;
    in >> dnaHeight;
    in >> polys;
    file.close();

    if (dnaWidth != width || dnaHeight != height)
    {
        QMessageBox::critical(this,"Invalid image","The original image and the DNA have different dimentions");
        return;
    }

    redraw(generated);
    fitness  = computeFitness(generated);
    updateGuiFitness();
    ui->generationLabel->setNum(generation);
    ui->polysLabel->setNum(polys.size());
    ui->imgBest->setPixmap(QPixmap::fromImage(generated));
}

void Widget::redrawClicked()
{
    redraw(generated);
    ui->imgBest->setPixmap(QPixmap::fromImage(generated));
}

void Widget::exportDnaClicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Export DNA"),"",
                                                    "DNA (*.dna)");
    if (fileName.isEmpty())
        return;
    QFile file(fileName);
    file.open(QIODevice::WriteOnly | QIODevice::Truncate);
    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_4_0);
    out << generation;
    out << width;
    out << height;
    out << polys;
    file.close();

    redraw(generated);
}

void Widget::saveSVGClicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Export SVG"),"",
                                                    "SVG (*.svg)");
    if (fileName.isEmpty())
        return;
    QSvgGenerator generator;
    generator.setFileName(fileName);
    //generator.setSize(QSize(width, height));
    generator.setTitle("Mlkj's Bruteforce Vectorizer");

    QPainter painter;
    painter.begin(&generator);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);
    painter.setPen(QPen(Qt::NoPen));

    painter.fillRect(0,0,width,height,Qt::white);
    for (Poly& poly : polys)
    {
        QBrush brush(poly.color);
        brush.setStyle(Qt::SolidPattern);
        painter.setBrush(brush);
        painter.drawPolygon(poly.points.data(), poly.points.size());
    }
    painter.end();
}

void Widget::settingsClicked()
{
    SettingsWidget settingsWidget;
    settingsWidget.show();
    while (settingsWidget.isVisible())
        app->processEvents();
}

void Widget::updateGuiFitness()
{
    quint64 worstFitness = width*height*3*255;
    float percentFitness = 100.0-((double)fitness/(double)worstFitness*100.0);
    ui->fitnessLabel->setNum(percentFitness);
    ui->fitnessLabel->setText(ui->fitnessLabel->text()+'%');
}

void Widget::startClicked()
{
    if (running)
    {
        running = false;
        setStoppedGui();
        return;
    }
    if (pic.isNull())
        return;
    running=true;
    setRunningGui();

    run();
}

void Widget::githubClicked()
{
#ifdef WIN32
    system("start http://github.com/tux3/Evolve");
#else
    QMessageBox::information(this,"GitHub","http://github.com/tux3/Evolve");
#endif
}

void Widget::optimizeDnaClicked()
{
    setRunningGui();
    ui->btnStart->setEnabled(false);
    startStopAction->setEnabled(false);
    ui->btnStart->setText("Start");
    running = false;

    int polysSize = polys.size();

    ProgressDialog progress;
    progress.setMax(polysSize);
    progress.show();
    for (int i=0; i<polysSize; ++i)
    {
        if (!progress.isVisible())
            break;
        optimizeColors(i);
        if (!progress.isVisible())
            break;
        optimizeShape(i);
        progress.increment();
        app->processEvents();
    }

    setStoppedGui();
    startStopAction->setEnabled(true);
    ui->btnStart->setEnabled(true);
}

void Widget::cleanDnaClicked()
{
    // Make sure we're the only one touching the polys
    setRunningGui();
    ui->btnStart->setEnabled(false);
    startStopAction->setEnabled(false);
    ui->btnStart->setText("Start");
    running = false;
    app->processEvents();

    bool ok;
    double thresholdPercent = QInputDialog::getDouble(this, "Clean DNA",
                                       "Fitness threshold", 0.0001, 0, 100, 5, &ok);
    unsigned worstFitness = width*height*3*255;
    unsigned fitnessThreshold = thresholdPercent*((double)worstFitness)/100.0;

    if (!ok)
    {
        setStoppedGui();
        startStopAction->setEnabled(true);
        ui->btnStart->setEnabled(true);
        return;
    }

    ProgressDialog progress;
    progress.setMax(polys.size());
    progress.show();

    for (int i=0; i<polys.size();)
    {
        if (!progress.isVisible())
            break;

        progress.increment();
        app->processEvents();
        // Remove broken polys
        for (QPoint& point : polys[i].points)
        {
            if (point.x() > (int)width || point.x() < 0
                || point.y() > (int)height || point.y() < 0)
            {
                polys.remove(i);
                generation++;
                ui->generationLabel->setNum(generation);
                break; // Go to the next poly
            }
        }

        // Remove polys that don't change or worsen the fitness, or are under the threshold
        QVector<Poly> polyBak = polys;
        polys.remove(i);
        redraw(generated);
        unsigned newFit = computeFitness(generated);
        if (newFit <= fitness + fitnessThreshold)
        {
            fitness = newFit;
            generation++;
            ui->generationLabel->setNum(generation);
            updateGuiFitness();
            ui->imgBest->setPixmap(QPixmap::fromImage(generated));
            ui->polysLabel->setNum(polys.size());
            app->processEvents();
        }
        else
        {
            polys = polyBak;
            i++;
        }

    }
    setStoppedGui();
    startStopAction->setEnabled(true);
    ui->btnStart->setEnabled(true);
}

void Widget::setRunningGui()
{
    ui->btnOpen->setEnabled(false);
    openAction->setEnabled(false);
    cleanAction->setEnabled(false);
    optimizeAction->setEnabled(false);
    startStopAction->setText("S&top");
    ui->btnStart->setText("Stop");
}

void Widget::setStoppedGui()
{
    startStopAction->setText("S&tart");
    ui->btnStart->setText("Start");
    ui->btnOpen->setEnabled(true);
    openAction->setEnabled(true);
    cleanAction->setEnabled(true);
    optimizeAction->setEnabled(true);
}

void Widget::focusClicked()
{
    QMessageBox::information(this, "Focus",
    "Draw a rectangle on the original image to focus on it.\nRight click to cancel.");
}

bool Widget::eventFilter(QObject *object, QEvent *event)
{
    if (pic.isNull())
        return false;

    static bool pressed = false; // Is the left mouse button pressed
    static QPoint startCoords;
    if (object == ui->imgOriginal && event->type() == QEvent::MouseButtonPress)
    {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        if (mouseEvent->button() == Qt::LeftButton)
        {
            //QMessageBox::information(this, "Focus", "Clicked");
            pressed = true;
            startCoords = mouseEvent->pos();
        }
        else if (mouseEvent->button() == Qt::RightButton)
        {
            ui->imgOriginal->setPixmap(QPixmap::fromImage(pic));
            FOCUS_LEFT = FOCUS_TOP = 0;
            FOCUS_RIGHT = FOCUS_BOTTOM = 100;
        }
    }
    else if (object == ui->imgOriginal && event->type() == QEvent::MouseButtonRelease)
    {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        if (mouseEvent->button() == Qt::LeftButton)
            pressed = false;
    }
    else if (pressed && object == ui->imgOriginal && event->type() == QEvent::MouseMove)
    {
        // Draw the focus rect
        QPixmap newpic = QPixmap::fromImage(pic);
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        QPoint endCoords = mouseEvent->pos();
        endCoords.setX(max(min(endCoords.x(), ui->imgOriginal->width()),0));
        endCoords.setY(max(min(endCoords.y(), ui->imgOriginal->height()),0));
        QPoint scaledStart = startCoords;
        scaledStart.rx() *= (float)width/ui->imgBest->width();
        scaledStart.ry() *= (float)height/ui->imgBest->height();
        QPoint scaledEnd = endCoords;
        scaledEnd.rx() *= (float)width/ui->imgBest->width();
        scaledEnd.ry() *= (float)height/ui->imgBest->height();
        QPainter paint;
        paint.begin(&newpic);
        QPen pen(Qt::SolidLine);
        pen.setWidth(3*max((float)width/ui->imgBest->width(), (float)height/ui->imgBest->height()));
        pen.setColor(QColor(200,0,0,150));
        paint.setPen(pen);
        paint.drawRect(QRect(scaledStart, scaledEnd));
        paint.end();
        ui->imgOriginal->clear();
        ui->imgOriginal->setPixmap(newpic);

        // Set the focus coords
        FOCUS_LEFT = min(scaledStart.x(),scaledEnd.x())*100/newpic.width();
        FOCUS_RIGHT = max(scaledStart.x(),scaledEnd.x())*100/newpic.width();
        FOCUS_TOP = min(scaledStart.y(),scaledEnd.y())*100/newpic.height();
        FOCUS_BOTTOM = max(scaledStart.y(),scaledEnd.y())*100/newpic.height();
        if (FOCUS_LEFT == 100)
            FOCUS_LEFT = 99;
        if (FOCUS_TOP == 100)
            FOCUS_TOP = 99;
        if (FOCUS_RIGHT <= FOCUS_LEFT)
            FOCUS_RIGHT = FOCUS_LEFT+1;
        if (FOCUS_BOTTOM <= FOCUS_TOP)
            FOCUS_BOTTOM = FOCUS_TOP+1;
    }
    return false;
}
<commit_msg>Antialiasing on saved images<commit_after>#include "widget.h"
#include "ui_widget.h"
#include "settingswidget.h"
#include "settings.h"
#include "progressdialog.h"
#include <QMessageBox>
#include <QCloseEvent>
#include <QFileDialog>
#include <QFile>
#include <QtSvg/QSvgGenerator>
#include <QPainter>
#include <QThreadPool>
#include <QDebug>
#include <QMouseEvent>
#include <QInputDialog>

using namespace std;

void Widget::closeEvent(QCloseEvent *event)
{
    running = false;
    event->accept();
    exit(0);
}

void Widget::autofocusClicked()
{
    QRect focus = computeAutofocusFitness(generated);

    // Draw the focus rect
    QPixmap newpic = QPixmap::fromImage(pic);
    QPainter paint;
    paint.begin(&newpic);
    QPen pen(Qt::SolidLine);
    pen.setWidth(3*max((float)width/ui->imgBest->width(), (float)height/ui->imgBest->height()));
    pen.setColor(QColor(200,0,0,150));
    paint.setPen(pen);
    paint.drawRect(focus);
    paint.end();
    ui->imgOriginal->clear();
    ui->imgOriginal->setPixmap(newpic);

    // Set the focus coords
    FOCUS_LEFT = min(focus.x(),focus.right())*100/newpic.width();
    FOCUS_RIGHT = max(focus.x(),focus.right())*100/newpic.width();
    FOCUS_TOP = min(focus.y(),focus.bottom())*100/newpic.height();
    FOCUS_BOTTOM = max(focus.y(),focus.bottom())*100/newpic.height();
    if (FOCUS_LEFT == 100)
        FOCUS_LEFT = 99;
    if (FOCUS_TOP == 100)
        FOCUS_TOP = 99;
    if (FOCUS_RIGHT <= FOCUS_LEFT)
        FOCUS_RIGHT = FOCUS_LEFT+1;
    if (FOCUS_BOTTOM <= FOCUS_TOP)
        FOCUS_BOTTOM = FOCUS_TOP+1;
}

void Widget::openImageClicked()
{
    QString filename = QFileDialog::getOpenFileName(this,"Open Image","", "Images (*.png *.gif *.jpg *.jpeg *.bmp)");
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly))
        return;

    running = false;

    pic.loadFromData(file.readAll());
    pic = pic.convertToFormat(QImage::Format_RGB32);
    ui->imgOriginal->setPixmap(QPixmap::fromImage(pic));

    // Update our data
    width = pic.width();
    height = pic.height();

    // Create a blank pic the size of the original
    generated = QImage(width, height, QImage::Format_ARGB32);
    generated.fill(QColor(255,255,255));
    ui->imgBest->setPixmap(QPixmap::fromImage(generated));

    FOCUS_LEFT=0;
    FOCUS_RIGHT=100;
    FOCUS_TOP=0;
    FOCUS_BOTTOM=100;

    generation = 0;
    ui->generationLabel->setNum(0);
    ui->polysLabel->setNum(0);
    polys.clear();
    fitness = computeFitness(generated);
    updateGuiFitness();
    file.close();
}

void Widget::saveImageClicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save Image"),"",
                                                    "Images (*.png *.jpg)");
    if (fileName.isEmpty())
        return;

    QImage image(width, height, QImage::Format_ARGB32);
    QBrush brush(Qt::SolidPattern);
    image.fill(Qt::white);
    QPainter painter(&image);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    painter.setPen(QPen(Qt::NoPen));
    for (Poly& poly : polys)
    {
        brush.setColor(poly.color);
        painter.setBrush(brush);
        painter.drawPolygon(poly.points.data(), poly.points.size());
    }

    image.save(fileName);
}

void Widget::importDnaClicked()
{
    unsigned dnaHeight, dnaWidth;
    QString fileName = QFileDialog::getOpenFileName(this, tr("Import DNA"),"",
                                                    "DNA (*.dna)");
    if (fileName.isEmpty())
        return;
    polys.clear();
    QFile file(fileName);
    file.open(QIODevice::ReadOnly);
    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_4_0);
    in >> generation;
    in >> dnaWidth;
    in >> dnaHeight;
    in >> polys;
    file.close();

    if (dnaWidth != width || dnaHeight != height)
    {
        QMessageBox::critical(this,"Invalid image","The original image and the DNA have different dimentions");
        return;
    }

    redraw(generated);
    fitness  = computeFitness(generated);
    updateGuiFitness();
    ui->generationLabel->setNum(generation);
    ui->polysLabel->setNum(polys.size());
    ui->imgBest->setPixmap(QPixmap::fromImage(generated));
}

void Widget::redrawClicked()
{
    redraw(generated);
    ui->imgBest->setPixmap(QPixmap::fromImage(generated));
}

void Widget::exportDnaClicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Export DNA"),"",
                                                    "DNA (*.dna)");
    if (fileName.isEmpty())
        return;
    QFile file(fileName);
    file.open(QIODevice::WriteOnly | QIODevice::Truncate);
    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_4_0);
    out << generation;
    out << width;
    out << height;
    out << polys;
    file.close();

    redraw(generated);
}

void Widget::saveSVGClicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Export SVG"),"",
                                                    "SVG (*.svg)");
    if (fileName.isEmpty())
        return;
    QSvgGenerator generator;
    generator.setFileName(fileName);
    //generator.setSize(QSize(width, height));
    generator.setTitle("Mlkj's Bruteforce Vectorizer");

    QPainter painter;
    painter.begin(&generator);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);
    painter.setPen(QPen(Qt::NoPen));

    painter.fillRect(0,0,width,height,Qt::white);
    for (Poly& poly : polys)
    {
        QBrush brush(poly.color);
        brush.setStyle(Qt::SolidPattern);
        painter.setBrush(brush);
        painter.drawPolygon(poly.points.data(), poly.points.size());
    }
    painter.end();
}

void Widget::settingsClicked()
{
    SettingsWidget settingsWidget;
    settingsWidget.show();
    while (settingsWidget.isVisible())
        app->processEvents();
}

void Widget::updateGuiFitness()
{
    quint64 worstFitness = width*height*3*255;
    float percentFitness = 100.0-((double)fitness/(double)worstFitness*100.0);
    ui->fitnessLabel->setNum(percentFitness);
    ui->fitnessLabel->setText(ui->fitnessLabel->text()+'%');
}

void Widget::startClicked()
{
    if (running)
    {
        running = false;
        setStoppedGui();
        return;
    }
    if (pic.isNull())
        return;
    running=true;
    setRunningGui();

    run();
}

void Widget::githubClicked()
{
#ifdef WIN32
    system("start http://github.com/tux3/Evolve");
#else
    QMessageBox::information(this,"GitHub","http://github.com/tux3/Evolve");
#endif
}

void Widget::optimizeDnaClicked()
{
    setRunningGui();
    ui->btnStart->setEnabled(false);
    startStopAction->setEnabled(false);
    ui->btnStart->setText("Start");
    running = false;

    int polysSize = polys.size();

    ProgressDialog progress;
    progress.setMax(polysSize);
    progress.show();
    for (int i=0; i<polysSize; ++i)
    {
        if (!progress.isVisible())
            break;
        optimizeColors(i);
        if (!progress.isVisible())
            break;
        optimizeShape(i);
        progress.increment();
        app->processEvents();
    }

    setStoppedGui();
    startStopAction->setEnabled(true);
    ui->btnStart->setEnabled(true);
}

void Widget::cleanDnaClicked()
{
    // Make sure we're the only one touching the polys
    setRunningGui();
    ui->btnStart->setEnabled(false);
    startStopAction->setEnabled(false);
    ui->btnStart->setText("Start");
    running = false;
    app->processEvents();

    bool ok;
    double thresholdPercent = QInputDialog::getDouble(this, "Clean DNA",
                                       "Fitness threshold", 0.0001, 0, 100, 5, &ok);
    unsigned worstFitness = width*height*3*255;
    unsigned fitnessThreshold = thresholdPercent*((double)worstFitness)/100.0;

    if (!ok)
    {
        setStoppedGui();
        startStopAction->setEnabled(true);
        ui->btnStart->setEnabled(true);
        return;
    }

    ProgressDialog progress;
    progress.setMax(polys.size());
    progress.show();

    for (int i=0; i<polys.size();)
    {
        if (!progress.isVisible())
            break;

        progress.increment();
        app->processEvents();
        // Remove broken polys
        for (QPoint& point : polys[i].points)
        {
            if (point.x() > (int)width || point.x() < 0
                || point.y() > (int)height || point.y() < 0)
            {
                polys.remove(i);
                generation++;
                ui->generationLabel->setNum(generation);
                break; // Go to the next poly
            }
        }

        // Remove polys that don't change or worsen the fitness, or are under the threshold
        QVector<Poly> polyBak = polys;
        polys.remove(i);
        redraw(generated);
        unsigned newFit = computeFitness(generated);
        if (newFit <= fitness + fitnessThreshold)
        {
            fitness = newFit;
            generation++;
            ui->generationLabel->setNum(generation);
            updateGuiFitness();
            ui->imgBest->setPixmap(QPixmap::fromImage(generated));
            ui->polysLabel->setNum(polys.size());
            app->processEvents();
        }
        else
        {
            polys = polyBak;
            i++;
        }

    }
    setStoppedGui();
    startStopAction->setEnabled(true);
    ui->btnStart->setEnabled(true);
}

void Widget::setRunningGui()
{
    ui->btnOpen->setEnabled(false);
    openAction->setEnabled(false);
    cleanAction->setEnabled(false);
    optimizeAction->setEnabled(false);
    startStopAction->setText("S&top");
    ui->btnStart->setText("Stop");
}

void Widget::setStoppedGui()
{
    startStopAction->setText("S&tart");
    ui->btnStart->setText("Start");
    ui->btnOpen->setEnabled(true);
    openAction->setEnabled(true);
    cleanAction->setEnabled(true);
    optimizeAction->setEnabled(true);
}

void Widget::focusClicked()
{
    QMessageBox::information(this, "Focus",
    "Draw a rectangle on the original image to focus on it.\nRight click to cancel.");
}

bool Widget::eventFilter(QObject *object, QEvent *event)
{
    if (pic.isNull())
        return false;

    static bool pressed = false; // Is the left mouse button pressed
    static QPoint startCoords;
    if (object == ui->imgOriginal && event->type() == QEvent::MouseButtonPress)
    {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        if (mouseEvent->button() == Qt::LeftButton)
        {
            //QMessageBox::information(this, "Focus", "Clicked");
            pressed = true;
            startCoords = mouseEvent->pos();
        }
        else if (mouseEvent->button() == Qt::RightButton)
        {
            ui->imgOriginal->setPixmap(QPixmap::fromImage(pic));
            FOCUS_LEFT = FOCUS_TOP = 0;
            FOCUS_RIGHT = FOCUS_BOTTOM = 100;
        }
    }
    else if (object == ui->imgOriginal && event->type() == QEvent::MouseButtonRelease)
    {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        if (mouseEvent->button() == Qt::LeftButton)
            pressed = false;
    }
    else if (pressed && object == ui->imgOriginal && event->type() == QEvent::MouseMove)
    {
        // Draw the focus rect
        QPixmap newpic = QPixmap::fromImage(pic);
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        QPoint endCoords = mouseEvent->pos();
        endCoords.setX(max(min(endCoords.x(), ui->imgOriginal->width()),0));
        endCoords.setY(max(min(endCoords.y(), ui->imgOriginal->height()),0));
        QPoint scaledStart = startCoords;
        scaledStart.rx() *= (float)width/ui->imgBest->width();
        scaledStart.ry() *= (float)height/ui->imgBest->height();
        QPoint scaledEnd = endCoords;
        scaledEnd.rx() *= (float)width/ui->imgBest->width();
        scaledEnd.ry() *= (float)height/ui->imgBest->height();
        QPainter paint;
        paint.begin(&newpic);
        QPen pen(Qt::SolidLine);
        pen.setWidth(3*max((float)width/ui->imgBest->width(), (float)height/ui->imgBest->height()));
        pen.setColor(QColor(200,0,0,150));
        paint.setPen(pen);
        paint.drawRect(QRect(scaledStart, scaledEnd));
        paint.end();
        ui->imgOriginal->clear();
        ui->imgOriginal->setPixmap(newpic);

        // Set the focus coords
        FOCUS_LEFT = min(scaledStart.x(),scaledEnd.x())*100/newpic.width();
        FOCUS_RIGHT = max(scaledStart.x(),scaledEnd.x())*100/newpic.width();
        FOCUS_TOP = min(scaledStart.y(),scaledEnd.y())*100/newpic.height();
        FOCUS_BOTTOM = max(scaledStart.y(),scaledEnd.y())*100/newpic.height();
        if (FOCUS_LEFT == 100)
            FOCUS_LEFT = 99;
        if (FOCUS_TOP == 100)
            FOCUS_TOP = 99;
        if (FOCUS_RIGHT <= FOCUS_LEFT)
            FOCUS_RIGHT = FOCUS_LEFT+1;
        if (FOCUS_BOTTOM <= FOCUS_TOP)
            FOCUS_BOTTOM = FOCUS_TOP+1;
    }
    return false;
}
<|endoftext|>


<commit_before>
#ifndef __CONFIG_ARGS_H__
#define __CONFIG_ARGS_H__

#define KILOBYTE 1024L
#define MEGABYTE (KILOBYTE*1024L)
#define GIGABYTE (MEGABYTE*1024L)
#define TERABYTE (GIGABYTE*1024L)

/*!
 * Version strings
 */

#define SOFTWARE_NAME_STRING "RethinkDB"
#define VERSION_STRING "0.0.1"

/**
 * Basic configuration parameters.
 * TODO: Many of these should be runtime switches.
 */
// Max concurrent IO requests per event queue
#define MAX_CONCURRENT_IO_REQUESTS                256

// Don't send more IO requests to the system until the per-thread
// queue of IO requests is higher than this depth
#define TARGET_IO_QUEUE_DEPTH                     128

// Defines the maximum size of the batch of IO events to process on
// each loop iteration. A larger number will increase throughput but
// decrease concurrency
#define MAX_IO_EVENT_PROCESSING_BATCH_SIZE        50

// Size of the buffer used to perform IO operations (in bytes).
#define IO_BUFFER_SIZE                            (4 * KILOBYTE)

// Size of the device block size (in bytes)
#define DEVICE_BLOCK_SIZE                         (4 * KILOBYTE)

// Size of each btree node (in bytes) on disk
#define DEFAULT_BTREE_BLOCK_SIZE                  (4 * KILOBYTE)

// Maximum number of data blocks
#define MAX_DATA_EXTENTS                          (TERABYTE / (16 * KILOBYTE))

// Size of each extent (in bytes)
#define DEFAULT_EXTENT_SIZE                       (1 * MEGABYTE)

// Max size of log file name
#define MAX_LOG_FILE_NAME                         1024

// Max length of log message
#define MAX_LOG_MSGLEN                            1024

// Queue ID of logging worker
#define LOG_WORKER 0

// Ratio of free ram to use for the cache by default
#define DEFAULT_MAX_CACHE_RATIO                   0.8f

// Maximum number of operations packed into a single request
// TODO: make this dynamic and get rid of this parameter
#define MAX_OPS_IN_REQUEST                        16

// Maximum number of CPUs we support
// TODO: make this dynamic where possible
#define MAX_CPUS                                  128

// Maximum slices total
#define MAX_SLICES                                128

// Maximum number of files we use
#define MAX_SERIALIZERS                           32

// The number of ways we split a BTree (the most optimal is the number
// of cores, but we use a higher split factor to allow upgrading to
// more cores without migrating the database file).
#define DEFAULT_BTREE_SHARD_FACTOR                16

// Default port to listen on
#define DEFAULT_LISTEN_PORT                       8080

// Ticks (in milliseconds) the internal timed tasks are performed at
#define TIMER_TICKS_IN_MS                         5

// How many milliseconds to allow changes to sit in memory before flushing to disk
#define DEFAULT_FLUSH_TIMER_MS                    5000

// If the number of dirty buffers is more than X% of the maximum number of buffers allowed, then
// writeback will be started. DEFAULT_FLUSH_THRESHOLD_PERCENT is the default value of X.
#define DEFAULT_FLUSH_THRESHOLD_PERCENT           30

// How many times the page replacement algorithm tries to find an eligible page before giving up
#define PAGE_REPL_NUM_TRIES                       3

// How large can the key be, in bytes?  This value needs to fit in a byte.
#define MAX_KEY_SIZE                              250

// Any values of this size or less will be directly stored in btree leaf nodes.
// Values greater than this size will be stored in overflow blocks. This value
// needs to fit in a byte.
#define MAX_IN_NODE_VALUE_SIZE                    250

// In addition to the value itself we could potentially store memcached flags
// and a CAS value in the value contents, so we reserve space for that.
#define MAX_TOTAL_NODE_CONTENTS_SIZE              (MAX_IN_NODE_VALUE_SIZE + sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint32_t))

// memcached specifies the maximum value size to be 1MB
#define MAX_VALUE_SIZE                            (1024 * KILOBYTE)

// Perform allocator GC every N milliseconds (the resolution is limited to TIMER_TICKS_IN_MS)
#define ALLOC_GC_INTERVAL_MS                      3000

//filenames for the database
#define DEFAULT_DB_FILE_NAME                      "rethinkdb_data"

// We assume there will never be more than this many blocks. The value is computed by dividing
// 1 TB by the smallest reasonable block size.
#define MAX_BLOCK_ID                              (TERABYTE / KILOBYTE)

// We assume that there will never be more than this many blocks held in memory by the cache at
// any one time. The value is computed by dividing 50 GB by the smallest reasonable block size.
#define MAX_BLOCKS_IN_MEMORY                      (50 * GIGABYTE / KILOBYTE)

// This special block ID indicates the superblock. It doesn't really belong here because it's more
// of a magic constant than a tunable parameter.
#define SUPERBLOCK_ID                             0

// The ratio at which we should start GCing.
#define DEFAULT_GC_HIGH_RATIO                     0.75
#define MAX_GC_HIGH_RATIO                         0.99

// The ratio at which we don't want to keep GC'ing.
#define DEFAULT_GC_LOW_RATIO                      0.5
#define MIN_GC_LOW_RATIO                          0.01

// What's the maximum number of "young" extents we can have?
#define GC_YOUNG_EXTENT_MAX_SIZE                  50
// What's the definition of a "young" extent in microseconds?
#define GC_YOUNG_EXTENT_TIMELIMIT_MICROS          50000

// The ratio at which we should GC the lba list.
#define LBA_GC_THRESHOLD_RATIO_NUMERATOR          9
#define LBA_GC_THRESHOLD_RATIO_DENOMINATOR        10

// How many LBA structures to have for each file
#define LBA_SHARD_FACTOR                          16

// How many different places in each file we should be writing to at once, not counting the
// metablock or LBA
#define MAX_ACTIVE_DATA_EXTENTS                   64
#define DEFAULT_ACTIVE_DATA_EXTENTS               8

// How many zones the serializer will divide a block device into
#define DEFAULT_FILE_ZONE_SIZE                    GIGABYTE
#define MAX_FILE_ZONES                            (TERABYTE / DEFAULT_FILE_ZONE_SIZE)

#endif // __CONFIG_ARGS_H__

<commit_msg>Change garbage ratio to do a test.<commit_after>
#ifndef __CONFIG_ARGS_H__
#define __CONFIG_ARGS_H__

#define KILOBYTE 1024L
#define MEGABYTE (KILOBYTE*1024L)
#define GIGABYTE (MEGABYTE*1024L)
#define TERABYTE (GIGABYTE*1024L)

/*!
 * Version strings
 */

#define SOFTWARE_NAME_STRING "RethinkDB"
#define VERSION_STRING "0.0.1"

/**
 * Basic configuration parameters.
 * TODO: Many of these should be runtime switches.
 */
// Max concurrent IO requests per event queue
#define MAX_CONCURRENT_IO_REQUESTS                256

// Don't send more IO requests to the system until the per-thread
// queue of IO requests is higher than this depth
#define TARGET_IO_QUEUE_DEPTH                     128

// Defines the maximum size of the batch of IO events to process on
// each loop iteration. A larger number will increase throughput but
// decrease concurrency
#define MAX_IO_EVENT_PROCESSING_BATCH_SIZE        50

// Size of the buffer used to perform IO operations (in bytes).
#define IO_BUFFER_SIZE                            (4 * KILOBYTE)

// Size of the device block size (in bytes)
#define DEVICE_BLOCK_SIZE                         (4 * KILOBYTE)

// Size of each btree node (in bytes) on disk
#define DEFAULT_BTREE_BLOCK_SIZE                  (4 * KILOBYTE)

// Maximum number of data blocks
#define MAX_DATA_EXTENTS                          (TERABYTE / (16 * KILOBYTE))

// Size of each extent (in bytes)
#define DEFAULT_EXTENT_SIZE                       (1 * MEGABYTE)

// Max size of log file name
#define MAX_LOG_FILE_NAME                         1024

// Max length of log message
#define MAX_LOG_MSGLEN                            1024

// Queue ID of logging worker
#define LOG_WORKER 0

// Ratio of free ram to use for the cache by default
#define DEFAULT_MAX_CACHE_RATIO                   0.8f

// Maximum number of operations packed into a single request
// TODO: make this dynamic and get rid of this parameter
#define MAX_OPS_IN_REQUEST                        16

// Maximum number of CPUs we support
// TODO: make this dynamic where possible
#define MAX_CPUS                                  128

// Maximum slices total
#define MAX_SLICES                                128

// Maximum number of files we use
#define MAX_SERIALIZERS                           32

// The number of ways we split a BTree (the most optimal is the number
// of cores, but we use a higher split factor to allow upgrading to
// more cores without migrating the database file).
#define DEFAULT_BTREE_SHARD_FACTOR                16

// Default port to listen on
#define DEFAULT_LISTEN_PORT                       8080

// Ticks (in milliseconds) the internal timed tasks are performed at
#define TIMER_TICKS_IN_MS                         5

// How many milliseconds to allow changes to sit in memory before flushing to disk
#define DEFAULT_FLUSH_TIMER_MS                    5000

// If the number of dirty buffers is more than X% of the maximum number of buffers allowed, then
// writeback will be started. DEFAULT_FLUSH_THRESHOLD_PERCENT is the default value of X.
#define DEFAULT_FLUSH_THRESHOLD_PERCENT           30

// How many times the page replacement algorithm tries to find an eligible page before giving up
#define PAGE_REPL_NUM_TRIES                       3

// How large can the key be, in bytes?  This value needs to fit in a byte.
#define MAX_KEY_SIZE                              250

// Any values of this size or less will be directly stored in btree leaf nodes.
// Values greater than this size will be stored in overflow blocks. This value
// needs to fit in a byte.
#define MAX_IN_NODE_VALUE_SIZE                    250

// In addition to the value itself we could potentially store memcached flags
// and a CAS value in the value contents, so we reserve space for that.
#define MAX_TOTAL_NODE_CONTENTS_SIZE              (MAX_IN_NODE_VALUE_SIZE + sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint32_t))

// memcached specifies the maximum value size to be 1MB
#define MAX_VALUE_SIZE                            (1024 * KILOBYTE)

// Perform allocator GC every N milliseconds (the resolution is limited to TIMER_TICKS_IN_MS)
#define ALLOC_GC_INTERVAL_MS                      3000

//filenames for the database
#define DEFAULT_DB_FILE_NAME                      "rethinkdb_data"

// We assume there will never be more than this many blocks. The value is computed by dividing
// 1 TB by the smallest reasonable block size.
#define MAX_BLOCK_ID                              (TERABYTE / KILOBYTE)

// We assume that there will never be more than this many blocks held in memory by the cache at
// any one time. The value is computed by dividing 50 GB by the smallest reasonable block size.
#define MAX_BLOCKS_IN_MEMORY                      (50 * GIGABYTE / KILOBYTE)

// This special block ID indicates the superblock. It doesn't really belong here because it's more
// of a magic constant than a tunable parameter.
#define SUPERBLOCK_ID                             0

// The ratio at which we should start GCing.
#define DEFAULT_GC_HIGH_RATIO                     0.65
#define MAX_GC_HIGH_RATIO                         0.99

// The ratio at which we don't want to keep GC'ing.
#define DEFAULT_GC_LOW_RATIO                      0.5
#define MIN_GC_LOW_RATIO                          0.01

// What's the maximum number of "young" extents we can have?
#define GC_YOUNG_EXTENT_MAX_SIZE                  50
// What's the definition of a "young" extent in microseconds?
#define GC_YOUNG_EXTENT_TIMELIMIT_MICROS          50000

// The ratio at which we should GC the lba list.
#define LBA_GC_THRESHOLD_RATIO_NUMERATOR          9
#define LBA_GC_THRESHOLD_RATIO_DENOMINATOR        10

// How many LBA structures to have for each file
#define LBA_SHARD_FACTOR                          16

// How many different places in each file we should be writing to at once, not counting the
// metablock or LBA
#define MAX_ACTIVE_DATA_EXTENTS                   64
#define DEFAULT_ACTIVE_DATA_EXTENTS               8

// How many zones the serializer will divide a block device into
#define DEFAULT_FILE_ZONE_SIZE                    GIGABYTE
#define MAX_FILE_ZONES                            (TERABYTE / DEFAULT_FILE_ZONE_SIZE)

#endif // __CONFIG_ARGS_H__

<|endoftext|>


<commit_before>/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkFont.h"
#include "SkTypeface.h"
#include "SkUtils.h"

static SkTypeface* ref_or_default(SkTypeface* face) {
    return face ? SkRef(face) : SkTypeface::RefDefault();
}

SkFont::SkFont(SkTypeface* face, SkScalar size, SkScalar scaleX, SkScalar skewX, MaskType mt,
               uint32_t flags)
    : fTypeface(ref_or_default(face))
    , fSize(size)
    , fScaleX(scaleX)
    , fSkewX(skewX)
    , fFlags(flags)
    , fMaskType(SkToU8(mt))
{
    SkASSERT(size > 0);
    SkASSERT(scaleX > 0);
    SkASSERT(SkScalarIsFinite(skewX));
    SkASSERT(0 == (flags & ~kAllFlags));
}

SkFont* SkFont::Create(SkTypeface* face, SkScalar size, SkScalar scaleX, SkScalar skewX,
                       MaskType mt, uint32_t flags) {
    if (size <= 0 || !SkScalarIsFinite(size)) {
        return NULL;
    }
    if (scaleX <= 0 || !SkScalarIsFinite(scaleX)) {
        return NULL;
    }
    if (!SkScalarIsFinite(skewX)) {
        return NULL;
    }
    flags &= kAllFlags;
    return SkNEW_ARGS(SkFont, (face, size, scaleX, skewX, mt, flags));
}

SkFont* SkFont::Create(SkTypeface* face, SkScalar size, MaskType mt, uint32_t flags) {
    return SkFont::Create(face, size, 1, 0, mt, flags);
}

SkFont* SkFont::cloneWithSize(SkScalar newSize) const {
    return SkFont::Create(this->getTypeface(), newSize, this->getScaleX(), this->getSkewX(),
                          this->getMaskType(), this->getFlags());
}

///////////////////////////////////////////////////////////////////////////////////////////////////

SkFont::~SkFont() {
    SkSafeUnref(fTypeface);
}

int SkFont::textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding,
                         uint16_t glyphs[], int maxGlyphCount) const {
    if (0 == byteLength) {
        return 0;
    }

    SkASSERT(text);

    int count;
    switch (encoding) {
        case kUTF8_SkTextEncoding:
            count = SkUTF8_CountUnichars((const char*)text, byteLength);
            break;
        case kUTF16_SkTextEncoding:
            count = SkUTF16_CountUnichars((const uint16_t*)text, SkToInt(byteLength >> 1));
            break;
        case kUTF32_SkTextEncoding:
            count = SkToInt(byteLength >> 2);
            break;
        case kGlyphID_SkTextEncoding:
            count = SkToInt(byteLength >> 1);
            break;
    }
    if (NULL == glyphs) {
        return count;
    }

    // TODO: unify/eliminate SkTypeface::Encoding with SkTextEncoding
    SkTypeface::Encoding typeface_encoding;
    switch (encoding) {
        case kUTF8_SkTextEncoding:
            typeface_encoding = SkTypeface::kUTF8_Encoding;
            break;
        case kUTF16_SkTextEncoding:
            typeface_encoding = SkTypeface::kUTF16_Encoding;
            break;
        case kUTF32_SkTextEncoding:
            typeface_encoding = SkTypeface::kUTF32_Encoding;
            break;
        case kGlyphID_SkTextEncoding:
            // we can early exit, since we already have glyphIDs
            memcpy(glyphs, text, count << 1);
            return count;
    }

    (void)fTypeface->charsToGlyphs(text, typeface_encoding, glyphs, count);
    return count;
}

SkScalar SkFont::measureText(const void* text, size_t byteLength, SkTextEncoding encoding) const {
    // TODO: need access to the cache
    return -1;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

#include "SkPaint.h"

SkFont* SkFont::Testing_CreateFromPaint(const SkPaint& paint) {
    uint32_t flags = 0;
    if (paint.isVerticalText()) {
        flags |= kVertical_Flag;
    }
    if (paint.isEmbeddedBitmapText()) {
        flags |= kEmbeddedBitmaps_Flag;
    }
    if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
        flags |= kGenA8FromLCD_Flag;
    }
    if (paint.isFakeBoldText()) {
        flags |= kEmbolden_Flag;
    }

    if (SkPaint::kFull_Hinting == paint.getHinting()) {
        flags |= kEnableByteCodeHints_Flag;
    }
    if (paint.isAutohinted()) {
        flags |= kEnableAutoHints_Flag;
    }
    if (paint.isSubpixelText() || paint.isLinearText()) {
        // this is our default
    } else {
        flags |= kUseNonlinearMetrics_Flag;
    }

    MaskType maskType = SkFont::kBW_MaskType;
    if (paint.isAntiAlias()) {
        maskType = paint.isLCDRenderText() ? kLCD_MaskType : kA8_MaskType;
    }

    return Create(paint.getTypeface(),
                  paint.getTextSize(), paint.getTextScaleX(), paint.getTextSkewX(),
                  maskType, flags);
}
<commit_msg>fix warning for uninitialized count<commit_after>/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkFont.h"
#include "SkTypeface.h"
#include "SkUtils.h"

static SkTypeface* ref_or_default(SkTypeface* face) {
    return face ? SkRef(face) : SkTypeface::RefDefault();
}

SkFont::SkFont(SkTypeface* face, SkScalar size, SkScalar scaleX, SkScalar skewX, MaskType mt,
               uint32_t flags)
    : fTypeface(ref_or_default(face))
    , fSize(size)
    , fScaleX(scaleX)
    , fSkewX(skewX)
    , fFlags(flags)
    , fMaskType(SkToU8(mt))
{
    SkASSERT(size > 0);
    SkASSERT(scaleX > 0);
    SkASSERT(SkScalarIsFinite(skewX));
    SkASSERT(0 == (flags & ~kAllFlags));
}

SkFont* SkFont::Create(SkTypeface* face, SkScalar size, SkScalar scaleX, SkScalar skewX,
                       MaskType mt, uint32_t flags) {
    if (size <= 0 || !SkScalarIsFinite(size)) {
        return NULL;
    }
    if (scaleX <= 0 || !SkScalarIsFinite(scaleX)) {
        return NULL;
    }
    if (!SkScalarIsFinite(skewX)) {
        return NULL;
    }
    flags &= kAllFlags;
    return SkNEW_ARGS(SkFont, (face, size, scaleX, skewX, mt, flags));
}

SkFont* SkFont::Create(SkTypeface* face, SkScalar size, MaskType mt, uint32_t flags) {
    return SkFont::Create(face, size, 1, 0, mt, flags);
}

SkFont* SkFont::cloneWithSize(SkScalar newSize) const {
    return SkFont::Create(this->getTypeface(), newSize, this->getScaleX(), this->getSkewX(),
                          this->getMaskType(), this->getFlags());
}

///////////////////////////////////////////////////////////////////////////////////////////////////

SkFont::~SkFont() {
    SkSafeUnref(fTypeface);
}

int SkFont::textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding,
                         uint16_t glyphs[], int maxGlyphCount) const {
    if (0 == byteLength) {
        return 0;
    }

    SkASSERT(text);

    int count = 0;  // fix uninitialized warning (even though the switch is complete!)

    switch (encoding) {
        case kUTF8_SkTextEncoding:
            count = SkUTF8_CountUnichars((const char*)text, byteLength);
            break;
        case kUTF16_SkTextEncoding:
            count = SkUTF16_CountUnichars((const uint16_t*)text, SkToInt(byteLength >> 1));
            break;
        case kUTF32_SkTextEncoding:
            count = SkToInt(byteLength >> 2);
            break;
        case kGlyphID_SkTextEncoding:
            count = SkToInt(byteLength >> 1);
            break;
    }
    if (NULL == glyphs) {
        return count;
    }

    // TODO: unify/eliminate SkTypeface::Encoding with SkTextEncoding
    SkTypeface::Encoding typeface_encoding;
    switch (encoding) {
        case kUTF8_SkTextEncoding:
            typeface_encoding = SkTypeface::kUTF8_Encoding;
            break;
        case kUTF16_SkTextEncoding:
            typeface_encoding = SkTypeface::kUTF16_Encoding;
            break;
        case kUTF32_SkTextEncoding:
            typeface_encoding = SkTypeface::kUTF32_Encoding;
            break;
        case kGlyphID_SkTextEncoding:
            // we can early exit, since we already have glyphIDs
            memcpy(glyphs, text, count << 1);
            return count;
    }

    (void)fTypeface->charsToGlyphs(text, typeface_encoding, glyphs, count);
    return count;
}

SkScalar SkFont::measureText(const void* text, size_t byteLength, SkTextEncoding encoding) const {
    // TODO: need access to the cache
    return -1;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

#include "SkPaint.h"

SkFont* SkFont::Testing_CreateFromPaint(const SkPaint& paint) {
    uint32_t flags = 0;
    if (paint.isVerticalText()) {
        flags |= kVertical_Flag;
    }
    if (paint.isEmbeddedBitmapText()) {
        flags |= kEmbeddedBitmaps_Flag;
    }
    if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
        flags |= kGenA8FromLCD_Flag;
    }
    if (paint.isFakeBoldText()) {
        flags |= kEmbolden_Flag;
    }

    if (SkPaint::kFull_Hinting == paint.getHinting()) {
        flags |= kEnableByteCodeHints_Flag;
    }
    if (paint.isAutohinted()) {
        flags |= kEnableAutoHints_Flag;
    }
    if (paint.isSubpixelText() || paint.isLinearText()) {
        // this is our default
    } else {
        flags |= kUseNonlinearMetrics_Flag;
    }

    MaskType maskType = SkFont::kBW_MaskType;
    if (paint.isAntiAlias()) {
        maskType = paint.isLCDRenderText() ? kLCD_MaskType : kA8_MaskType;
    }

    return Create(paint.getTypeface(),
                  paint.getTextSize(), paint.getTextScaleX(), paint.getTextSkewX(),
                  maskType, flags);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ali Saidi
 */

#ifndef __CPU_CPUEVENT_HH__
#define __CPU_CPUEVENT_HH__

#include <vector>
#include "sim/eventq.hh"

class ThreadContext;

/** This class creates a global list of events that need a pointer to a
 * thread context. When a switchover takes place the events can be migrated
 * to the new thread context, otherwise you could have a wake timer interrupt
 * go off on a switched out cpu or other unfortunate events. This object MUST be
 * dynamically allocated to avoid it being deleted after a cpu switch happens.
 * */
class CpuEvent : public Event
{
  private:
    /** type of global list of cpu events. */
    typedef std::vector<CpuEvent *> CpuEventList;

    /** Static list of cpu events that is searched every time a cpu switch
     * happens. */
    static CpuEventList cpuEventList;

    /** The thread context that is switched to the new cpus. */
    ThreadContext *tc;

  public:
    CpuEvent(EventQueue *q, ThreadContext *_tc, Priority p = Default_Pri)
        : Event(q, p), tc(_tc)
    { cpuEventList.push_back(this); }

    /** delete the cpu event from the global list. */
    ~CpuEvent();

    /** Update all events switching old tc to new tc.
     * @param oldTc the old thread context we are switching from
     * @param newTc the new thread context we are switching to.
     */
    static void replaceThreadContext(ThreadContext *oldTc,
                                     ThreadContext *newTc);
};

template <class T, void (T::* F)(ThreadContext *tc)>
class CpuEventWrapper : public CpuEvent
{
  private:
    T *object;

  public:
    CpuEventWrapper(T *obj, ThreadContext *_tc, EventQueue *q = &mainEventQueue,
            Priority p = Default_Pri)
        : CpuEvent(q, _tc, p), object(obj)
    { }
    void process() { (object->*F)(tc); }
};

#endif // __CPU_CPUEVENT_HH__

<commit_msg>The tc needs to be protected instead of private so that the CpuEventWrapper can access it.<commit_after>/*
 * Copyright (c) 2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ali Saidi
 */

#ifndef __CPU_CPUEVENT_HH__
#define __CPU_CPUEVENT_HH__

#include <vector>
#include "sim/eventq.hh"

class ThreadContext;

/** This class creates a global list of events that need a pointer to a
 * thread context. When a switchover takes place the events can be migrated
 * to the new thread context, otherwise you could have a wake timer interrupt
 * go off on a switched out cpu or other unfortunate events. This object MUST be
 * dynamically allocated to avoid it being deleted after a cpu switch happens.
 * */
class CpuEvent : public Event
{
  protected:
    /** type of global list of cpu events. */
    typedef std::vector<CpuEvent *> CpuEventList;

    /** Static list of cpu events that is searched every time a cpu switch
     * happens. */
    static CpuEventList cpuEventList;

    /** The thread context that is switched to the new cpus. */
    ThreadContext *tc;

  public:
    CpuEvent(EventQueue *q, ThreadContext *_tc, Priority p = Default_Pri)
        : Event(q, p), tc(_tc)
    { cpuEventList.push_back(this); }

    /** delete the cpu event from the global list. */
    ~CpuEvent();

    /** Update all events switching old tc to new tc.
     * @param oldTc the old thread context we are switching from
     * @param newTc the new thread context we are switching to.
     */
    static void replaceThreadContext(ThreadContext *oldTc,
                                     ThreadContext *newTc);
};

template <class T, void (T::* F)(ThreadContext *tc)>
class CpuEventWrapper : public CpuEvent
{
  private:
    T *object;

  public:
    CpuEventWrapper(T *obj, ThreadContext *_tc, EventQueue *q = &mainEventQueue,
            Priority p = Default_Pri)
        : CpuEvent(q, _tc, p), object(obj)
    { }
    void process() { (object->*F)(tc); }
};

#endif // __CPU_CPUEVENT_HH__

<|endoftext|>


<commit_before>// Copyright (c) 2011-2013 Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <list>
#include <map>
#include <stack>
#include <string>

#include <amx/amx.h>
#include <amx/amxaux.h>

#include "amxdebuginfo.h"
#include "amxerror.h"
#include "amxpathfinder.h"
#include "amxscript.h"
#include "amxstacktrace.h"
#include "compiler.h"
#include "configreader.h"
#include "crashdetect.h"
#include "fileutils.h"
#include "logprintf.h"
#include "npcall.h"
#include "os.h"
#include "stacktrace.h"

#define AMX_EXEC_GDK (-10)

bool CrashDetect::error_detected_ = false;
std::stack<NPCall*> CrashDetect::np_calls_;;

// static
void CrashDetect::OnException(void *context) {
	if (!np_calls_.empty()) {
		CrashDetect::Get(np_calls_.top()->amx())->HandleException();
	} else {
		logprintf("Server crashed due to an unknown error");
	}
	PrintSystemBacktrace(context);
}

// static
void CrashDetect::OnInterrupt(void *context) {
	if (!np_calls_.empty()) {
		CrashDetect::Get(np_calls_.top()->amx())->HandleInterrupt();
	} else {
		logprintf("Server received interrupt signal");
	}
	PrintSystemBacktrace(context);
}

// static
void CrashDetect::DieOrContinue() {
	if (server_cfg_.GetOption("die_on_error", false)) {
		logprintf("Aborting...");
		std::exit(EXIT_FAILURE);
	}
}

CrashDetect::CrashDetect(AMX *amx)
	: AMXService<CrashDetect>(amx)
	, amx_(amx)
	, prev_callback_(0)
	, server_cfg_("server.cfg")
{
}

int CrashDetect::Load() {
	AMXPathFinder pathFinder;
	pathFinder.AddSearchPath("gamemodes");
	pathFinder.AddSearchPath("filterscripts");

	// Read a list of additional search paths from AMX_PATH.
	const char *AMX_PATH = getenv("AMX_PATH");
	if (AMX_PATH != 0) {
		std::string var(AMX_PATH);
		std::string path;
		std::string::size_type begin = 0;
		while (begin < var.length()) {
			std::string::size_type end = var.find(fileutils::kNativePathListSepChar, begin);
			if (end == std::string::npos) {
				end = var.length();
			}
			path.assign(var.begin() + begin, var.begin() + end);
			if (!path.empty()) {
				pathFinder.AddSearchPath(path);
			}
			begin = end + 1;
		}
	}

	amx_path_ = pathFinder.FindAMX(this->amx());
	amx_name_ = fileutils::GetFileName(amx_path_);

	if (!amx_path_.empty() && AMXDebugInfo::IsPresent(this->amx())) {
		debug_info_.Load(amx_path_);
	}

	amx_.DisableSysreqD();
	prev_callback_ = amx_.GetCallback();

	return AMX_ERR_NONE;
}

int CrashDetect::Unload() {
	return AMX_ERR_NONE;
}

int CrashDetect::DoAmxCallback(cell index, cell *result, cell *params) {
	NPCall call = NPCall::Native(amx(), index);
	np_calls_.push(&call);
	int error = prev_callback_(amx(), index, result, params);
	np_calls_.pop();
	return error;
}

int CrashDetect::DoAmxExec(cell *retval, int index) {
	NPCall call = NPCall::Public(amx(), index);
	np_calls_.push(&call);

	int error = ::amx_Exec(amx(), retval, index);
	if (error != AMX_ERR_NONE && !error_detected_) {
		HandleExecError(index, error);
	} else {
		error_detected_ = false;
	}

	np_calls_.pop();
	return error;
}

void CrashDetect::HandleExecError(int index, const AMXError &error) {
	CrashDetect::error_detected_ = true;

	if (error.code() == AMX_ERR_INDEX && index == AMX_EXEC_GDK) {
		return;
	}

	logprintf("Run time error %d: \"%s\"", error, error.string());

	switch (error.code()) {
		case AMX_ERR_BOUNDS: {
			cell bound = *(reinterpret_cast<cell*>(amx_.GetCode() + amx_.GetCip() + sizeof(cell)));
			cell index = amx_.GetPri();
			if (index < 0) {
				logprintf(" Accessing element at negative index %d", index);
			} else {
				logprintf(" Accessing element at index %d past array upper bound %d", index, bound);
			}
			break;
		}
		case AMX_ERR_NOTFOUND: {
			AMX_FUNCSTUBNT *natives = amx_.GetNatives();
			int num_natives = amx_.GetNumNatives();
			for (int i = 0; i < num_natives; ++i) {
				if (natives[i].address == 0) {
					logprintf(" %s", amx_.GetName(natives[i].nameofs));
				}
			}
			break;
		}
		case AMX_ERR_STACKERR:
			logprintf(" Stack pointer (STK) is 0x%X, heap pointer (HEA) is 0x%X", amx_.GetStk(), amx_.GetHea());
			break;
		case AMX_ERR_STACKLOW:
			logprintf(" Stack pointer (STK) is 0x%X, stack top (STP) is 0x%X", amx_.GetStk(), amx_.GetStp());
			break;
		case AMX_ERR_HEAPLOW:
			logprintf(" Heap pointer (HEA) is 0x%X, heap bottom (HLW) is 0x%X", amx_.GetHea(), amx_.GetHlw());
			break;
		case AMX_ERR_INVINSTR: {
			cell opcode = *(reinterpret_cast<cell*>(amx_.GetCode() + amx_.GetCip()));
			logprintf(" Unknown opcode 0x%x at address 0x%08X", opcode , amx_.GetCip());
			break;
		}
		case AMX_ERR_NATIVE: {
			static const int sysreq_c = 123;
			cell *ip = reinterpret_cast<cell*>(amx_.GetCode() + amx_.GetCip());
			cell opcode = *(ip - 2);
			if (opcode == sysreq_c) {
				cell index = *(ip - 1);
				logprintf(" %s", amx_.GetNativeName(index));
			}
			break;
		}
	}

	if (error.code() != AMX_ERR_NOTFOUND &&
		error.code() != AMX_ERR_INDEX &&
		error.code() != AMX_ERR_CALLBACK &&
		error.code() != AMX_ERR_INIT)
	{
		PrintAmxBacktrace();
	}

	std::string command = server_cfg_.GetOption("run_on_error", std::string());
	if (!command.empty()) {
		std::system(command.c_str());
	}

	DieOrContinue();
}

void CrashDetect::HandleException() {
	logprintf("Server crashed while executing %s", amx_name_.c_str());
	PrintAmxBacktrace();
}

void CrashDetect::HandleInterrupt() {
	logprintf("Server received interrupt signal while executing %s", amx_name_.c_str());
	PrintAmxBacktrace();
}

// static
void CrashDetect::PrintAmxBacktrace() {
	if (np_calls_.empty()) {
		return;
	}

	AMXScript topAmx = np_calls_.top()->amx();

	cell frm = topAmx.GetFrm();
	cell cip = topAmx.GetCip();
	int level = 0;

	if (cip == 0) {
		return;
	}

	logprintf("AMX backtrace:");

	std::stack<NPCall*> np_calls = np_calls_;

	while (!np_calls.empty() && cip != 0) {
		const NPCall *call = np_calls.top();
		AMXScript amx = call->amx();

		// We don't trace calls across AMX bounds i.e. outside of top-level
		// function's AMX instance.
		if (amx != topAmx) {
			assert(level != 0);
			break;
		}

		if (call->IsNative()) {
			AMX_NATIVE address = reinterpret_cast<AMX_NATIVE>(amx.GetNativeAddr(call->index()));
			if (address != 0) {
				std::string module = fileutils::GetFileName(os::GetModulePathFromAddr((void*)address));
				std::string from = " from " + module;
				if (module.empty()) {
					from.clear();
				}
				const char *name = amx.GetNativeName(call->index());
				if (name != 0) {
					logprintf("#%d native %s () [%08x]%s", level++, name, address, from.c_str());
				}
			}
		} else if (call->IsPublic()) {
			AMXDebugInfo &debug_info = CrashDetect::Get(amx)->debug_info_;

			amx.PushStack(cip); // push return address
			amx.PushStack(frm); // push frame pointer

			std::list<AMXStackFrame> frames;
			{
				AMXStackTrace trace(amx, amx.GetStk(), &debug_info);
				do {
					AMXStackFrame frame = trace.GetFrame();
					if (frame) {
						frames.push_back(frame);
					} else {
						break;
					}
				} while (trace.Next());
			}

			frm = amx.PopStack(); // pop frame pointer
			cip = amx.PopStack(); // pop return address

			if (frames.empty()) {
				ucell ep_addr = amx.GetPublicAddr(call->index());
				frames.push_front(AMXStackFrame(amx, frm, 0, ep_addr, &debug_info));
			} else {
				if (!debug_info.IsLoaded()) {
					AMXStackFrame &bottom = frames.back();
					bottom = AMXStackFrame(amx,
						bottom.GetFrameAddr(),
						bottom.GetRetAddr(),
						amx.GetPublicAddr(call->index()),
						&debug_info);
				}
			}

			std::string &amx_name = CrashDetect::Get(amx)->amx_name_;
			for (std::list<AMXStackFrame>::const_iterator iterator = frames.begin();
					iterator != frames.end(); ++iterator)
			{
				std::string from = " from " + amx_name;
				if (amx_name.empty() || debug_info.IsLoaded()) {
					from.clear();
				}
				logprintf("#%d %s%s", level++, iterator->AsString().c_str(), from.c_str());
			}

			frm = call->frm();
			cip = call->cip();
		}

		np_calls.pop();
	}
}

// static
void CrashDetect::PrintSystemBacktrace(void *context) {
	logprintf("System backtrace:");

	int level = 0;

	StackTrace trace(context);
	std::deque<StackFrame> frames = trace.GetFrames();

	for (std::deque<StackFrame>::const_iterator iterator = frames.begin();
			iterator != frames.end(); ++iterator) {
		const StackFrame &frame = *iterator;

		std::string module = os::GetModulePathFromAddr(frame.GetRetAddr());
		std::string from = " from " + module;
		if (module.empty()) {
			from.clear();
		}

		logprintf("#%d %s%s", level++, frame.AsString().c_str(), from.c_str());
	}
}

// static
void CrashDetect::logprintf(const char *format, ...) {
	std::va_list va;
	va_start(va, format);

	std::string new_format;
	new_format.append("[debug] ");
	new_format.append(format);

	vlogprintf(new_format.c_str(), va);
	va_end(va);
}
<commit_msg>Add sanity check for AMX_ERR_BOUNDS error<commit_after>// Copyright (c) 2011-2013 Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <list>
#include <map>
#include <stack>
#include <string>

#include <amx/amx.h>
#include <amx/amxaux.h>

#include "amxdebuginfo.h"
#include "amxerror.h"
#include "amxpathfinder.h"
#include "amxscript.h"
#include "amxstacktrace.h"
#include "compiler.h"
#include "configreader.h"
#include "crashdetect.h"
#include "fileutils.h"
#include "logprintf.h"
#include "npcall.h"
#include "os.h"
#include "stacktrace.h"

#define AMX_EXEC_GDK (-10)

bool CrashDetect::error_detected_ = false;
std::stack<NPCall*> CrashDetect::np_calls_;;

// static
void CrashDetect::OnException(void *context) {
	if (!np_calls_.empty()) {
		CrashDetect::Get(np_calls_.top()->amx())->HandleException();
	} else {
		logprintf("Server crashed due to an unknown error");
	}
	PrintSystemBacktrace(context);
}

// static
void CrashDetect::OnInterrupt(void *context) {
	if (!np_calls_.empty()) {
		CrashDetect::Get(np_calls_.top()->amx())->HandleInterrupt();
	} else {
		logprintf("Server received interrupt signal");
	}
	PrintSystemBacktrace(context);
}

// static
void CrashDetect::DieOrContinue() {
	if (server_cfg_.GetOption("die_on_error", false)) {
		logprintf("Aborting...");
		std::exit(EXIT_FAILURE);
	}
}

CrashDetect::CrashDetect(AMX *amx)
	: AMXService<CrashDetect>(amx)
	, amx_(amx)
	, prev_callback_(0)
	, server_cfg_("server.cfg")
{
}

int CrashDetect::Load() {
	AMXPathFinder pathFinder;
	pathFinder.AddSearchPath("gamemodes");
	pathFinder.AddSearchPath("filterscripts");

	// Read a list of additional search paths from AMX_PATH.
	const char *AMX_PATH = getenv("AMX_PATH");
	if (AMX_PATH != 0) {
		std::string var(AMX_PATH);
		std::string path;
		std::string::size_type begin = 0;
		while (begin < var.length()) {
			std::string::size_type end = var.find(fileutils::kNativePathListSepChar, begin);
			if (end == std::string::npos) {
				end = var.length();
			}
			path.assign(var.begin() + begin, var.begin() + end);
			if (!path.empty()) {
				pathFinder.AddSearchPath(path);
			}
			begin = end + 1;
		}
	}

	amx_path_ = pathFinder.FindAMX(this->amx());
	amx_name_ = fileutils::GetFileName(amx_path_);

	if (!amx_path_.empty() && AMXDebugInfo::IsPresent(this->amx())) {
		debug_info_.Load(amx_path_);
	}

	amx_.DisableSysreqD();
	prev_callback_ = amx_.GetCallback();

	return AMX_ERR_NONE;
}

int CrashDetect::Unload() {
	return AMX_ERR_NONE;
}

int CrashDetect::DoAmxCallback(cell index, cell *result, cell *params) {
	NPCall call = NPCall::Native(amx(), index);
	np_calls_.push(&call);
	int error = prev_callback_(amx(), index, result, params);
	np_calls_.pop();
	return error;
}

int CrashDetect::DoAmxExec(cell *retval, int index) {
	NPCall call = NPCall::Public(amx(), index);
	np_calls_.push(&call);

	int error = ::amx_Exec(amx(), retval, index);
	if (error != AMX_ERR_NONE && !error_detected_) {
		HandleExecError(index, error);
	} else {
		error_detected_ = false;
	}

	np_calls_.pop();
	return error;
}

void CrashDetect::HandleExecError(int index, const AMXError &error) {
	CrashDetect::error_detected_ = true;

	if (error.code() == AMX_ERR_INDEX && index == AMX_EXEC_GDK) {
		return;
	}

	logprintf("Run time error %d: \"%s\"", error, error.string());

	switch (error.code()) {
		case AMX_ERR_BOUNDS: {
			static const int bounds = 121;
			cell *ip = reinterpret_cast<cell*>(amx_.GetCode() + amx_.GetCip());
			cell opcode = *ip;
			if (opcode == bounds) {
				cell bound = *(ip + 1);
				cell index = amx_.GetPri();
				if (index < 0) {
					logprintf(" Accessing element at negative index %d", index);
				} else {
					logprintf(" Accessing element at index %d past array upper bound %d", index, bound);
				}
			}
			break;
		}
		case AMX_ERR_NOTFOUND: {
			AMX_FUNCSTUBNT *natives = amx_.GetNatives();
			int num_natives = amx_.GetNumNatives();
			for (int i = 0; i < num_natives; ++i) {
				if (natives[i].address == 0) {
					logprintf(" %s", amx_.GetName(natives[i].nameofs));
				}
			}
			break;
		}
		case AMX_ERR_STACKERR:
			logprintf(" Stack pointer (STK) is 0x%X, heap pointer (HEA) is 0x%X", amx_.GetStk(), amx_.GetHea());
			break;
		case AMX_ERR_STACKLOW:
			logprintf(" Stack pointer (STK) is 0x%X, stack top (STP) is 0x%X", amx_.GetStk(), amx_.GetStp());
			break;
		case AMX_ERR_HEAPLOW:
			logprintf(" Heap pointer (HEA) is 0x%X, heap bottom (HLW) is 0x%X", amx_.GetHea(), amx_.GetHlw());
			break;
		case AMX_ERR_INVINSTR: {
			cell opcode = *(reinterpret_cast<cell*>(amx_.GetCode() + amx_.GetCip()));
			logprintf(" Unknown opcode 0x%x at address 0x%08X", opcode , amx_.GetCip());
			break;
		}
		case AMX_ERR_NATIVE: {
			static const int op_sysreq_c = 123;
			cell *ip = reinterpret_cast<cell*>(amx_.GetCode() + amx_.GetCip());
			cell opcode = *(ip - 2);
			if (opcode == op_sysreq_c) {
				cell index = *(ip - 1);
				logprintf(" %s", amx_.GetNativeName(index));
			}
			break;
		}
	}

	if (error.code() != AMX_ERR_NOTFOUND &&
		error.code() != AMX_ERR_INDEX &&
		error.code() != AMX_ERR_CALLBACK &&
		error.code() != AMX_ERR_INIT)
	{
		PrintAmxBacktrace();
	}

	std::string command = server_cfg_.GetOption("run_on_error", std::string());
	if (!command.empty()) {
		std::system(command.c_str());
	}

	DieOrContinue();
}

void CrashDetect::HandleException() {
	logprintf("Server crashed while executing %s", amx_name_.c_str());
	PrintAmxBacktrace();
}

void CrashDetect::HandleInterrupt() {
	logprintf("Server received interrupt signal while executing %s", amx_name_.c_str());
	PrintAmxBacktrace();
}

// static
void CrashDetect::PrintAmxBacktrace() {
	if (np_calls_.empty()) {
		return;
	}

	AMXScript topAmx = np_calls_.top()->amx();

	cell frm = topAmx.GetFrm();
	cell cip = topAmx.GetCip();
	int level = 0;

	if (cip == 0) {
		return;
	}

	logprintf("AMX backtrace:");

	std::stack<NPCall*> np_calls = np_calls_;

	while (!np_calls.empty() && cip != 0) {
		const NPCall *call = np_calls.top();
		AMXScript amx = call->amx();

		// We don't trace calls across AMX bounds i.e. outside of top-level
		// function's AMX instance.
		if (amx != topAmx) {
			assert(level != 0);
			break;
		}

		if (call->IsNative()) {
			AMX_NATIVE address = reinterpret_cast<AMX_NATIVE>(amx.GetNativeAddr(call->index()));
			if (address != 0) {
				std::string module = fileutils::GetFileName(os::GetModulePathFromAddr((void*)address));
				std::string from = " from " + module;
				if (module.empty()) {
					from.clear();
				}
				const char *name = amx.GetNativeName(call->index());
				if (name != 0) {
					logprintf("#%d native %s () [%08x]%s", level++, name, address, from.c_str());
				}
			}
		} else if (call->IsPublic()) {
			AMXDebugInfo &debug_info = CrashDetect::Get(amx)->debug_info_;

			amx.PushStack(cip); // push return address
			amx.PushStack(frm); // push frame pointer

			std::list<AMXStackFrame> frames;
			{
				AMXStackTrace trace(amx, amx.GetStk(), &debug_info);
				do {
					AMXStackFrame frame = trace.GetFrame();
					if (frame) {
						frames.push_back(frame);
					} else {
						break;
					}
				} while (trace.Next());
			}

			frm = amx.PopStack(); // pop frame pointer
			cip = amx.PopStack(); // pop return address

			if (frames.empty()) {
				ucell ep_addr = amx.GetPublicAddr(call->index());
				frames.push_front(AMXStackFrame(amx, frm, 0, ep_addr, &debug_info));
			} else {
				if (!debug_info.IsLoaded()) {
					AMXStackFrame &bottom = frames.back();
					bottom = AMXStackFrame(amx,
						bottom.GetFrameAddr(),
						bottom.GetRetAddr(),
						amx.GetPublicAddr(call->index()),
						&debug_info);
				}
			}

			std::string &amx_name = CrashDetect::Get(amx)->amx_name_;
			for (std::list<AMXStackFrame>::const_iterator iterator = frames.begin();
					iterator != frames.end(); ++iterator)
			{
				std::string from = " from " + amx_name;
				if (amx_name.empty() || debug_info.IsLoaded()) {
					from.clear();
				}
				logprintf("#%d %s%s", level++, iterator->AsString().c_str(), from.c_str());
			}

			frm = call->frm();
			cip = call->cip();
		}

		np_calls.pop();
	}
}

// static
void CrashDetect::PrintSystemBacktrace(void *context) {
	logprintf("System backtrace:");

	int level = 0;

	StackTrace trace(context);
	std::deque<StackFrame> frames = trace.GetFrames();

	for (std::deque<StackFrame>::const_iterator iterator = frames.begin();
			iterator != frames.end(); ++iterator) {
		const StackFrame &frame = *iterator;

		std::string module = os::GetModulePathFromAddr(frame.GetRetAddr());
		std::string from = " from " + module;
		if (module.empty()) {
			from.clear();
		}

		logprintf("#%d %s%s", level++, frame.AsString().c_str(), from.c_str());
	}
}

// static
void CrashDetect::logprintf(const char *format, ...) {
	std::va_list va;
	va_start(va, format);

	std::string new_format;
	new_format.append("[debug] ");
	new_format.append(format);

	vlogprintf(new_format.c_str(), va);
	va_end(va);
}
<|endoftext|>