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

 QGroundControl Open Source Ground Control Station

 (c) 2009, 2014 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 PX4 Firmware Upgrade operations which occur on a seperate thread.
///     @author Don Gagne <don@thegagnes.com>

#include "PX4FirmwareUpgradeThread.h"
#include "Bootloader.h"
#include "QGCLoggingCategory.h"
#include "QGC.h"

#include <QTimer>
#include <QDebug>
#include <QSerialPort>

PX4FirmwareUpgradeThreadWorker::PX4FirmwareUpgradeThreadWorker(PX4FirmwareUpgradeThreadController* controller) :
    _controller(controller),
    _bootloader(NULL),
    _bootloaderPort(NULL),
    _timerRetry(NULL),
    _foundBoard(false),
    _findBoardFirstAttempt(true)
{
    Q_ASSERT(_controller);

    connect(_controller, &PX4FirmwareUpgradeThreadController::_initThreadWorker,            this, &PX4FirmwareUpgradeThreadWorker::_init);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_startFindBoardLoopOnThread,  this, &PX4FirmwareUpgradeThreadWorker::_startFindBoardLoop);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_flashOnThread,               this, &PX4FirmwareUpgradeThreadWorker::_flash);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_rebootOnThread,              this, &PX4FirmwareUpgradeThreadWorker::_reboot);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_cancel,                      this, &PX4FirmwareUpgradeThreadWorker::_cancel);
}

PX4FirmwareUpgradeThreadWorker::~PX4FirmwareUpgradeThreadWorker()
{
    if (_bootloaderPort) {
        // deleteLater so delete happens on correct thread
        _bootloaderPort->deleteLater();
    }
}

/// @brief Initializes the PX4FirmwareUpgradeThreadWorker with the various child objects which must be created
///         on the worker thread.
void PX4FirmwareUpgradeThreadWorker::_init(void)
{
    // We create the timers here so that they are on the right thread

    Q_ASSERT(_timerRetry == NULL);
    _timerRetry = new QTimer(this);
    Q_CHECK_PTR(_timerRetry);
    _timerRetry->setSingleShot(true);
    _timerRetry->setInterval(_retryTimeout);
    connect(_timerRetry, &QTimer::timeout, this, &PX4FirmwareUpgradeThreadWorker::_findBoardOnce);

    Q_ASSERT(_bootloader == NULL);
    _bootloader = new Bootloader(this);
    connect(_bootloader, &Bootloader::updateProgress, this, &PX4FirmwareUpgradeThreadWorker::_updateProgress);
}

void PX4FirmwareUpgradeThreadWorker::_cancel(void)
{
    if (_bootloaderPort) {
        _bootloaderPort->close();
        _bootloaderPort->deleteLater();
        _bootloaderPort = NULL;
    }
}

void PX4FirmwareUpgradeThreadWorker::_startFindBoardLoop(void)
{
    _foundBoard = false;
    _findBoardFirstAttempt = true;
    _findBoardOnce();
}

void PX4FirmwareUpgradeThreadWorker::_findBoardOnce(void)
{
    qCDebug(FirmwareUpgradeVerboseLog) << "_findBoardOnce";

    QGCSerialPortInfo               portInfo;
    QGCSerialPortInfo::BoardType_t  boardType;

    if (_findBoardFromPorts(portInfo, boardType)) {
        if (!_foundBoard) {
            _foundBoard = true;
            _foundBoardPortInfo = portInfo;
            emit foundBoard(_findBoardFirstAttempt, portInfo, boardType);
            if (!_findBoardFirstAttempt) {
                if (boardType == QGCSerialPortInfo::BoardType3drRadio) {
                    _3drRadioForceBootloader(portInfo);
                    return;
                } else {
                    _findBootloader(portInfo, false /* radio mode */, true /* errorOnNotFound */);
                    return;
                }
            }
        }
    } else {
        if (_foundBoard) {
            _foundBoard = false;
            qCDebug(FirmwareUpgradeLog) << "Board gone";
            emit boardGone();
        } else if (_findBoardFirstAttempt) {
            emit noBoardFound();
        }
    }

    _findBoardFirstAttempt = false;
    _timerRetry->start();
}

bool PX4FirmwareUpgradeThreadWorker::_findBoardFromPorts(QGCSerialPortInfo& portInfo, QGCSerialPortInfo::BoardType_t& boardType)
{
    foreach (QGCSerialPortInfo info, QGCSerialPortInfo::availablePorts()) {
        qCDebug(FirmwareUpgradeVerboseLog) << "Serial Port --------------";
        qCDebug(FirmwareUpgradeVerboseLog) << "\tboard type" << info.boardType();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tport name:" << info.portName();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tdescription:" << info.description();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tsystem location:" << info.systemLocation();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tvendor ID:" << info.vendorIdentifier();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tproduct ID:" << info.productIdentifier();

        boardType = info.boardType();
        if (boardType != QGCSerialPortInfo::BoardTypeUnknown) {
            portInfo = info;
            return true;
        }
    }

    return false;
}

void PX4FirmwareUpgradeThreadWorker::_3drRadioForceBootloader(const QGCSerialPortInfo& portInfo)
{
    // First make sure we can't get the bootloader

    if (_findBootloader(portInfo, true /* radio Mode */, false /* errorOnNotFound */)) {
        return;
    }

    // Couldn't find the bootloader. We'll need to reboot the radio into bootloader.

    QSerialPort port(portInfo);

    port.setBaudRate(QSerialPort::Baud57600);

    emit status("Putting radio into command mode");

    // Wait a little while for the USB port to initialize. 3DR Radio boot is really slow.
    QGC::SLEEP::msleep(2000);
    port.open(QIODevice::ReadWrite);

    if (!port.isOpen()) {
        emit error(QString("Unable to open port: %1 error: %2").arg(portInfo.systemLocation()).arg(port.errorString()));
        return;
    }

    // Put radio into command mode
    QGC::SLEEP::msleep(2000);
    port.write("+++", 3);
    if (!port.waitForReadyRead(1500)) {
        emit error("Unable to put radio into command mode");
        return;
    }
    QByteArray bytes = port.readAll();
    if (!bytes.contains("OK")) {
        qCDebug(FirmwareUpgradeLog) << bytes;
        emit error("Unable to put radio into command mode");
        return;
    }

    emit status("Rebooting radio to bootloader");

    port.write("AT&UPDATE\r\n");
    if (!port.waitForBytesWritten(1500)) {
        emit error("Unable to reboot radio (bytes written)");
        return;
    }
    if (!port.waitForReadyRead(1500)) {
        emit error("Unable to reboot radio (ready read)");
        return;
    }
    port.close();
    QGC::SLEEP::msleep(2000);

    // The bootloader should be waiting for us now

    _findBootloader(portInfo, true /* radio mode */, true /* errorOnNotFound */);
}

bool PX4FirmwareUpgradeThreadWorker::_findBootloader(const QGCSerialPortInfo& portInfo, bool radioMode, bool errorOnNotFound)
{
    qCDebug(FirmwareUpgradeLog) << "_findBootloader" << portInfo.systemLocation();

    uint32_t bootloaderVersion = 0;
    uint32_t boardID;
    uint32_t flashSize = 0;

    _bootloaderPort = new QextSerialPort(QextSerialPort::Polling);
    if (radioMode) {
        _bootloaderPort->setBaudRate(BAUD115200);
    }

    // Wait a little while for the USB port to initialize.
    bool openFailed = true;
    for (int i=0; i<10; i++) {
        if (_bootloader->open(_bootloaderPort, portInfo.systemLocation())) {
            openFailed = false;
            break;
        } else {
            QGC::SLEEP::msleep(100);
        }
    }

    if (radioMode) {
        QGC::SLEEP::msleep(2000);
    }

    if (openFailed) {
        qCDebug(FirmwareUpgradeLog) << "Bootloader open port failed:" << _bootloader->errorString();
        if (errorOnNotFound) {
            emit error(_bootloader->errorString());
        }
        _bootloaderPort->deleteLater();
        _bootloaderPort = NULL;
        return false;
    }

    if (_bootloader->sync(_bootloaderPort)) {
        bool success;

        if (radioMode) {
            success = _bootloader->get3DRRadioBoardId(_bootloaderPort, boardID);
        } else {
            success = _bootloader->getPX4BoardInfo(_bootloaderPort, bootloaderVersion, boardID, flashSize);
        }
        if (success) {
            qCDebug(FirmwareUpgradeLog) << "Found bootloader";
            emit foundBootloader(bootloaderVersion, boardID, flashSize);
            return true;
        }
    }

    _bootloaderPort->close();
    _bootloaderPort->deleteLater();
    _bootloaderPort = NULL;
    qCDebug(FirmwareUpgradeLog) << "Bootloader error:" << _bootloader->errorString();
    if (errorOnNotFound) {
        emit error(_bootloader->errorString());
    }

    return false;
}

void PX4FirmwareUpgradeThreadWorker::_reboot(void)
{
    if (_bootloaderPort) {
        if (_bootloaderPort->isOpen()) {
            _bootloader->reboot(_bootloaderPort);
        }
        _bootloaderPort->deleteLater();
        _bootloaderPort = NULL;
    }
}

void PX4FirmwareUpgradeThreadWorker::_flash(void)
{
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadWorker::_flash";

    if (_erase()) {
        emit status("Programming new version...");

        if (_bootloader->program(_bootloaderPort, _controller->image())) {
            qCDebug(FirmwareUpgradeLog) << "Program complete";
            emit status("Program complete");
        } else {
            _bootloaderPort->deleteLater();
            _bootloaderPort = NULL;
            qCDebug(FirmwareUpgradeLog) << "Program failed:" << _bootloader->errorString();
            emit error(_bootloader->errorString());
        }

        emit status("Verifying program...");

        if (_bootloader->verify(_bootloaderPort, _controller->image())) {
            qCDebug(FirmwareUpgradeLog) << "Verify complete";
            emit status("Verify complete");
        } else {
            qCDebug(FirmwareUpgradeLog) << "Verify failed:" << _bootloader->errorString();
            emit error(_bootloader->errorString());
        }
    }

    emit _reboot();

    emit flashComplete();
}

bool PX4FirmwareUpgradeThreadWorker::_erase(void)
{
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadWorker::_erase";

    emit eraseStarted();
    emit status("Erasing previous program...");

    if (_bootloader->erase(_bootloaderPort)) {
        qCDebug(FirmwareUpgradeLog) << "Erase complete";
        emit status("Erase complete");
        emit eraseComplete();
        return true;
    } else {
        qCDebug(FirmwareUpgradeLog) << "Erase failed:" << _bootloader->errorString();
        emit error(_bootloader->errorString());
        return false;
    }
}

PX4FirmwareUpgradeThreadController::PX4FirmwareUpgradeThreadController(QObject* parent) :
    QObject(parent)
{
    _worker = new PX4FirmwareUpgradeThreadWorker(this);
    Q_CHECK_PTR(_worker);

    _workerThread = new QThread(this);
    Q_CHECK_PTR(_workerThread);
    _worker->moveToThread(_workerThread);

    connect(_worker, &PX4FirmwareUpgradeThreadWorker::updateProgress,       this, &PX4FirmwareUpgradeThreadController::_updateProgress);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::foundBoard,           this, &PX4FirmwareUpgradeThreadController::_foundBoard);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::noBoardFound,         this, &PX4FirmwareUpgradeThreadController::_noBoardFound);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::boardGone,            this, &PX4FirmwareUpgradeThreadController::_boardGone);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::foundBootloader,      this, &PX4FirmwareUpgradeThreadController::_foundBootloader);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::bootloaderSyncFailed, this, &PX4FirmwareUpgradeThreadController::_bootloaderSyncFailed);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::error,                this, &PX4FirmwareUpgradeThreadController::_error);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::status,               this, &PX4FirmwareUpgradeThreadController::_status);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::eraseStarted,         this, &PX4FirmwareUpgradeThreadController::_eraseStarted);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::eraseComplete,        this, &PX4FirmwareUpgradeThreadController::_eraseComplete);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::flashComplete,        this, &PX4FirmwareUpgradeThreadController::_flashComplete);

    _workerThread->start();

    emit _initThreadWorker();
}

PX4FirmwareUpgradeThreadController::~PX4FirmwareUpgradeThreadController()
{
    _workerThread->quit();
    _workerThread->wait();

    delete _workerThread;
}

void PX4FirmwareUpgradeThreadController::startFindBoardLoop(void)
{
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadController::findBoard";
    emit _startFindBoardLoopOnThread();
}

void PX4FirmwareUpgradeThreadController::cancel(void)
{
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadController::cancel";
    emit _cancel();
}

void PX4FirmwareUpgradeThreadController::flash(const FirmwareImage* image)
{
    _image = image;
    emit _flashOnThread();
}
<commit_msg>Return after error<commit_after>/*=====================================================================

 QGroundControl Open Source Ground Control Station

 (c) 2009, 2014 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 PX4 Firmware Upgrade operations which occur on a seperate thread.
///     @author Don Gagne <don@thegagnes.com>

#include "PX4FirmwareUpgradeThread.h"
#include "Bootloader.h"
#include "QGCLoggingCategory.h"
#include "QGC.h"

#include <QTimer>
#include <QDebug>
#include <QSerialPort>

PX4FirmwareUpgradeThreadWorker::PX4FirmwareUpgradeThreadWorker(PX4FirmwareUpgradeThreadController* controller) :
    _controller(controller),
    _bootloader(NULL),
    _bootloaderPort(NULL),
    _timerRetry(NULL),
    _foundBoard(false),
    _findBoardFirstAttempt(true)
{
    Q_ASSERT(_controller);

    connect(_controller, &PX4FirmwareUpgradeThreadController::_initThreadWorker,            this, &PX4FirmwareUpgradeThreadWorker::_init);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_startFindBoardLoopOnThread,  this, &PX4FirmwareUpgradeThreadWorker::_startFindBoardLoop);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_flashOnThread,               this, &PX4FirmwareUpgradeThreadWorker::_flash);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_rebootOnThread,              this, &PX4FirmwareUpgradeThreadWorker::_reboot);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_cancel,                      this, &PX4FirmwareUpgradeThreadWorker::_cancel);
}

PX4FirmwareUpgradeThreadWorker::~PX4FirmwareUpgradeThreadWorker()
{
    if (_bootloaderPort) {
        // deleteLater so delete happens on correct thread
        _bootloaderPort->deleteLater();
    }
}

/// @brief Initializes the PX4FirmwareUpgradeThreadWorker with the various child objects which must be created
///         on the worker thread.
void PX4FirmwareUpgradeThreadWorker::_init(void)
{
    // We create the timers here so that they are on the right thread

    Q_ASSERT(_timerRetry == NULL);
    _timerRetry = new QTimer(this);
    Q_CHECK_PTR(_timerRetry);
    _timerRetry->setSingleShot(true);
    _timerRetry->setInterval(_retryTimeout);
    connect(_timerRetry, &QTimer::timeout, this, &PX4FirmwareUpgradeThreadWorker::_findBoardOnce);

    Q_ASSERT(_bootloader == NULL);
    _bootloader = new Bootloader(this);
    connect(_bootloader, &Bootloader::updateProgress, this, &PX4FirmwareUpgradeThreadWorker::_updateProgress);
}

void PX4FirmwareUpgradeThreadWorker::_cancel(void)
{
    if (_bootloaderPort) {
        _bootloaderPort->close();
        _bootloaderPort->deleteLater();
        _bootloaderPort = NULL;
    }
}

void PX4FirmwareUpgradeThreadWorker::_startFindBoardLoop(void)
{
    _foundBoard = false;
    _findBoardFirstAttempt = true;
    _findBoardOnce();
}

void PX4FirmwareUpgradeThreadWorker::_findBoardOnce(void)
{
    qCDebug(FirmwareUpgradeVerboseLog) << "_findBoardOnce";

    QGCSerialPortInfo               portInfo;
    QGCSerialPortInfo::BoardType_t  boardType;

    if (_findBoardFromPorts(portInfo, boardType)) {
        if (!_foundBoard) {
            _foundBoard = true;
            _foundBoardPortInfo = portInfo;
            emit foundBoard(_findBoardFirstAttempt, portInfo, boardType);
            if (!_findBoardFirstAttempt) {
                if (boardType == QGCSerialPortInfo::BoardType3drRadio) {
                    _3drRadioForceBootloader(portInfo);
                    return;
                } else {
                    _findBootloader(portInfo, false /* radio mode */, true /* errorOnNotFound */);
                    return;
                }
            }
        }
    } else {
        if (_foundBoard) {
            _foundBoard = false;
            qCDebug(FirmwareUpgradeLog) << "Board gone";
            emit boardGone();
        } else if (_findBoardFirstAttempt) {
            emit noBoardFound();
        }
    }

    _findBoardFirstAttempt = false;
    _timerRetry->start();
}

bool PX4FirmwareUpgradeThreadWorker::_findBoardFromPorts(QGCSerialPortInfo& portInfo, QGCSerialPortInfo::BoardType_t& boardType)
{
    foreach (QGCSerialPortInfo info, QGCSerialPortInfo::availablePorts()) {
        qCDebug(FirmwareUpgradeVerboseLog) << "Serial Port --------------";
        qCDebug(FirmwareUpgradeVerboseLog) << "\tboard type" << info.boardType();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tport name:" << info.portName();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tdescription:" << info.description();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tsystem location:" << info.systemLocation();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tvendor ID:" << info.vendorIdentifier();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tproduct ID:" << info.productIdentifier();

        boardType = info.boardType();
        if (boardType != QGCSerialPortInfo::BoardTypeUnknown) {
            portInfo = info;
            return true;
        }
    }

    return false;
}

void PX4FirmwareUpgradeThreadWorker::_3drRadioForceBootloader(const QGCSerialPortInfo& portInfo)
{
    // First make sure we can't get the bootloader

    if (_findBootloader(portInfo, true /* radio Mode */, false /* errorOnNotFound */)) {
        return;
    }

    // Couldn't find the bootloader. We'll need to reboot the radio into bootloader.

    QSerialPort port(portInfo);

    port.setBaudRate(QSerialPort::Baud57600);

    emit status("Putting radio into command mode");

    // Wait a little while for the USB port to initialize. 3DR Radio boot is really slow.
    QGC::SLEEP::msleep(2000);
    port.open(QIODevice::ReadWrite);

    if (!port.isOpen()) {
        emit error(QString("Unable to open port: %1 error: %2").arg(portInfo.systemLocation()).arg(port.errorString()));
        return;
    }

    // Put radio into command mode
    QGC::SLEEP::msleep(2000);
    port.write("+++", 3);
    if (!port.waitForReadyRead(1500)) {
        emit error("Unable to put radio into command mode");
        return;
    }
    QByteArray bytes = port.readAll();
    if (!bytes.contains("OK")) {
        qCDebug(FirmwareUpgradeLog) << bytes;
        emit error("Unable to put radio into command mode");
        return;
    }

    emit status("Rebooting radio to bootloader");

    port.write("AT&UPDATE\r\n");
    if (!port.waitForBytesWritten(1500)) {
        emit error("Unable to reboot radio (bytes written)");
        return;
    }
    if (!port.waitForReadyRead(1500)) {
        emit error("Unable to reboot radio (ready read)");
        return;
    }
    port.close();
    QGC::SLEEP::msleep(2000);

    // The bootloader should be waiting for us now

    _findBootloader(portInfo, true /* radio mode */, true /* errorOnNotFound */);
}

bool PX4FirmwareUpgradeThreadWorker::_findBootloader(const QGCSerialPortInfo& portInfo, bool radioMode, bool errorOnNotFound)
{
    qCDebug(FirmwareUpgradeLog) << "_findBootloader" << portInfo.systemLocation();

    uint32_t bootloaderVersion = 0;
    uint32_t boardID;
    uint32_t flashSize = 0;

    _bootloaderPort = new QextSerialPort(QextSerialPort::Polling);
    if (radioMode) {
        _bootloaderPort->setBaudRate(BAUD115200);
    }

    // Wait a little while for the USB port to initialize.
    bool openFailed = true;
    for (int i=0; i<10; i++) {
        if (_bootloader->open(_bootloaderPort, portInfo.systemLocation())) {
            openFailed = false;
            break;
        } else {
            QGC::SLEEP::msleep(100);
        }
    }

    if (radioMode) {
        QGC::SLEEP::msleep(2000);
    }

    if (openFailed) {
        qCDebug(FirmwareUpgradeLog) << "Bootloader open port failed:" << _bootloader->errorString();
        if (errorOnNotFound) {
            emit error(_bootloader->errorString());
        }
        _bootloaderPort->deleteLater();
        _bootloaderPort = NULL;
        return false;
    }

    if (_bootloader->sync(_bootloaderPort)) {
        bool success;

        if (radioMode) {
            success = _bootloader->get3DRRadioBoardId(_bootloaderPort, boardID);
        } else {
            success = _bootloader->getPX4BoardInfo(_bootloaderPort, bootloaderVersion, boardID, flashSize);
        }
        if (success) {
            qCDebug(FirmwareUpgradeLog) << "Found bootloader";
            emit foundBootloader(bootloaderVersion, boardID, flashSize);
            return true;
        }
    }

    _bootloaderPort->close();
    _bootloaderPort->deleteLater();
    _bootloaderPort = NULL;
    qCDebug(FirmwareUpgradeLog) << "Bootloader error:" << _bootloader->errorString();
    if (errorOnNotFound) {
        emit error(_bootloader->errorString());
    }

    return false;
}

void PX4FirmwareUpgradeThreadWorker::_reboot(void)
{
    if (_bootloaderPort) {
        if (_bootloaderPort->isOpen()) {
            _bootloader->reboot(_bootloaderPort);
        }
        _bootloaderPort->deleteLater();
        _bootloaderPort = NULL;
    }
}

void PX4FirmwareUpgradeThreadWorker::_flash(void)
{
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadWorker::_flash";

    if (_erase()) {
        emit status("Programming new version...");

        if (_bootloader->program(_bootloaderPort, _controller->image())) {
            qCDebug(FirmwareUpgradeLog) << "Program complete";
            emit status("Program complete");
        } else {
            _bootloaderPort->deleteLater();
            _bootloaderPort = NULL;
            qCDebug(FirmwareUpgradeLog) << "Program failed:" << _bootloader->errorString();
            emit error(_bootloader->errorString());
            return;
        }

        emit status("Verifying program...");

        if (_bootloader->verify(_bootloaderPort, _controller->image())) {
            qCDebug(FirmwareUpgradeLog) << "Verify complete";
            emit status("Verify complete");
        } else {
            qCDebug(FirmwareUpgradeLog) << "Verify failed:" << _bootloader->errorString();
            emit error(_bootloader->errorString());
            return;
        }
    }

    emit _reboot();

    emit flashComplete();
}

bool PX4FirmwareUpgradeThreadWorker::_erase(void)
{
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadWorker::_erase";

    emit eraseStarted();
    emit status("Erasing previous program...");

    if (_bootloader->erase(_bootloaderPort)) {
        qCDebug(FirmwareUpgradeLog) << "Erase complete";
        emit status("Erase complete");
        emit eraseComplete();
        return true;
    } else {
        qCDebug(FirmwareUpgradeLog) << "Erase failed:" << _bootloader->errorString();
        emit error(_bootloader->errorString());
        return false;
    }
}

PX4FirmwareUpgradeThreadController::PX4FirmwareUpgradeThreadController(QObject* parent) :
    QObject(parent)
{
    _worker = new PX4FirmwareUpgradeThreadWorker(this);
    Q_CHECK_PTR(_worker);

    _workerThread = new QThread(this);
    Q_CHECK_PTR(_workerThread);
    _worker->moveToThread(_workerThread);

    connect(_worker, &PX4FirmwareUpgradeThreadWorker::updateProgress,       this, &PX4FirmwareUpgradeThreadController::_updateProgress);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::foundBoard,           this, &PX4FirmwareUpgradeThreadController::_foundBoard);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::noBoardFound,         this, &PX4FirmwareUpgradeThreadController::_noBoardFound);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::boardGone,            this, &PX4FirmwareUpgradeThreadController::_boardGone);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::foundBootloader,      this, &PX4FirmwareUpgradeThreadController::_foundBootloader);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::bootloaderSyncFailed, this, &PX4FirmwareUpgradeThreadController::_bootloaderSyncFailed);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::error,                this, &PX4FirmwareUpgradeThreadController::_error);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::status,               this, &PX4FirmwareUpgradeThreadController::_status);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::eraseStarted,         this, &PX4FirmwareUpgradeThreadController::_eraseStarted);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::eraseComplete,        this, &PX4FirmwareUpgradeThreadController::_eraseComplete);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::flashComplete,        this, &PX4FirmwareUpgradeThreadController::_flashComplete);

    _workerThread->start();

    emit _initThreadWorker();
}

PX4FirmwareUpgradeThreadController::~PX4FirmwareUpgradeThreadController()
{
    _workerThread->quit();
    _workerThread->wait();

    delete _workerThread;
}

void PX4FirmwareUpgradeThreadController::startFindBoardLoop(void)
{
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadController::findBoard";
    emit _startFindBoardLoopOnThread();
}

void PX4FirmwareUpgradeThreadController::cancel(void)
{
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadController::cancel";
    emit _cancel();
}

void PX4FirmwareUpgradeThreadController::flash(const FirmwareImage* image)
{
    _image = image;
    emit _flashOnThread();
}
<|endoftext|>


<commit_before>// Sh: A GPU metaprogramming language.
//
// Copyright (c) 2003 University of Waterloo Computer Graphics Laboratory
// Project administrator: Michael D. McCool
// Authors: Zheng Qin, Stefanus Du Toit, Kevin Moule, Tiberiu S. Popa,
//          Michael D. McCool
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must
// not claim that you wrote the original software. If you use this
// software in a product, an acknowledgment in the product documentation
// would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must
// not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//////////////////////////////////////////////////////////////////////////////
#include <sh/sh.hpp>
#include <sh/shutil.hpp>
#include <iostream>
#include <sstream>
#include <cmath>
#include "Shader.hpp"
#include "Globals.hpp"

using namespace SH;
using namespace ShUtil;

const double DIFFR = 0.05;

class NonPeriodic3D : public Shader {
public:
  NonPeriodic3D();
  ~NonPeriodic3D();

  bool init();

  ShProgram vertex() { return vsh;}
  ShProgram fragment() { return fsh;}

  ShProgram vsh, fsh;

  static NonPeriodic3D instance;
};

NonPeriodic3D::NonPeriodic3D()
  : Shader("Tiling: 3D Nonperiodic")
{
}

NonPeriodic3D::~NonPeriodic3D()
{
}

ShColor3f color[3] = {ShColor3f(1.0f, 0.2f, 0.2f), ShColor3f(0.2f, 1.0f, 0.2f), ShColor3f(0.2f, 0.2f, 1.0f)};
ShAttrib1f edgewidth(0.05);

ShPoint3f modPoint(const ShPoint3f& p)
{
  return frac(p + ShConstAttrib3f(0.5, 0.5, 0.5)) - ShConstAttrib3f(0.5,0.5,0.5);
}

void testPlane(const ShVector3f& body, const ShAttrib4f& plane, const ShPoint3f& pt, ShColor3f col,
               ShColor3f& C, ShAttrib1f& best)
{
  ShPoint4f pth;
  pth(0,1,2) = pt;
  pth(3) = 1.0;
  ShAttrib1f t = -(plane | pth) / (body | plane(0,1,2));

  ShPoint3f p = pt + t*body;

  ShAttrib1f xmin = floor(p(1)+0.5)-0.5;
  ShAttrib1f ymin = floor(p(2)+0.5)-0.5;

  ShAttrib1f dmax = body | ShVector3f(plane(3), -xmin, -ymin);
  ShAttrib1f dmin = body | ShVector3f(plane(3), -xmin-1.0, -ymin-1.0);
  ShAttrib1f my_d = -(body | pt);

  ShAttrib1f ignore = max(max(p(1)<-0.5, p(1)>1.5), max(p(2)<-0.5, p(2)>1.5));
  ignore = max(ignore, min(my_d>(dmin-1e-7), my_d<(dmax+1e-7)));
  ignore = max(ignore, t > best);

  // Darken edges
  ShAttrib1f diff = min(frac(p(1)+0.5), frac(p(2)+0.5));
  col = min(diff/edgewidth, 1.0) * col;

  C = cond(ignore, C, col);
  best = cond(ignore, best, t);
}

ShColor3f testCube(ShVector3f body, ShPoint3f p)
{
  ShColor3f result;
  ShAttrib1f best = 100000.0;

  for (int i = 0; i < 3; i++) {
    testPlane(body, ShConstAttrib4f(1.0, 0.0, 0.0, -0.5), p, color[i], result, best);
    testPlane(body, ShConstAttrib4f(1.0, 0.0, 0.0, -1.5), p, color[i], result, best);
    body = body(1,2,0);
    p = p(1,2,0);
  }

  return result;
}

bool NonPeriodic3D::init()
{
  vsh = SH_BEGIN_PROGRAM("gpu:vertex") {
    ShInputPosition4f ipos;

    ShOutputPosition4f opos; // Position in NDC
    ShInOutTexCoord2f tc; // pass through tex coords

    opos = Globals::mvp | ipos; // Compute NDC position
  } SH_END;

  ShAttrib2f SH_DECL(scale) = ShAttrib2f(10.0, 10.0);
  scale.range(1.0, 100.0);

  ShVector3f SH_DECL(body) = ShVector3f(M_PI/2.0, M_E/2.0, 1.0);
  body.name("plane orientation");
  body.range(0.0, 20.0);
  edgewidth.name("Edge width");
  edgewidth.range(0.0, 0.5);

  for (int i = 0; i < 3; i++) {
    std::ostringstream os;
    os << "Colour " << i;
    color[i].name(os.str());
  }

  // This is a 3D nonperiodic tiling from Craig Kaplan.
  // It should be possible to extend this to make Penrose tiles.
  fsh = SH_BEGIN_PROGRAM("gpu:fragment") {
    ShInputTexCoord2f tc; // ignore texcoords

    ShOutputColor3f result;

    tc = scale*tc;

    body = normalize(body);
    ShVector3f v1;
    v1[0] = 1.0/body(0);
    v1[1] = -1.0/body(1);
    v1[2] = 0.0;
    v1 = normalize(v1);

    ShVector3f v2 = cross(body,v1);
    v2 = normalize(v2);

    ShPoint3f p = tc(0)*v1 + tc(1)*v2;
    p = modPoint(p);

    result = testCube(body, p);

  } SH_END;
  return true;
}

NonPeriodic3D NonPeriodic3D::instance = NonPeriodic3D();

<commit_msg>Don't assign to uniforms. Sigh<commit_after>// Sh: A GPU metaprogramming language.
//
// Copyright (c) 2003 University of Waterloo Computer Graphics Laboratory
// Project administrator: Michael D. McCool
// Authors: Zheng Qin, Stefanus Du Toit, Kevin Moule, Tiberiu S. Popa,
//          Michael D. McCool
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must
// not claim that you wrote the original software. If you use this
// software in a product, an acknowledgment in the product documentation
// would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must
// not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//////////////////////////////////////////////////////////////////////////////
#include <sh/sh.hpp>
#include <sh/shutil.hpp>
#include <iostream>
#include <sstream>
#include <cmath>
#include "Shader.hpp"
#include "Globals.hpp"

using namespace SH;
using namespace ShUtil;

const double DIFFR = 0.05;

class NonPeriodic3D : public Shader {
public:
  NonPeriodic3D();
  ~NonPeriodic3D();

  bool init();

  ShProgram vertex() { return vsh;}
  ShProgram fragment() { return fsh;}

  ShProgram vsh, fsh;

  static NonPeriodic3D instance;
};

NonPeriodic3D::NonPeriodic3D()
  : Shader("Tiling: 3D Nonperiodic")
{
}

NonPeriodic3D::~NonPeriodic3D()
{
}

ShColor3f color[3] = {ShColor3f(1.0f, 0.2f, 0.2f), ShColor3f(0.2f, 1.0f, 0.2f), ShColor3f(0.2f, 0.2f, 1.0f)};
ShAttrib1f edgewidth(0.05);

ShPoint3f modPoint(const ShPoint3f& p)
{
  return frac(p + ShConstAttrib3f(0.5, 0.5, 0.5)) - ShConstAttrib3f(0.5,0.5,0.5);
}

void testPlane(const ShVector3f& body, const ShAttrib4f& plane, const ShPoint3f& pt, ShColor3f col,
               ShColor3f& C, ShAttrib1f& best)
{
  ShPoint4f pth;
  pth(0,1,2) = pt;
  pth(3) = 1.0;
  ShAttrib1f t = -(plane | pth) / (body | plane(0,1,2));

  ShPoint3f p = pt + t*body;

  ShAttrib1f xmin = floor(p(1)+0.5)-0.5;
  ShAttrib1f ymin = floor(p(2)+0.5)-0.5;

  ShAttrib1f dmax = body | ShVector3f(plane(3), -xmin, -ymin);
  ShAttrib1f dmin = body | ShVector3f(plane(3), -xmin-1.0, -ymin-1.0);
  ShAttrib1f my_d = -(body | pt);

  ShAttrib1f ignore = max(max(p(1)<-0.5, p(1)>1.5), max(p(2)<-0.5, p(2)>1.5));
  ignore = max(ignore, min(my_d>(dmin-1e-7), my_d<(dmax+1e-7)));
  ignore = max(ignore, t > best);

  // Darken edges
  ShAttrib1f diff = min(frac(p(1)+0.5), frac(p(2)+0.5));
  col = min(diff/edgewidth, 1.0) * col;

  C = cond(ignore, C, col);
  best = cond(ignore, best, t);
}

ShColor3f testCube(ShVector3f body, ShPoint3f p)
{
  ShColor3f result;
  ShAttrib1f best = 100000.0;

  for (int i = 0; i < 3; i++) {
    testPlane(body, ShConstAttrib4f(1.0, 0.0, 0.0, -0.5), p, color[i], result, best);
    testPlane(body, ShConstAttrib4f(1.0, 0.0, 0.0, -1.5), p, color[i], result, best);
    body = body(1,2,0);
    p = p(1,2,0);
  }

  return result;
}

bool NonPeriodic3D::init()
{
  vsh = SH_BEGIN_PROGRAM("gpu:vertex") {
    ShInputPosition4f ipos;

    ShOutputPosition4f opos; // Position in NDC
    ShInOutTexCoord2f tc; // pass through tex coords

    opos = Globals::mvp | ipos; // Compute NDC position
  } SH_END;

  ShAttrib2f SH_DECL(scale) = ShAttrib2f(10.0, 10.0);
  scale.range(1.0, 100.0);

  ShVector3f SH_DECL(body) = ShVector3f(M_PI/2.0, M_E/2.0, 1.0);
  body.name("plane orientation");
  body.range(0.0, 20.0);
  edgewidth.name("Edge width");
  edgewidth.range(0.0, 0.5);

  for (int i = 0; i < 3; i++) {
    std::ostringstream os;
    os << "Colour " << i;
    color[i].name(os.str());
  }

  // This is a 3D nonperiodic tiling from Craig Kaplan.
  // It should be possible to extend this to make Penrose tiles.
  fsh = SH_BEGIN_PROGRAM("gpu:fragment") {
    ShInputTexCoord2f tc; // ignore texcoords

    ShOutputColor3f result;

    tc = scale*tc;

    ShVector3f nbody = normalize(body);
    ShVector3f v1;
    v1[0] = 1.0/nbody(0);
    v1[1] = -1.0/nbody(1);
    v1[2] = 0.0;
    v1 = normalize(v1);

    ShVector3f v2 = cross(nbody,v1);
    v2 = normalize(v2);

    ShPoint3f p = tc(0)*v1 + tc(1)*v2;
    p = modPoint(p);

    result = testCube(nbody, p);

  } SH_END;
  return true;
}

NonPeriodic3D NonPeriodic3D::instance = NonPeriodic3D();

<|endoftext|>


<commit_before>/// Poset.hpp
/// Shaun Harker
/// 2015-05-15

#ifndef DSGRN_POSET_HPP
#define DSGRN_POSET_HPP

#ifndef INLINE_IF_HEADER_ONLY
#define INLINE_IF_HEADER_ONLY
#endif

#include "Poset.h"

INLINE_IF_HEADER_ONLY Poset::
Poset ( void ) {
  data_ . reset ( new Poset_ );
}

INLINE_IF_HEADER_ONLY Poset::
Poset ( std::vector<std::vector<uint64_t>> & adjacencies ) {
  assign ( adjacencies );
}

INLINE_IF_HEADER_ONLY Poset::
Poset ( Digraph const& digraph ) {
  assign ( digraph );
}

INLINE_IF_HEADER_ONLY void Poset::
assign ( std::vector<std::vector<uint64_t>> & adjacencies ) {
  assign ( Digraph ( adjacencies ) );
}

INLINE_IF_HEADER_ONLY void Poset::
assign ( Digraph const& digraph ) {
  data_ . reset ( new Poset_ );
  data_ -> transitive_closure = digraph . transitive_closure ();
  data_ -> transitive_reduction = data_ -> transitive_closure . transitive_reduction ();
  data_ -> transpose_transitive_closure = data_ -> transitive_closure . transpose ();
  data_ -> transpose_transitive_reduction = data_ -> transitive_reduction . transpose ();
  uint64_t N = size ();
  data_ -> relation . resize ( N );
  for ( uint64_t i = 0; i < N; ++ i ) {
    for ( uint64_t j : descendants ( i ) ) {
      data_ -> relation[i] . insert(j);
    }
  }
}

INLINE_IF_HEADER_ONLY uint64_t Poset::
size ( void ) const {
  return data_ -> transitive_reduction . size ();
}

INLINE_IF_HEADER_ONLY std::vector<uint64_t> const& Poset::
parents ( uint64_t v ) const {
  return data_ -> transpose_transitive_reduction . adjacencies ( v );
}

INLINE_IF_HEADER_ONLY std::vector<uint64_t> const& Poset::
children ( uint64_t v ) const {
  return data_ -> transitive_reduction . adjacencies ( v );
}

INLINE_IF_HEADER_ONLY std::vector<uint64_t> const& Poset::
ancestors ( uint64_t v ) const {
  return data_ -> transpose_transitive_closure . adjacencies ( v );
}

INLINE_IF_HEADER_ONLY std::vector<uint64_t> const& Poset::
descendants ( uint64_t v ) const {
  return data_ -> transitive_closure . adjacencies ( v );
}

INLINE_IF_HEADER_ONLY std::set<uint64_t> Poset::
maximal ( std::set<uint64_t> const& elements ) {
  std::set<uint64_t> result;
  auto is_maximal = [&](uint64_t u){
    for ( uint64_t v : elements ) if ( compare (u, v) ) return false;
    return true;
  }
  for ( uint64_t u : elements ) if ( is_maximal(u) ) result . insert ( u );
  return result;
}

INLINE_IF_HEADER_ONLY bool Poset::
compare ( const uint64_t & u, const uint64_t & v ) const {
  return data_ -> relation[u] . count (v);
}

INLINE_IF_HEADER_ONLY Poset Poset::
permute ( const std::vector<uint64_t> & permutation ) const {
  Poset result;
  result . data_ -> transitive_closure = data_ -> transitive_closure . permute ( permutation );
  result . data_ -> transitive_reduction = data_ -> transitive_reduction . permute ( permutation );
  result . data_ -> transpose_transitive_closure = data_ -> transpose_transitive_closure . permute ( permutation );
  result . data_ -> transpose_transitive_reduction = data_ -> transpose_transitive_reduction . permute ( permutation );
  uint64_t N = size ();
  result . data_ -> relation . resize ( N );
  for ( uint64_t i = 0; i < N; ++ i ) {
    for ( uint64_t j : result . data_ -> transitive_closure . adjacencies ( i ) ) {
      result . data_ -> relation[i] . insert ( j );
    }
  }
  return result;
}

INLINE_IF_HEADER_ONLY std::string Poset::
stringify ( void ) const {
  return data_ -> transitive_reduction . stringify ();
}

INLINE_IF_HEADER_ONLY void Poset::
parse ( std::string const& str ) {
  Digraph digraph;
  digraph . parse ( str );
  assign ( digraph );
}

INLINE_IF_HEADER_ONLY std::ostream& operator << ( std::ostream& stream, Poset const& poset ) {
  return stream << poset . data_ -> transitive_reduction;
}

#endif
<commit_msg>development<commit_after>/// Poset.hpp
/// Shaun Harker
/// 2015-05-15

#ifndef DSGRN_POSET_HPP
#define DSGRN_POSET_HPP

#ifndef INLINE_IF_HEADER_ONLY
#define INLINE_IF_HEADER_ONLY
#endif

#include "Poset.h"

INLINE_IF_HEADER_ONLY Poset::
Poset ( void ) {
  data_ . reset ( new Poset_ );
}

INLINE_IF_HEADER_ONLY Poset::
Poset ( std::vector<std::vector<uint64_t>> & adjacencies ) {
  assign ( adjacencies );
}

INLINE_IF_HEADER_ONLY Poset::
Poset ( Digraph const& digraph ) {
  assign ( digraph );
}

INLINE_IF_HEADER_ONLY void Poset::
assign ( std::vector<std::vector<uint64_t>> & adjacencies ) {
  assign ( Digraph ( adjacencies ) );
}

INLINE_IF_HEADER_ONLY void Poset::
assign ( Digraph const& digraph ) {
  data_ . reset ( new Poset_ );
  data_ -> transitive_closure = digraph . transitive_closure ();
  data_ -> transitive_reduction = data_ -> transitive_closure . transitive_reduction ();
  data_ -> transpose_transitive_closure = data_ -> transitive_closure . transpose ();
  data_ -> transpose_transitive_reduction = data_ -> transitive_reduction . transpose ();
  uint64_t N = size ();
  data_ -> relation . resize ( N );
  for ( uint64_t i = 0; i < N; ++ i ) {
    for ( uint64_t j : descendants ( i ) ) {
      data_ -> relation[i] . insert(j);
    }
  }
}

INLINE_IF_HEADER_ONLY uint64_t Poset::
size ( void ) const {
  return data_ -> transitive_reduction . size ();
}

INLINE_IF_HEADER_ONLY std::vector<uint64_t> const& Poset::
parents ( uint64_t v ) const {
  return data_ -> transpose_transitive_reduction . adjacencies ( v );
}

INLINE_IF_HEADER_ONLY std::vector<uint64_t> const& Poset::
children ( uint64_t v ) const {
  return data_ -> transitive_reduction . adjacencies ( v );
}

INLINE_IF_HEADER_ONLY std::vector<uint64_t> const& Poset::
ancestors ( uint64_t v ) const {
  return data_ -> transpose_transitive_closure . adjacencies ( v );
}

INLINE_IF_HEADER_ONLY std::vector<uint64_t> const& Poset::
descendants ( uint64_t v ) const {
  return data_ -> transitive_closure . adjacencies ( v );
}

INLINE_IF_HEADER_ONLY std::set<uint64_t> Poset::
maximal ( std::set<uint64_t> const& elements ) {
  std::set<uint64_t> result;
  auto is_maximal = [&](uint64_t u){
    for ( uint64_t v : elements ) if ( compare (u, v) ) return false;
    return true;
  };
  for ( uint64_t u : elements ) if ( is_maximal(u) ) result . insert ( u );
  return result;
}

INLINE_IF_HEADER_ONLY bool Poset::
compare ( const uint64_t & u, const uint64_t & v ) const {
  return data_ -> relation[u] . count (v);
}

INLINE_IF_HEADER_ONLY Poset Poset::
permute ( const std::vector<uint64_t> & permutation ) const {
  Poset result;
  result . data_ -> transitive_closure = data_ -> transitive_closure . permute ( permutation );
  result . data_ -> transitive_reduction = data_ -> transitive_reduction . permute ( permutation );
  result . data_ -> transpose_transitive_closure = data_ -> transpose_transitive_closure . permute ( permutation );
  result . data_ -> transpose_transitive_reduction = data_ -> transpose_transitive_reduction . permute ( permutation );
  uint64_t N = size ();
  result . data_ -> relation . resize ( N );
  for ( uint64_t i = 0; i < N; ++ i ) {
    for ( uint64_t j : result . data_ -> transitive_closure . adjacencies ( i ) ) {
      result . data_ -> relation[i] . insert ( j );
    }
  }
  return result;
}

INLINE_IF_HEADER_ONLY std::string Poset::
stringify ( void ) const {
  return data_ -> transitive_reduction . stringify ();
}

INLINE_IF_HEADER_ONLY void Poset::
parse ( std::string const& str ) {
  Digraph digraph;
  digraph . parse ( str );
  assign ( digraph );
}

INLINE_IF_HEADER_ONLY std::ostream& operator << ( std::ostream& stream, Poset const& poset ) {
  return stream << poset . data_ -> transitive_reduction;
}

#endif
<|endoftext|>


<commit_before>#pragma once

#include <eosio/trace_api/common.hpp>
#include <eosio/trace_api/trace.hpp>
#include <eosio/trace_api/extract_util.hpp>
#include <exception>
#include <functional>
#include <map>

namespace eosio { namespace trace_api {

using chain::transaction_id_type;
using chain::packed_transaction;

template <typename StoreProvider>
class chain_extraction_impl_type {
public:
   /**
    * Chain Extractor for capturing transaction traces, action traces, and block info.
    * @param store provider of append & append_lib
    * @param except_handler called on exceptions, logging if any is left to the user
    */
   chain_extraction_impl_type( StoreProvider store, exception_handler except_handler )
   : store(std::move(store))
   , except_handler(std::move(except_handler))
   {}

   /// connect to chain controller applied_transaction signal
   void signal_applied_transaction( const chain::transaction_trace_ptr& trace, const chain::signed_transaction& strx ) {
      on_applied_transaction( trace, strx );
   }

   /// connect to chain controller accepted_block signal
   void signal_accepted_block( const chain::block_state_ptr& bsp ) {
      on_accepted_block( bsp );
   }

   /// connect to chain controller irreversible_block signal
   void signal_irreversible_block( const chain::block_state_ptr& bsp ) {
      on_irreversible_block( bsp );
   }

private:
   static bool is_onblock(const chain::transaction_trace_ptr& p) {
      if (p->action_traces.empty())
         return false;
      const auto& act = p->action_traces[0].act;
      if (act.account != eosio::chain::config::system_account_name || act.name != N(onblock) ||
          act.authorization.size() != 1)
         return false;
      const auto& auth = act.authorization[0];
      return auth.actor == eosio::chain::config::system_account_name &&
             auth.permission == eosio::chain::config::active_name;
   }

   void on_applied_transaction(const chain::transaction_trace_ptr& trace, const chain::signed_transaction& t) {
      if( !trace->receipt ) return;
      // include only executed transactions; soft_fail included so that onerror (and any inlines via onerror) are included
      if((trace->receipt->status != chain::transaction_receipt_header::executed &&
          trace->receipt->status != chain::transaction_receipt_header::soft_fail)) {
         return;
      }
      if( is_onblock( trace )) {
         onblock_trace.emplace( cache_trace{trace, static_cast<const chain::transaction_header&>(t), t.signatures} );
      } else if( trace->failed_dtrx_trace ) {
         cached_traces[trace->failed_dtrx_trace->id] = {trace, static_cast<const chain::transaction_header&>(t), t.signatures};
      } else {
         cached_traces[trace->id] = {trace, static_cast<const chain::transaction_header&>(t), t.signatures};
      }
   }

   void on_accepted_block(const chain::block_state_ptr& block_state) {
      store_block_trace( block_state );
   }

   void on_irreversible_block( const chain::block_state_ptr& block_state ) {
      store_lib( block_state );
   }

   void store_block_trace( const chain::block_state_ptr& block_state ) {
      try {
         block_trace_v1 bt = create_block_trace_v1( block_state );

         std::vector<transaction_trace_v1>& traces = bt.transactions_v1;
         traces.reserve( block_state->block->transactions.size() + 1 );
         if( onblock_trace )
            traces.emplace_back( to_transaction_trace_v1( *onblock_trace ));
         for( const auto& r : block_state->block->transactions ) {
            transaction_id_type id;
            if( r.trx.contains<transaction_id_type>()) {
               id = r.trx.get<transaction_id_type>();
            } else {
               id = r.trx.get<packed_transaction>().id();
            }
            const auto it = cached_traces.find( id );
            if( it != cached_traces.end() ) {
               traces.emplace_back( to_transaction_trace_v1( it->second ));
            }
         }
         cached_traces.clear();
         onblock_trace.reset();

         store.append( std::move( bt ) );

      } catch( ... ) {
         except_handler( MAKE_EXCEPTION_WITH_CONTEXT( std::current_exception() ) );
      }
   }

   void store_lib( const chain::block_state_ptr& bsp ) {
      try {
         store.append_lib( bsp->block_num );
      } catch( ... ) {
         except_handler( MAKE_EXCEPTION_WITH_CONTEXT( std::current_exception() ) );
      }
   }

private:
   StoreProvider                                                store;
   exception_handler                                            except_handler;
   std::map<transaction_id_type, cache_trace>                   cached_traces;
   fc::optional<cache_trace>                                    onblock_trace;

};

}}<commit_msg>Verify block_num of on_block trace to account for possible aborted block followed by a block with no onblock. Also moved is_onblock to trace.hpp<commit_after>#pragma once

#include <eosio/trace_api/common.hpp>
#include <eosio/trace_api/trace.hpp>
#include <eosio/trace_api/extract_util.hpp>
#include <exception>
#include <functional>
#include <map>

namespace eosio { namespace trace_api {

using chain::transaction_id_type;
using chain::packed_transaction;

template <typename StoreProvider>
class chain_extraction_impl_type {
public:
   /**
    * Chain Extractor for capturing transaction traces, action traces, and block info.
    * @param store provider of append & append_lib
    * @param except_handler called on exceptions, logging if any is left to the user
    */
   chain_extraction_impl_type( StoreProvider store, exception_handler except_handler )
   : store(std::move(store))
   , except_handler(std::move(except_handler))
   {}

   /// connect to chain controller applied_transaction signal
   void signal_applied_transaction( const chain::transaction_trace_ptr& trace, const chain::signed_transaction& strx ) {
      on_applied_transaction( trace, strx );
   }

   /// connect to chain controller accepted_block signal
   void signal_accepted_block( const chain::block_state_ptr& bsp ) {
      on_accepted_block( bsp );
   }

   /// connect to chain controller irreversible_block signal
   void signal_irreversible_block( const chain::block_state_ptr& bsp ) {
      on_irreversible_block( bsp );
   }

private:

   void on_applied_transaction(const chain::transaction_trace_ptr& trace, const chain::signed_transaction& t) {
      if( !trace->receipt ) return;
      // include only executed transactions; soft_fail included so that onerror (and any inlines via onerror) are included
      if((trace->receipt->status != chain::transaction_receipt_header::executed &&
          trace->receipt->status != chain::transaction_receipt_header::soft_fail)) {
         return;
      }
      if( chain::is_onblock( *trace )) {
         onblock_trace.emplace( cache_trace{trace, static_cast<const chain::transaction_header&>(t), t.signatures} );
      } else if( trace->failed_dtrx_trace ) {
         cached_traces[trace->failed_dtrx_trace->id] = {trace, static_cast<const chain::transaction_header&>(t), t.signatures};
      } else {
         cached_traces[trace->id] = {trace, static_cast<const chain::transaction_header&>(t), t.signatures};
      }
   }

   void on_accepted_block(const chain::block_state_ptr& block_state) {
      store_block_trace( block_state );
   }

   void on_irreversible_block( const chain::block_state_ptr& block_state ) {
      store_lib( block_state );
   }

   void store_block_trace( const chain::block_state_ptr& block_state ) {
      try {
         block_trace_v1 bt = create_block_trace_v1( block_state );

         std::vector<transaction_trace_v1>& traces = bt.transactions_v1;
         traces.reserve( block_state->block->transactions.size() + 1 );
         // verify block_num of onblock_trace because last block could have been aborted & new block have no onblock
         if( onblock_trace && onblock_trace->trace->block_num == block_state->block_num )
            traces.emplace_back( to_transaction_trace_v1( *onblock_trace ));
         for( const auto& r : block_state->block->transactions ) {
            transaction_id_type id;
            if( r.trx.contains<transaction_id_type>()) {
               id = r.trx.get<transaction_id_type>();
            } else {
               id = r.trx.get<packed_transaction>().id();
            }
            const auto it = cached_traces.find( id );
            if( it != cached_traces.end() ) {
               traces.emplace_back( to_transaction_trace_v1( it->second ));
            }
         }
         cached_traces.clear();
         onblock_trace.reset();

         store.append( std::move( bt ) );

      } catch( ... ) {
         except_handler( MAKE_EXCEPTION_WITH_CONTEXT( std::current_exception() ) );
      }
   }

   void store_lib( const chain::block_state_ptr& bsp ) {
      try {
         store.append_lib( bsp->block_num );
      } catch( ... ) {
         except_handler( MAKE_EXCEPTION_WITH_CONTEXT( std::current_exception() ) );
      }
   }

private:
   StoreProvider                                                store;
   exception_handler                                            except_handler;
   std::map<transaction_id_type, cache_trace>                   cached_traces;
   fc::optional<cache_trace>                                    onblock_trace;

};

}}<|endoftext|>


<commit_before>// Copyright 2015 Alessio Sclocco <a.sclocco@vu.nl>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <string>
#include <map>
#include <vector>
#include <fstream>

#include <Observation.hpp>
#include <utils.hpp>

#ifndef INTEGRATION_HPP
#define INTEGRATION_HPP

namespace PulsarSearch {

class integrationSamplesDMsConf {
public:
  integrationSamplesDMsConf();
  ~integrationSamplesDMsConf();
  // Get
  unsigned int getNrSamplesPerBlock() const;
  unsigned int getNrSamplesPerThread() const;
  // Set
  void setNrSamplesPerBlock(unsigned int samples);
  void setNrSamplesPerThread(unsigned int samples);
  // utils
  std::string print() const;
private:
  unsigned int nrSamplesPerBlock;
  unsigned int nrSamplesPerThread;
};

typedef std::map< std::string, std::map < unsigned int, std::map< unsigned int, PulsarSearch::integrationSamplesDMsConf > > > tunedIntegrationSamplesDMsConf;

// Sequential
template< typename T > void integrationSamplesDMs(const AstroData::Observation & observation, const unsigned int integration, const unsigned int padding, const std::vector< T > & input, std::vector< T > & output);
// OpenCL
template< typename T > std::string * getIntegrationSamplesDMsOpenCL(const integrationSamplesDMsConf & conf, const AstroData::Observation & observation, const std::string & inputDataName, const unsigned int integration, const unsigned int padding);
// Read configuration files
void readTunedIntegrationSamplesDMsConf(tunedIntegrationSamplesDMsConf & tunedConf, const std::string & confFilename);

// Implementations
inline unsigned int integrationSamplesDMsConf::getNrSamplesPerBlock() const {
  return nrSamplesPerBlock;
}

inline unsigned int integrationSamplesDMsConf::getNrSamplesPerThread() const {
  return nrSamplesPerThread;
}

inline void integrationSamplesDMsConf::setNrSamplesPerBlock(unsigned int samples) {
  nrSamplesPerBlock = samples;
}

inline void integrationSamplesDMsConf::setNrSamplesPerThread(unsigned int samples) {
  nrSamplesPerThread = samples;
}

template< typename T > void integrationSamplesDMs(const AstroData::Observation & observation, const unsigned int integration, const unsigned int padding, const std::vector< T > & input, std::vector< T > & output) {
  for ( unsigned int dm = 0; dm < observation.getNrDMs(); dm++ ) {
    for ( unsigned int sample = 0; sample < observation.getNrSamplesPerSecond(); sample += integration ) {
      T integratedSample = 0;

      for ( unsigned int i = 0; i < integration; i++ ) {
        integratedSample += input[(dm * observation.getNrSamplesPerPaddedSecond(padding / sizeof(T))) + (sample + i)];
      }
      output[(dm * isa::utils::pad(observation.getNrSamplesPerSecond() / integration, padding / sizeof(T))) + (sample / integration)] = integratedSample / integration;
    }
  }
}

template< typename T > std::string * getIntegrationSamplesDMsOpenCL(const integrationSamplesDMsConf & conf, const AstroData::Observation & observation, const std::string & dataName, const unsigned int integration, const unsigned int padding) {
  std::string * code = new std::string();

  // Begin kernel's template
  *code = "__kernel void integrateSamplesDMs" + isa::utils::toString(integration) + "(__global const " + dataName + " * const restrict input, __global " + dataName + " * const restrict output) {\n"
    "unsigned int dm = get_group_id(1);\n"
    "__local " + dataName + " buffer[" + isa::utils::toString(integration * conf.getNrSamplesPerThread()) + "];\n"
    "<%DEFS%>"
    "\n"
    "// Load local memory"
    "unsigned int inLocalMemory = get_local_id(0);\n"
    "unsigned int inGlobalMemory = get_local_id(0) + (get_group_id(0) * " + isa::utils::toString(integration * conf.getNrSamplesPerThread()) + ");\n"
    "while ( inLocalMemory < " + isa::utils::toString(integration * conf.getNrSamplesPerThread()) + " ) {\n"
    "buffer[inLocalMemory] = input[(dm * " + isa::utils::toString(observation.getNrSamplesPerPaddedSecond(padding / sizeof(T))) + ") + inGlobalMemory];\n"
    "inGlobalMemory += " + isa::utils::toString(conf.getNrSamplesPerBlock()) + ";\n"
    "inLocalMemory += " + isa::utils::toString(conf.getNrSamplesPerBlock()) + ";\n"
    "}\n"
    "barrier(CLK_LOCAL_MEM_FENCE);\n"
    "// Reduce"
    "unsigned int threshold = " + isa::utils::toString(conf.getNrSamplesPerBlock() / 2) + ";\n"
    "for ( unsigned int sample = get_local_id(0); threshold > 0; threshold /= 2 ) {\n"
    "if ( sample < threshold ) {\n"
    "<%REDUCE%>"
    "}\n"
    "barrier(CLK_LOCAL_MEM_FENCE);\n"
    "}\n"
    "if ( get_local_id(0) < " + isa::utils::toString(conf.getNrSamplesPerThread()) + " ) {\n";
  if ( dataName == "float" ) {
    *code += "output[(dm * " + isa::utils::toString(isa::utils::pad(observation.getNrSamplesPerSecond() / integration, padding / sizeof(T))) + ") + ((get_group_id(0) * " + isa::utils::toString(conf.getNrSamplesPerThread()) + ") + get_local_id(0))] = buffer[get_local_id(0) * " + isa::utils::toString(integration) + "] * " + isa::utils::toString(1.0f / integration) + "f;\n";
  } else if ( dataName == "double" ) {
    *code += "output[(dm * " + isa::utils::toString(isa::utils::pad(observation.getNrSamplesPerSecond() / integration, padding / sizeof(T))) + ") + ((get_group_id(0) * " + isa::utils::toString(conf.getNrSamplesPerThread()) + ") + get_local_id(0))] = buffer[get_local_id(0) * " + isa::utils::toString(integration) + "] * " + isa::utils::toString(1.0 / integration) + ";\n";
  } else {
    *code += "output[(dm * " + isa::utils::toString(isa::utils::pad(observation.getNrSamplesPerSecond() / integration, padding / sizeof(T))) + ") + ((get_group_id(0) * " + isa::utils::toString(conf.getNrSamplesPerThread()) + ") + get_local_id(0))] = buffer[get_local_id(0) * " + isa::utils::toString(integration) + "] / " + isa::utils::toString(integration) + ";\n";
  }
  *code += "<%STORE%>"
    "}\n"
    "}\n";
  std::string defs_sTemplate = dataName + " integratedSample<%NUM%> = 0;\n";
  std::string reduce_sTemplate = "integratedSample<%NUM%> += buffer[(sample + <%OFFSET%>) + threshold];\n"
    "buffer[sample + <%OFFSET%>] = integratedSample<%NUM%>;\n";
  // End kernel's template

  std::string * defs_s = new std::string();
  std::string * reduce_s = new std::string();

  for ( unsigned int sample = 0; sample < conf.getNrSamplesPerThread(); sample++ ) {
    std::string sample_s = isa::utils::toString(sample);
    std::string offset_s = isa::utils::replace(sample * integration);
    std::string * temp = 0;

    temp = isa::utils::replace(defs_sTemplate, "<%NUM%>", sample_s);
    defs_s->append(*temp);
    delete temp;
    temp = isa::utils::replace(reduce_sTemplate, "<%NUM%>", sample_s);
    if ( sample == 0 ) {
      std::string empty_s("");
      temp = isa::utils::replace(temp, " + <%OFFSET%>", empty_s, true);
    } else {
      temp = isa::utils::replace(temp, "<%OFFSET%>", offset_s, true);
    }
    reduce_s->append(*temp);
    delete temp;
  }
  code = isa::utils::replace(code, "<%DEFS%>", *defs_s, true);
  code = isa::utils::replace(code, "<%REDUCE%>", *reduce_s, true);
  delete defs_s;
  delete reduce_s;

  return code;
}

} // PulsarSearch

#endif // INTEGRATION_HPP

<commit_msg>Typo.<commit_after>// Copyright 2015 Alessio Sclocco <a.sclocco@vu.nl>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <string>
#include <map>
#include <vector>
#include <fstream>

#include <Observation.hpp>
#include <utils.hpp>

#ifndef INTEGRATION_HPP
#define INTEGRATION_HPP

namespace PulsarSearch {

class integrationSamplesDMsConf {
public:
  integrationSamplesDMsConf();
  ~integrationSamplesDMsConf();
  // Get
  unsigned int getNrSamplesPerBlock() const;
  unsigned int getNrSamplesPerThread() const;
  // Set
  void setNrSamplesPerBlock(unsigned int samples);
  void setNrSamplesPerThread(unsigned int samples);
  // utils
  std::string print() const;
private:
  unsigned int nrSamplesPerBlock;
  unsigned int nrSamplesPerThread;
};

typedef std::map< std::string, std::map < unsigned int, std::map< unsigned int, PulsarSearch::integrationSamplesDMsConf > > > tunedIntegrationSamplesDMsConf;

// Sequential
template< typename T > void integrationSamplesDMs(const AstroData::Observation & observation, const unsigned int integration, const unsigned int padding, const std::vector< T > & input, std::vector< T > & output);
// OpenCL
template< typename T > std::string * getIntegrationSamplesDMsOpenCL(const integrationSamplesDMsConf & conf, const AstroData::Observation & observation, const std::string & inputDataName, const unsigned int integration, const unsigned int padding);
// Read configuration files
void readTunedIntegrationSamplesDMsConf(tunedIntegrationSamplesDMsConf & tunedConf, const std::string & confFilename);

// Implementations
inline unsigned int integrationSamplesDMsConf::getNrSamplesPerBlock() const {
  return nrSamplesPerBlock;
}

inline unsigned int integrationSamplesDMsConf::getNrSamplesPerThread() const {
  return nrSamplesPerThread;
}

inline void integrationSamplesDMsConf::setNrSamplesPerBlock(unsigned int samples) {
  nrSamplesPerBlock = samples;
}

inline void integrationSamplesDMsConf::setNrSamplesPerThread(unsigned int samples) {
  nrSamplesPerThread = samples;
}

template< typename T > void integrationSamplesDMs(const AstroData::Observation & observation, const unsigned int integration, const unsigned int padding, const std::vector< T > & input, std::vector< T > & output) {
  for ( unsigned int dm = 0; dm < observation.getNrDMs(); dm++ ) {
    for ( unsigned int sample = 0; sample < observation.getNrSamplesPerSecond(); sample += integration ) {
      T integratedSample = 0;

      for ( unsigned int i = 0; i < integration; i++ ) {
        integratedSample += input[(dm * observation.getNrSamplesPerPaddedSecond(padding / sizeof(T))) + (sample + i)];
      }
      output[(dm * isa::utils::pad(observation.getNrSamplesPerSecond() / integration, padding / sizeof(T))) + (sample / integration)] = integratedSample / integration;
    }
  }
}

template< typename T > std::string * getIntegrationSamplesDMsOpenCL(const integrationSamplesDMsConf & conf, const AstroData::Observation & observation, const std::string & dataName, const unsigned int integration, const unsigned int padding) {
  std::string * code = new std::string();

  // Begin kernel's template
  *code = "__kernel void integrateSamplesDMs" + isa::utils::toString(integration) + "(__global const " + dataName + " * const restrict input, __global " + dataName + " * const restrict output) {\n"
    "unsigned int dm = get_group_id(1);\n"
    "__local " + dataName + " buffer[" + isa::utils::toString(integration * conf.getNrSamplesPerThread()) + "];\n"
    "<%DEFS%>"
    "\n"
    "// Load local memory"
    "unsigned int inLocalMemory = get_local_id(0);\n"
    "unsigned int inGlobalMemory = get_local_id(0) + (get_group_id(0) * " + isa::utils::toString(integration * conf.getNrSamplesPerThread()) + ");\n"
    "while ( inLocalMemory < " + isa::utils::toString(integration * conf.getNrSamplesPerThread()) + " ) {\n"
    "buffer[inLocalMemory] = input[(dm * " + isa::utils::toString(observation.getNrSamplesPerPaddedSecond(padding / sizeof(T))) + ") + inGlobalMemory];\n"
    "inGlobalMemory += " + isa::utils::toString(conf.getNrSamplesPerBlock()) + ";\n"
    "inLocalMemory += " + isa::utils::toString(conf.getNrSamplesPerBlock()) + ";\n"
    "}\n"
    "barrier(CLK_LOCAL_MEM_FENCE);\n"
    "// Reduce"
    "unsigned int threshold = " + isa::utils::toString(conf.getNrSamplesPerBlock() / 2) + ";\n"
    "for ( unsigned int sample = get_local_id(0); threshold > 0; threshold /= 2 ) {\n"
    "if ( sample < threshold ) {\n"
    "<%REDUCE%>"
    "}\n"
    "barrier(CLK_LOCAL_MEM_FENCE);\n"
    "}\n"
    "if ( get_local_id(0) < " + isa::utils::toString(conf.getNrSamplesPerThread()) + " ) {\n";
  if ( dataName == "float" ) {
    *code += "output[(dm * " + isa::utils::toString(isa::utils::pad(observation.getNrSamplesPerSecond() / integration, padding / sizeof(T))) + ") + ((get_group_id(0) * " + isa::utils::toString(conf.getNrSamplesPerThread()) + ") + get_local_id(0))] = buffer[get_local_id(0) * " + isa::utils::toString(integration) + "] * " + isa::utils::toString(1.0f / integration) + "f;\n";
  } else if ( dataName == "double" ) {
    *code += "output[(dm * " + isa::utils::toString(isa::utils::pad(observation.getNrSamplesPerSecond() / integration, padding / sizeof(T))) + ") + ((get_group_id(0) * " + isa::utils::toString(conf.getNrSamplesPerThread()) + ") + get_local_id(0))] = buffer[get_local_id(0) * " + isa::utils::toString(integration) + "] * " + isa::utils::toString(1.0 / integration) + ";\n";
  } else {
    *code += "output[(dm * " + isa::utils::toString(isa::utils::pad(observation.getNrSamplesPerSecond() / integration, padding / sizeof(T))) + ") + ((get_group_id(0) * " + isa::utils::toString(conf.getNrSamplesPerThread()) + ") + get_local_id(0))] = buffer[get_local_id(0) * " + isa::utils::toString(integration) + "] / " + isa::utils::toString(integration) + ";\n";
  }
  *code += "<%STORE%>"
    "}\n"
    "}\n";
  std::string defs_sTemplate = dataName + " integratedSample<%NUM%> = 0;\n";
  std::string reduce_sTemplate = "integratedSample<%NUM%> += buffer[(sample + <%OFFSET%>) + threshold];\n"
    "buffer[sample + <%OFFSET%>] = integratedSample<%NUM%>;\n";
  // End kernel's template

  std::string * defs_s = new std::string();
  std::string * reduce_s = new std::string();

  for ( unsigned int sample = 0; sample < conf.getNrSamplesPerThread(); sample++ ) {
    std::string sample_s = isa::utils::toString(sample);
    std::string offset_s = isa::utils::toString(sample * integration);
    std::string * temp = 0;

    temp = isa::utils::replace(defs_sTemplate, "<%NUM%>", sample_s);
    defs_s->append(*temp);
    delete temp;
    temp = isa::utils::replace(reduce_sTemplate, "<%NUM%>", sample_s);
    if ( sample == 0 ) {
      std::string empty_s("");
      temp = isa::utils::replace(temp, " + <%OFFSET%>", empty_s, true);
    } else {
      temp = isa::utils::replace(temp, "<%OFFSET%>", offset_s, true);
    }
    reduce_s->append(*temp);
    delete temp;
  }
  code = isa::utils::replace(code, "<%DEFS%>", *defs_s, true);
  code = isa::utils::replace(code, "<%REDUCE%>", *reduce_s, true);
  delete defs_s;
  delete reduce_s;

  return code;
}

} // PulsarSearch

#endif // INTEGRATION_HPP

<|endoftext|>


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

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// CLI utilities
//==============================================================================

#include "cliapplication.h"
#include "cliutils.h"
#include "coresettings.h"
#include "settings.h"

//==============================================================================

#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QProcess>
#include <QResource>
#include <QSettings>
#include <QTemporaryFile>

//==============================================================================

namespace OpenCOR {

//==============================================================================

#include "corecliutils.cpp.inl"

//==============================================================================

void initQtMessagePattern()
{
    // We don't want to see debug/warning messages when not in debug mode

#ifndef QT_DEBUG
    qSetMessagePattern("%{if-debug}%{endif}"              \
                       "%{if-warning}%{endif}"            \
                       "%{if-critical}%{message}%{endif}" \
                       "%{if-fatal}%{message}%{endif}");
#endif
}

//==============================================================================

void initPluginsPath(const QString &pAppFileName)
{
    // Initialise the plugins path

    QFileInfo appFileInfo = pAppFileName;
    QString appDir;

#ifdef Q_OS_WIN
    if (appFileInfo.suffix().isEmpty())
        // If pAppFileName has no suffix, then it means we tried to run OpenCOR
        // using something like "[OpenCOR]/OpenCOR", in which case QFileInfo()
        // will get lost when trying to retrieve the canonical path for OpenCOR.
        // Now, when use something like "[OpenCOR]/OpenCOR", it's as if we were
        // to use something like "[OpenCOR]/OpenCOR.com", so update appFileInfo
        // accordingly

        appFileInfo = pAppFileName+".com";
#endif

    appDir = QDir::toNativeSeparators(appFileInfo.canonicalPath());

    QString pluginsDir = QString();

#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
    pluginsDir = appDir+QDir::separator()+QString("..")+QDir::separator()+"plugins";

    if (!QDir(pluginsDir).exists()) {
        // The plugins directory doesn't exist, which should only happen if we
        // are trying to run OpenCOR from within Qt Creator, in which case
        // OpenCOR's file name will be [OpenCOR]/build/OpenCOR.exe rather than
        // [OpenCOR]/build/bin/OpenCOR.exe as it should normally be if we were
        // to mimic the case where OpenCOR has been deployed. Then, because the
        // plugins are in [OpenCOR]/build/plugins/OpenCOR, we must skip the
        // "../" bit. So, yes, it's not neat, but is there another solution?...

        pluginsDir = appDir+QDir::separator()+"plugins";
    }
#elif defined(Q_OS_MAC)
    pluginsDir = appDir+QDir::separator()+QString("..")+QDir::separator()+"PlugIns";
#else
    #error Unsupported platform
#endif

    pluginsDir = QDir::toNativeSeparators(QDir(pluginsDir).canonicalPath());

    QCoreApplication::setLibraryPaths(QStringList() << pluginsDir);
}

//==============================================================================

void initApplication(QCoreApplication *pApp, QString *pAppDate)
{
    // Remove all 'global' instances, in case OpenCOR previously crashed or
    // something (and therefore didn't remove all of them before quitting)

    OpenCOR::removeGlobalInstances();

    // Ignore SSL-related warnings
    // Note #1: this is to address an issue with QSslSocket not being able to
    //          resolve some methods...
    // Note #2: see https://github.com/opencor/opencor/issues/516 for more
    //          information...

    qputenv("QT_LOGGING_RULES", "qt.network.ssl.warning=false");

    // Set the name of the application

    pApp->setApplicationName(QFileInfo(pApp->applicationFilePath()).baseName());

    // Retrieve and set the version of the application

    QString versionData;

    readTextFromFile(":app_versiondate", versionData);

    QStringList versionDataList = versionData.split(eolString());

    pApp->setApplicationVersion(versionDataList.first());

    if (pAppDate)
        *pAppDate = versionDataList.last();
}

//==============================================================================

bool cliApplication(QCoreApplication *pApp, int *pRes)
{
    // Create and run our CLI application object

    CliApplication *cliApp = new CliApplication(pApp);

    bool res = cliApp->run(pRes);

    delete cliApp;

    return res;
}

//==============================================================================

void removeGlobalInstances()
{
    // Remove all the 'global' information shared between OpenCOR and its
    // different plugins

    QSettings(SettingsOrganization, SettingsApplication).remove(SettingsGlobal);
}

//==============================================================================

}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Some minor cleaning up [ci skip].<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// CLI utilities
//==============================================================================

#include "cliapplication.h"
#include "cliutils.h"
#include "coresettings.h"
#include "settings.h"

//==============================================================================

#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QProcess>
#include <QResource>
#include <QSettings>
#include <QTemporaryFile>

//==============================================================================

namespace OpenCOR {

//==============================================================================

#include "corecliutils.cpp.inl"

//==============================================================================

void initQtMessagePattern()
{
    // We don't want to see debug/warning messages when not in debug mode

#ifndef QT_DEBUG
    qSetMessagePattern("%{if-debug}%{endif}"              \
                       "%{if-warning}%{endif}"            \
                       "%{if-critical}%{message}%{endif}" \
                       "%{if-fatal}%{message}%{endif}");
#endif
}

//==============================================================================

void initPluginsPath(const QString &pAppFileName)
{
    // Initialise the plugins path

    QFileInfo appFileInfo = pAppFileName;
    QString appDir;

#ifdef Q_OS_WIN
    if (appFileInfo.suffix().isEmpty()) {
        // If pAppFileName has no suffix, then it means we tried to run OpenCOR
        // using something like "[OpenCOR]/OpenCOR", in which case QFileInfo()
        // will be lost when trying to retrieve the canonical path for OpenCOR.
        // Now, when use something like "[OpenCOR]/OpenCOR", it's as if we were
        // to use something like "[OpenCOR]/OpenCOR.com", so update appFileInfo
        // accordingly

        appFileInfo = pAppFileName+".com";
    }
#endif

    appDir = QDir::toNativeSeparators(appFileInfo.canonicalPath());

    QString pluginsDir = QString();

#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
    pluginsDir = appDir+QDir::separator()+QString("..")+QDir::separator()+"plugins";

    if (!QDir(pluginsDir).exists()) {
        // The plugins directory doesn't exist, which should only happen if we
        // are trying to run OpenCOR from within Qt Creator, in which case
        // OpenCOR's file name will be [OpenCOR]/build/OpenCOR.exe rather than
        // [OpenCOR]/build/bin/OpenCOR.exe as it should normally be if we were
        // to mimic the case where OpenCOR has been deployed. Then, because the
        // plugins are in [OpenCOR]/build/plugins/OpenCOR, we must skip the
        // "../" bit. So, yes, it's not neat, but is there another solution?...

        pluginsDir = appDir+QDir::separator()+"plugins";
    }
#elif defined(Q_OS_MAC)
    pluginsDir = appDir+QDir::separator()+QString("..")+QDir::separator()+"PlugIns";
#else
    #error Unsupported platform
#endif

    pluginsDir = QDir::toNativeSeparators(QDir(pluginsDir).canonicalPath());

    QCoreApplication::setLibraryPaths(QStringList() << pluginsDir);
}

//==============================================================================

void initApplication(QCoreApplication *pApp, QString *pAppDate)
{
    // Remove all 'global' instances, in case OpenCOR previously crashed or
    // something (and therefore didn't remove all of them before quitting)

    OpenCOR::removeGlobalInstances();

    // Ignore SSL-related warnings
    // Note #1: this is to address an issue with QSslSocket not being able to
    //          resolve some methods...
    // Note #2: see https://github.com/opencor/opencor/issues/516 for more
    //          information...

    qputenv("QT_LOGGING_RULES", "qt.network.ssl.warning=false");

    // Set the name of the application

    pApp->setApplicationName(QFileInfo(pApp->applicationFilePath()).baseName());

    // Retrieve and set the version of the application

    QString versionData;

    readTextFromFile(":app_versiondate", versionData);

    QStringList versionDataList = versionData.split(eolString());

    pApp->setApplicationVersion(versionDataList.first());

    if (pAppDate)
        *pAppDate = versionDataList.last();
}

//==============================================================================

bool cliApplication(QCoreApplication *pApp, int *pRes)
{
    // Create and run our CLI application object

    CliApplication *cliApp = new CliApplication(pApp);

    bool res = cliApp->run(pRes);

    delete cliApp;

    return res;
}

//==============================================================================

void removeGlobalInstances()
{
    // Remove all the 'global' information shared between OpenCOR and its
    // different plugins

    QSettings(SettingsOrganization, SettingsApplication).remove(SettingsGlobal);
}

//==============================================================================

}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2019-2020 Fastly, Inc., Toru Maesaka, Goro Fuji
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <vector>
#include <unistd.h>
#include <stdarg.h>

#include "h2olog.h"

#define VERSION "0.1.0"
#define POLL_TIMEOUT (1000)

static void usage(void)
{
    printf(R"(h2olog (v%s)
Usage: h2olog -p PID
       h2olog quic -p PID
       h2olog quic -t event_type -p PID
       h2olog quic -v -s response_header_name -p PID
Other options:
    -h Shows this help and exit.
    -d Shows debugging information.
    -w <file> Write output to <file> instead of stdout.
)",
           VERSION);
    return;
}

static void make_timestamp(char *buf, size_t buf_len)
{
    time_t t = time(NULL);
    struct tm tms;
    localtime_r(&t, &tms);
    const char *iso8601format = "%FT%TZ";
    strftime(buf, buf_len, iso8601format, &tms);
}

static void infof(const char* fmt, ...)
    __attribute__((format (printf, 1, 2)));

static void infof(const char* fmt, ...)
 {
    char buf[1024];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    char timestamp[128];
    make_timestamp(timestamp, sizeof(timestamp));

    fprintf(stderr, "%s %s\n", timestamp, buf);
}

static void show_event_per_sec(h2o_tracer_t *tracer, time_t *t0)
{
    time_t t1 = time(NULL);
    int64_t d = t1 - *t0;
    if (d > 10) {
        uint64_t c = tracer->count / d;
        if (c > 0) {
            if (tracer->lost_count > 0) {
                infof("%20" PRIu64 " events/s (possibly lost %" PRIu64 " events)", c, tracer->lost_count);
                tracer->lost_count = 0;
            } else {
                infof("%20" PRIu64 " events/s", c);
            }
            tracer->count = 0;
        }
        *t0 = t1;
    }
}

static void show_process(pid_t pid)
{
    char cmdline[256];
    char proc_file[256];
    snprintf(proc_file, sizeof(proc_file), "/proc/%d/cmdline", pid);
    FILE *f = fopen(proc_file, "r");
    if (f == nullptr) {
        fprintf(stderr, "Error: failed to open %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    size_t nread = fread(cmdline, 1, sizeof(cmdline), f);
    fclose(f);
    for (size_t i = 0; i < nread; i++) {
        if (cmdline[i] == '\0') {
            cmdline[i] = ' ';
        }
    }
    infof("Attaching pid=%d (%s)", pid, cmdline);
}

static std::string join_str(const std::string &sep, const std::vector<std::string> &strs)
{
    std::string s;
    for (auto iter = strs.cbegin(); iter != strs.cend(); ++iter) {
        if (iter != strs.cbegin()) {
            s += sep;
        }
        s += *iter;
    }
    return s;
}

static std::string generate_header_filter_cflag(const std::vector<std::string> &tokens)
{
    std::vector<std::string> conditions;

    for (auto &token : tokens) {
        char buf[256];
        snprintf(buf, sizeof(buf), "/* %s */ (slen) == %zu", token.c_str(), token.size());
        std::vector<std::string> exprs = {buf};

        for (size_t i = 0; i < token.size(); ++i) {
            snprintf(buf, sizeof(buf), "(s)[%zu] == '%c'", i, token[i]);
            exprs.push_back(buf);
        }
        conditions.push_back("(" + join_str(" && ", exprs) + ")");
    }

    std::string cflag("-DCHECK_ALLOWED_RES_HEADER_NAME(s,slen)=(");
    cflag += join_str(" || ", conditions);
    cflag += ")";
    return cflag;
}

static void event_cb(void *context, void *data, int len)
{
    h2o_tracer_t *tracer = (h2o_tracer_t *)context;
    tracer->count++;

    tracer->handle_event(tracer, data, len);
}

static void lost_cb(void *context, uint64_t lost)
{
    h2o_tracer_t *tracer = (h2o_tracer_t *)context;
    tracer->lost_count += lost;

    tracer->handle_lost(tracer, lost);
}

int main(int argc, char **argv)
{
    h2o_tracer_t tracer = {};
    if (argc > 1 && strcmp(argv[1], "quic") == 0) {
        init_quic_tracer(&tracer);
        --argc;
        ++argv;
    } else {
        init_http_tracer(&tracer);
    }

    bool debug = false;
    const char *out_file = nullptr;
    std::vector<std::string> event_type_filters;
    std::vector<std::string> response_header_filters;
    int c;
    pid_t h2o_pid = -1;
    while ((c = getopt(argc, argv, "hdp:t:s:w:")) != -1) {
        switch (c) {
        case 'p':
            h2o_pid = atoi(optarg);
            break;
        case 't':
            event_type_filters.push_back(optarg);
            break;
        case 's':
            response_header_filters.push_back(optarg);
            break;
        case 'w':
            out_file = optarg;
            break;
        case 'd':
            debug = true;
            break;
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        default:
            usage();
            exit(EXIT_FAILURE);
        }
    }

    if (argc > optind) {
        fprintf(stderr, "Error: too many aruments\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (h2o_pid == -1) {
        fprintf(stderr, "Error: -p option is missing\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (geteuid() != 0) {
        fprintf(stderr, "Error: root privilege is required\n");
        exit(EXIT_FAILURE);
    }

    if (out_file != nullptr) {
        FILE *out = fopen(out_file, "w");
        if (out == nullptr) {
            fprintf(stderr, "Error: failed to open %s: %s", out_file, strerror(errno));
            exit(EXIT_FAILURE);
        }
        tracer.out = out;
    } else {
        tracer.out = stdout;
    }

    std::vector<std::string> cflags;

    if (!response_header_filters.empty()) {
        cflags.push_back(generate_header_filter_cflag(response_header_filters));
    }

    ebpf::BPF *bpf = new ebpf::BPF();
    std::vector<ebpf::USDT> probes = tracer.init_usdt_probes(h2o_pid);

    ebpf::StatusTuple ret = bpf->init(tracer.bpf_text(), cflags, probes);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: init: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    for (auto &probe : probes) {
        ret = bpf->attach_usdt(probe);
        if (ret.code() != 0) {
            fprintf(stderr, "Error: attach_usdt: %s\n", ret.msg().c_str());
            return EXIT_FAILURE;
        }
    }

    ret = bpf->open_perf_buffer("events", event_cb, lost_cb, &tracer, 64);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: open_perf_buffer: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    if (debug) {
        show_process(h2o_pid);
    }

    ebpf::BPFPerfBuffer *perf_buffer = bpf->get_perf_buffer("events");
    if (perf_buffer) {
        time_t t0 = time(NULL);

        while (true) {
            perf_buffer->poll(POLL_TIMEOUT);
            fflush(tracer.out);

            if (debug) {
                show_event_per_sec(&tracer, &t0);
            }
        }
    }

    fprintf(stderr, "Error: failed to get_perf_buffer()\n");
    return EXIT_FAILURE;
}
<commit_msg>apply clang-format<commit_after>/*
 * Copyright (c) 2019-2020 Fastly, Inc., Toru Maesaka, Goro Fuji
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <vector>
#include <unistd.h>
#include <stdarg.h>

#include "h2olog.h"

#define VERSION "0.1.0"
#define POLL_TIMEOUT (1000)

static void usage(void)
{
    printf(R"(h2olog (v%s)
Usage: h2olog -p PID
       h2olog quic -p PID
       h2olog quic -t event_type -p PID
       h2olog quic -v -s response_header_name -p PID
Other options:
    -h Shows this help and exit.
    -d Shows debugging information.
    -w <file> Write output to <file> instead of stdout.
)",
           VERSION);
    return;
}

static void make_timestamp(char *buf, size_t buf_len)
{
    time_t t = time(NULL);
    struct tm tms;
    localtime_r(&t, &tms);
    const char *iso8601format = "%FT%TZ";
    strftime(buf, buf_len, iso8601format, &tms);
}

static void infof(const char *fmt, ...) __attribute__((format(printf, 1, 2)));

static void infof(const char *fmt, ...)
{
    char buf[1024];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    char timestamp[128];
    make_timestamp(timestamp, sizeof(timestamp));

    fprintf(stderr, "%s %s\n", timestamp, buf);
}

static void show_event_per_sec(h2o_tracer_t *tracer, time_t *t0)
{
    time_t t1 = time(NULL);
    int64_t d = t1 - *t0;
    if (d > 10) {
        uint64_t c = tracer->count / d;
        if (c > 0) {
            if (tracer->lost_count > 0) {
                infof("%20" PRIu64 " events/s (possibly lost %" PRIu64 " events)", c, tracer->lost_count);
                tracer->lost_count = 0;
            } else {
                infof("%20" PRIu64 " events/s", c);
            }
            tracer->count = 0;
        }
        *t0 = t1;
    }
}

static void show_process(pid_t pid)
{
    char cmdline[256];
    char proc_file[256];
    snprintf(proc_file, sizeof(proc_file), "/proc/%d/cmdline", pid);
    FILE *f = fopen(proc_file, "r");
    if (f == nullptr) {
        fprintf(stderr, "Error: failed to open %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    size_t nread = fread(cmdline, 1, sizeof(cmdline), f);
    fclose(f);
    for (size_t i = 0; i < nread; i++) {
        if (cmdline[i] == '\0') {
            cmdline[i] = ' ';
        }
    }
    infof("Attaching pid=%d (%s)", pid, cmdline);
}

static std::string join_str(const std::string &sep, const std::vector<std::string> &strs)
{
    std::string s;
    for (auto iter = strs.cbegin(); iter != strs.cend(); ++iter) {
        if (iter != strs.cbegin()) {
            s += sep;
        }
        s += *iter;
    }
    return s;
}

static std::string generate_header_filter_cflag(const std::vector<std::string> &tokens)
{
    std::vector<std::string> conditions;

    for (auto &token : tokens) {
        char buf[256];
        snprintf(buf, sizeof(buf), "/* %s */ (slen) == %zu", token.c_str(), token.size());
        std::vector<std::string> exprs = {buf};

        for (size_t i = 0; i < token.size(); ++i) {
            snprintf(buf, sizeof(buf), "(s)[%zu] == '%c'", i, token[i]);
            exprs.push_back(buf);
        }
        conditions.push_back("(" + join_str(" && ", exprs) + ")");
    }

    std::string cflag("-DCHECK_ALLOWED_RES_HEADER_NAME(s,slen)=(");
    cflag += join_str(" || ", conditions);
    cflag += ")";
    return cflag;
}

static void event_cb(void *context, void *data, int len)
{
    h2o_tracer_t *tracer = (h2o_tracer_t *)context;
    tracer->count++;

    tracer->handle_event(tracer, data, len);
}

static void lost_cb(void *context, uint64_t lost)
{
    h2o_tracer_t *tracer = (h2o_tracer_t *)context;
    tracer->lost_count += lost;

    tracer->handle_lost(tracer, lost);
}

int main(int argc, char **argv)
{
    h2o_tracer_t tracer = {};
    if (argc > 1 && strcmp(argv[1], "quic") == 0) {
        init_quic_tracer(&tracer);
        --argc;
        ++argv;
    } else {
        init_http_tracer(&tracer);
    }

    bool debug = false;
    const char *out_file = nullptr;
    std::vector<std::string> event_type_filters;
    std::vector<std::string> response_header_filters;
    int c;
    pid_t h2o_pid = -1;
    while ((c = getopt(argc, argv, "hdp:t:s:w:")) != -1) {
        switch (c) {
        case 'p':
            h2o_pid = atoi(optarg);
            break;
        case 't':
            event_type_filters.push_back(optarg);
            break;
        case 's':
            response_header_filters.push_back(optarg);
            break;
        case 'w':
            out_file = optarg;
            break;
        case 'd':
            debug = true;
            break;
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        default:
            usage();
            exit(EXIT_FAILURE);
        }
    }

    if (argc > optind) {
        fprintf(stderr, "Error: too many aruments\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (h2o_pid == -1) {
        fprintf(stderr, "Error: -p option is missing\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (geteuid() != 0) {
        fprintf(stderr, "Error: root privilege is required\n");
        exit(EXIT_FAILURE);
    }

    if (out_file != nullptr) {
        FILE *out = fopen(out_file, "w");
        if (out == nullptr) {
            fprintf(stderr, "Error: failed to open %s: %s", out_file, strerror(errno));
            exit(EXIT_FAILURE);
        }
        tracer.out = out;
    } else {
        tracer.out = stdout;
    }

    std::vector<std::string> cflags;

    if (!response_header_filters.empty()) {
        cflags.push_back(generate_header_filter_cflag(response_header_filters));
    }

    ebpf::BPF *bpf = new ebpf::BPF();
    std::vector<ebpf::USDT> probes = tracer.init_usdt_probes(h2o_pid);

    ebpf::StatusTuple ret = bpf->init(tracer.bpf_text(), cflags, probes);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: init: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    for (auto &probe : probes) {
        ret = bpf->attach_usdt(probe);
        if (ret.code() != 0) {
            fprintf(stderr, "Error: attach_usdt: %s\n", ret.msg().c_str());
            return EXIT_FAILURE;
        }
    }

    ret = bpf->open_perf_buffer("events", event_cb, lost_cb, &tracer, 64);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: open_perf_buffer: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    if (debug) {
        show_process(h2o_pid);
    }

    ebpf::BPFPerfBuffer *perf_buffer = bpf->get_perf_buffer("events");
    if (perf_buffer) {
        time_t t0 = time(NULL);

        while (true) {
            perf_buffer->poll(POLL_TIMEOUT);
            fflush(tracer.out);

            if (debug) {
                show_event_per_sec(&tracer, &t0);
            }
        }
    }

    fprintf(stderr, "Error: failed to get_perf_buffer()\n");
    return EXIT_FAILURE;
}
<|endoftext|>