<commit_before>/** This is a TcpServer's performance test.
  * It could be run in N different modes:
  * 1) Local throughput test:
  *     Clients and server created on the same machine and communicates through the loopback.
  *     This mode is designated to test server performance locally, on a single machine. And
  *     this mode is very limited because clients affects the server performance.
  * 2) Server mode:
  *     Application starts in server mode and accepts incomming connections. It can be termia
  *     ted using ^C signal. This mode can be used to test server performance in isolation.
  *     Also, this test mode takes network into account. Application should dump number of me
  *     ssages per second to stdout.
  * 3) Client mode:
  *     Application started in client mode can connect to server (the same application should
  *     be started on another node in server mode) and dump specified number of messages to
  *     the server.
  *
  * Parameters:
  * a) `mode` can accept three parameters - 'client', 'server' or 'local'.
  * b) `host` url or ip of the server if app was started in client mode.
  * c) `count` number of messages to send inf started in client mode.
  * d) `njobs` number of threads to use
  */
#include <iostream>
#include <thread>
#include <boost/thread/barrier.hpp>
#include <boost/program_options.hpp>

#include "tcp_server.h"
#include "perftest_tools.h"
#include <sys/time.h>

using namespace Akumuli;
namespace po = boost::program_options;

struct DbMock : DbConnection {
    typedef std::tuple<aku_ParamId, aku_TimeStamp, double> ValueT;
    aku_ParamId idsum;
    aku_TimeStamp tssum;
    double valsum;

    DbMock() {
        idsum  = 0;
        tssum  = 0;
        valsum = 0;
    }

    void write_double(aku_ParamId param, aku_TimeStamp ts, double data) {
        idsum  += param;
        tssum  += ts;
        valsum += data;
    }
};

enum Mode {
    CLIENT,
    SERVER,
    LOCAL,
};

Mode str_to_mode(std::string str) {
    if (str == "client" || str == "CLIENT") {
        return CLIENT;
    } else if (str == "server" || str == "SERVER") {
        return SERVER;
    } else if (str == "local" || str == "LOCAL") {
        return LOCAL;
    }
    throw std::runtime_error("Bad mode value");
}

struct Server {

    Mode                                mode;
    std::shared_ptr<IngestionPipeline>  pline;
    std::shared_ptr<DbMock>             dbcon;
    std::shared_ptr<TcpServer>          serv;
    boost::asio::io_service             ioA;
    std::vector<IOService*>             iovec       = { &ioA };
    boost::barrier                      barrier;

    Server(Mode mode)
        : mode(mode)
        , barrier(iovec.size() + 1)
    {
        dbcon = std::make_shared<DbMock>();
        pline = std::make_shared<IngestionPipeline>(dbcon, AKU_LINEAR_BACKOFF);
        int port = 4096;
        serv = std::make_shared<TcpServer>(iovec, port, pline);
        pline->start();
        serv->start();
    }

    //! Run IO service
    void start() {
        auto iorun = [](IOService& io, boost::barrier& bar) {
            auto fn = [&]() {
                io.run();
                bar.wait();
            };
            return fn;
        };

        for (auto io: iovec) {
            std::thread iothread(iorun(*io, barrier));
            iothread.detach();
        }
    }

    void stop() {
        serv->stop();
        std::cout << "TcpServer stopped" << std::endl;

        // No need to joint I/O threads, just wait until they completes.
        barrier.wait();
        std::cout << "I/O threads stopped" << std::endl;

        pline->stop();
        std::cout << "Pipeline stopped" << std::endl;

        for (auto io: iovec) {
            io->stop();
        }
        std::cout << "I/O service stopped" << std::endl;

        std::cout << dbcon->idsum << " messages received" << std::endl;
    }
};

struct Client {
    int nthreads;
    int count;
    boost::barrier start_barrier, stop_barrier;
    EndpointT endpoint;
    PerfTimer *timer;

    Client(EndpointT ep, PerfTimer *timer, int nthreads = 4, int count = 2500000)
        : nthreads(nthreads)
        , count(count)
        , start_barrier(nthreads + 1)
        , stop_barrier(nthreads + 1)
        , endpoint(ep)
        , timer(timer)
    {
    }

    void start() {
        auto self = this;
        auto push = [self]() {
            std::vector<double> threshold_values;
            IOService io;
            TcpSocket socket(io);
            std::cout << "Connecting to server at " << self->endpoint << std::endl;
            socket.connect(self->endpoint);
            self->start_barrier.wait();

            boost::asio::streambuf stream;
            std::ostream os(&stream);
            size_t nsent = 0u;
            double tm = self->timer->elapsed();
            for (int i = self->count; i --> 0; ) {
                os << ":1\r\n" ":2\r\n" "+3.14\r\n";
                nsent += boost::asio::write(socket, stream);
                if (nsent >= 1024*1024) {  // +1Mb was sent
                    double newtm = self->timer->elapsed();
                    threshold_values.push_back(newtm - tm);
                    nsent = 0u;
                    tm = newtm;
                }
            }
            socket.shutdown(TcpSocket::shutdown_both);
            self->stop_barrier.wait();
            std::cout << "Push process completed" << std::endl;
            if (threshold_values.size()) {
                std::sort(threshold_values.begin(), threshold_values.end());
                double min = threshold_values.front(), max = threshold_values.back();
                double sum = 0, avg = 0, med = threshold_values[threshold_values.size() / 2];
                for (auto x: threshold_values) { sum += x; }
                avg = sum / threshold_values.size();
                auto convert = [&](double val) {
                    // Convert seconds per Mb to Mb per second
                    return 1.0/val;
                };
                std::cout << "Push process performance" << std::endl;
                std::cout << "max: " << convert(min) << " Mb/sec" << std::endl;  // min and max should be
                std::cout << "min: " << convert(max) << " Mb/sec" << std::endl;  // swapped because of conv
                std::cout << "avg: " << convert(avg) << " Mb/sec" << std::endl;
                std::cout << "med: " << convert(med) << " Mb/sec" << std::endl;
            }
        };

        for (int i = 0; i < nthreads; i++) {
            std::thread th(push);
            th.detach();
        }

        start_barrier.wait();
        timer->restart();
    }

    void wait() {
        stop_barrier.wait();
    }
};

int main(int argc, char *argv[]) {
    std::cout << "Tcp server performance test" << std::endl;
    po::options_description desc("Allowed options");
    /*
     * Parameters:
     * a) `mode` can accept three parameters - 'client', 'server' or 'local'.
     * b) `host` url or ip of the server if app was started in client mode.
     * c) `count` number of messages to send inf started in client mode.
     * d) `njobs` number of threads to use
     */
    std::string strmode;
    std::string host;
    int num_messages;
    bool graphite_enabled = false;
    desc.add_options()
        ("help",                                                                 "produce help message")
        ("mode",     po::value<std::string>(&strmode)->default_value("local"),   "test mode")
        ("host",     po::value<std::string>(&host)->default_value("localhost"),  "server host in client mode")
        ("count",    po::value<int>(&num_messages)->default_value(1000000),      "number of messages to send")
        ("graphite", po::value<bool>(&graphite_enabled)->default_value(false),   "push result to graphite")
    ;

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    if (vm.count("help")) {
        std::cout << desc << std::endl;
        return 0;
    }

    Mode mode = str_to_mode(strmode);

    switch(mode) {
    case LOCAL: {
        PerfTimer tm;
        Server server(mode);
        EndpointT ep(boost::asio::ip::address_v4::loopback(), 4096);
        Client client(ep, &tm, 4, 2500000);
        server.start();
        client.start();
        client.wait();
        server.stop();
        double elapsed = tm.elapsed();
        std::cout << "Local test completed in " << elapsed << " seconds" << std::endl;
        if (graphite_enabled) {
            push_metric_to_graphite("tcp_server_local", elapsed);
        }
        break;
    }
    case SERVER:
        break;
    case CLIENT:
        break;
    }

    return 0;
}
<commit_msg>WIP: tcp server test suite, client and server mode implemented<commit_after>/** This is a TcpServer's performance test.
  * It could be run in N different modes:
  * 1) Local throughput test:
  *     Clients and server created on the same machine and communicates through the loopback.
  *     This mode is designated to test server performance locally, on a single machine. And
  *     this mode is very limited because clients affects the server performance.
  * 2) Server mode:
  *     Application starts in server mode and accepts incomming connections. It can be termia
  *     ted using ^C signal. This mode can be used to test server performance in isolation.
  *     Also, this test mode takes network into account. Application should dump number of me
  *     ssages per second to stdout.
  * 3) Client mode:
  *     Application started in client mode can connect to server (the same application should
  *     be started on another node in server mode) and dump specified number of messages to
  *     the server.
  *
  * Parameters:
  * a) `mode` can accept three parameters - 'client', 'server' or 'local'.
  * b) `host` url or ip of the server if app was started in client mode.
  * c) `count` number of messages to send inf started in client mode.
  * d) `njobs` number of threads to use
  */
#include <iostream>
#include <thread>
#include <boost/thread/barrier.hpp>
#include <boost/program_options.hpp>

#include "tcp_server.h"
#include "perftest_tools.h"
#include <sys/time.h>

using namespace Akumuli;
namespace po = boost::program_options;

struct DbMock : DbConnection {
    typedef std::tuple<aku_ParamId, aku_TimeStamp, double> ValueT;
    aku_ParamId idsum;
    aku_TimeStamp tssum;
    double valsum;

    DbMock() {
        idsum  = 0;
        tssum  = 0;
        valsum = 0;
    }

    void write_double(aku_ParamId param, aku_TimeStamp ts, double data) {
        idsum  += param;
        tssum  += ts;
        valsum += data;
    }
};

enum Mode {
    CLIENT,
    SERVER,
    LOCAL,
};

Mode str_to_mode(std::string str) {
    if (str == "client" || str == "CLIENT") {
        return CLIENT;
    } else if (str == "server" || str == "SERVER") {
        return SERVER;
    } else if (str == "local" || str == "LOCAL") {
        return LOCAL;
    }
    throw std::runtime_error("Bad mode value");
}

struct Server {

    Mode                                mode;
    std::shared_ptr<IngestionPipeline>  pline;
    std::shared_ptr<DbMock>             dbcon;
    std::shared_ptr<TcpServer>          serv;
    boost::asio::io_service             ioA;
    std::vector<IOService*>             iovec       = { &ioA };
    boost::barrier                      barrier;
    boost::asio::signal_set             sig;
    std::atomic<int>                    stopped = {0};

    Server(Mode mode)
        : mode(mode)
        , barrier(iovec.size() + 1)
        , sig(ioA, SIGINT)
    {
        dbcon = std::make_shared<DbMock>();
        pline = std::make_shared<IngestionPipeline>(dbcon, AKU_LINEAR_BACKOFF);
        int port = 4096;
        serv = std::make_shared<TcpServer>(iovec, port, pline);
        pline->start();
        serv->start();
    }

    //! Run IO service
    void start() {

        sig.async_wait(
                    // Wait for sigint
                    boost::bind(&Server::handle_sigint,
                                this,
                                boost::asio::placeholders::error)
                    );

        auto iorun = [](IOService& io, boost::barrier& bar) {
            auto fn = [&]() {
                io.run();
                bar.wait();
            };
            return fn;
        };

        for (auto io: iovec) {
            std::thread iothread(iorun(*io, barrier));
            iothread.detach();
        }
    }

    void handle_sigint(boost::system::error_code err) {
        if (!err) {
            if (stopped++ == 0) {
                std::cout << "SIGINT catched, stopping pipeline" << std::endl;
                for (auto io: iovec) {
                    io->stop();
                }
                pline->stop();
                serv->stop();
                barrier.wait();
                std::cout << "Server stopped" << std::endl;
            } else {
                std::cout << "Already stopped (sigint)" << std::endl;
            }
        } else {
            std::cout << "Signal handler error " << err.message() << std::endl;
        }
    }

    void stop() {
        if (stopped++ == 0) {
            serv->stop();
            std::cout << "TcpServer stopped" << std::endl;

            sig.cancel();

            // No need to joint I/O threads, just wait until they completes.
            barrier.wait();
            std::cout << "I/O threads stopped" << std::endl;

            pline->stop();
            std::cout << "Pipeline stopped" << std::endl;

            for (auto io: iovec) {
                io->stop();
            }
            std::cout << "I/O service stopped" << std::endl;

            std::cout << dbcon->idsum << " messages received" << std::endl;
        } else {
            std::cout << "Already stopped" << std::endl;
        }
    }

    void wait() {
        while(!stopped) {
            sleep(1);
        }
    }
};

struct Client {
    int nthreads;
    int count;
    boost::barrier start_barrier, stop_barrier;
    EndpointT endpoint;
    PerfTimer *timer;

    Client(EndpointT ep, PerfTimer *timer, int nthreads = 4, int count = 2500000)
        : nthreads(nthreads)
        , count(count)
        , start_barrier(nthreads + 1)
        , stop_barrier(nthreads + 1)
        , endpoint(ep)
        , timer(timer)
    {
    }

    void start() {
        auto self = this;
        auto push = [self]() {
            std::vector<double> threshold_values;
            IOService io;
            TcpSocket socket(io);
            std::cout << "Connecting to server at " << self->endpoint << std::endl;
            socket.connect(self->endpoint);
            self->start_barrier.wait();

            boost::asio::streambuf stream;
            std::ostream os(&stream);
            size_t nsent = 0u;
            double tm = self->timer->elapsed();
            for (int i = self->count; i --> 0; ) {
                os << ":1\r\n" ":2\r\n" "+3.14\r\n";
                nsent += boost::asio::write(socket, stream);
                if (nsent >= 1024*1024) {  // +1Mb was sent
                    double newtm = self->timer->elapsed();
                    threshold_values.push_back(newtm - tm);
                    nsent = 0u;
                    tm = newtm;
                }
            }
            socket.shutdown(TcpSocket::shutdown_both);
            self->stop_barrier.wait();
            std::cout << "Push process completed" << std::endl;
            if (threshold_values.size()) {
                std::sort(threshold_values.begin(), threshold_values.end());
                double min = threshold_values.front(), max = threshold_values.back();
                double sum = 0, avg = 0, med = threshold_values[threshold_values.size() / 2];
                for (auto x: threshold_values) { sum += x; }
                avg = sum / threshold_values.size();
                auto convert = [&](double val) {
                    // Convert seconds per Mb to Mb per second
                    return 1.0/val;
                };
                std::cout << "Push process performance" << std::endl;
                std::cout << "max: " << convert(min) << " Mb/sec" << std::endl;  // min and max should be
                std::cout << "min: " << convert(max) << " Mb/sec" << std::endl;  // swapped because of conv
                std::cout << "avg: " << convert(avg) << " Mb/sec" << std::endl;
                std::cout << "med: " << convert(med) << " Mb/sec" << std::endl;
            }
        };

        for (int i = 0; i < nthreads; i++) {
            std::thread th(push);
            th.detach();
        }

        start_barrier.wait();
        timer->restart();
    }

    void wait() {
        stop_barrier.wait();
    }
};

int main(int argc, char *argv[]) {
    std::cout << "Tcp server performance test" << std::endl;
    po::options_description desc("Allowed options");
    /*
     * Parameters:
     * a) `mode` can accept three parameters - 'client', 'server' or 'local'.
     * b) `host` url or ip of the server if app was started in client mode.
     * c) `count` number of messages to send inf started in client mode.
     * d) `njobs` number of threads to use
     */
    std::string strmode;
    std::string host;
    int num_messages;
    bool graphite_enabled = false;
    desc.add_options()
        ("help",                                                                 "produce help message")
        ("mode",     po::value<std::string>(&strmode)->default_value("local"),   "test mode")
        ("host",     po::value<std::string>(&host)->default_value("localhost"),  "server host in client mode")
        ("count",    po::value<int>(&num_messages)->default_value(1000000),      "number of messages to send")
        ("graphite", po::value<bool>(&graphite_enabled)->default_value(false),   "push result to graphite")
    ;

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    if (vm.count("help")) {
        std::cout << desc << std::endl;
        return 0;
    }

    Mode mode = str_to_mode(strmode);

    switch(mode) {
    case LOCAL: {
        PerfTimer tm;
        Server server(mode);
        EndpointT ep(boost::asio::ip::address_v4::loopback(), 4096);
        Client client(ep, &tm, 4, 2500000);
        server.start();
        client.start();
        client.wait();
        server.stop();
        double elapsed = tm.elapsed();
        std::cout << "Local test completed in " << elapsed << " seconds" << std::endl;
        if (graphite_enabled) {
            push_metric_to_graphite("tcp_server_local", elapsed);
        }
        break;
    }
    case SERVER: {
        PerfTimer tm;
        Server server(mode);
        server.start();
        server.wait();
        server.stop();
        break;
    }
    case CLIENT:
        PerfTimer tm;
        Server server(mode);
        EndpointT ep(boost::asio::ip::address_v4::from_string(host), 4096);
        Client client(ep, &tm, 1, num_messages);
        client.start();
        client.wait();
        break;
    }

    return 0;
}
<|endoftext|>


<commit_before>#include "BasicBlock.h"

#include <iostream>

#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/CFG.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"

#include "Type.h"
#include "Utils.h"

namespace dev
{
namespace eth
{
namespace jit
{

BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc):
	m_firstInstrIdx{_firstInstrIdx},
	m_begin(_begin),
	m_end(_end),
	m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {"Instr.", std::to_string(_firstInstrIdx)}, _mainFunc))
{}

LocalStack::LocalStack(Stack& _globalStack):
	m_global(_globalStack)
{}

void LocalStack::push(llvm::Value* _value)
{
	assert(_value->getType() == Type::Word);
	m_local.push_back(_value);
	m_maxSize = std::max(m_maxSize, size());
}

llvm::Value* LocalStack::pop()
{
	auto item = get(0);
	assert(!m_local.empty() || !m_input.empty());

	if (m_local.size() > 0)
		m_local.pop_back();
	else
		++m_globalPops;

	m_minSize = std::min(m_minSize, size());
	return item;
}

/**
 *  Pushes a copy of _index-th element (tos is 0-th elem).
 */
void LocalStack::dup(size_t _index)
{
	auto val = get(_index);
	push(val);
}

/**
 *  Swaps tos with _index-th element (tos is 0-th elem).
 *  _index must be > 0.
 */
void LocalStack::swap(size_t _index)
{
	assert(_index > 0);
	auto val = get(_index);
	auto tos = get(0);
	set(_index, tos);
	set(0, val);
}

llvm::Value* LocalStack::get(size_t _index)
{
	if (_index < m_local.size())
		return *(m_local.rbegin() + _index); // count from back

	auto idx = _index - m_local.size() + m_globalPops;
	if (idx >= m_input.size())
		m_input.resize(idx + 1);
	auto& item = m_input[idx];

	if (!item)
		item = m_global.get(idx);

	return item;
}

void LocalStack::set(size_t _index, llvm::Value* _word)
{
	if (_index < m_local.size())
	{
		*(m_local.rbegin() + _index) = _word;
		return;
	}

	auto idx = _index - m_local.size() + m_globalPops;
	assert(idx < m_input.size());
	m_input[idx] = _word;
}

void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb)
{
	auto blockTerminator = _bb.getTerminator();
	assert(blockTerminator);
	if (blockTerminator->getOpcode() != llvm::Instruction::Ret)
	{
		// Not needed in case of ret instruction. Ret invalidates the stack.
		_builder.SetInsertPoint(blockTerminator);

		// Update items fetched from global stack ignoring the poped ones
		assert(m_globalPops <= m_input.size()); // pop() always does get()
		for (auto i = m_globalPops; i < m_input.size(); ++i)
		{
			if (m_input[i])
				m_global.set(i, m_input[i]);
		}

		// Add new items
		for (auto& item: m_local)
		{
			if (m_globalPops) 						// Override poped global items
				m_global.set(--m_globalPops, item);	// using pops counter as the index
			else
				m_global.push(item);
		}

		// Pop not overriden items
		if (m_globalPops)
			m_global.pop(m_globalPops);
	}
}

}
}
}
<commit_msg>Do not modify pops counter of LocalStack during finalization.<commit_after>#include "BasicBlock.h"

#include <iostream>

#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/CFG.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"

#include "Type.h"
#include "Utils.h"

namespace dev
{
namespace eth
{
namespace jit
{

BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc):
	m_firstInstrIdx{_firstInstrIdx},
	m_begin(_begin),
	m_end(_end),
	m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {"Instr.", std::to_string(_firstInstrIdx)}, _mainFunc))
{}

LocalStack::LocalStack(Stack& _globalStack):
	m_global(_globalStack)
{}

void LocalStack::push(llvm::Value* _value)
{
	assert(_value->getType() == Type::Word);
	m_local.push_back(_value);
	m_maxSize = std::max(m_maxSize, size());
}

llvm::Value* LocalStack::pop()
{
	auto item = get(0);
	assert(!m_local.empty() || !m_input.empty());

	if (m_local.size() > 0)
		m_local.pop_back();
	else
		++m_globalPops;

	m_minSize = std::min(m_minSize, size());
	return item;
}

/**
 *  Pushes a copy of _index-th element (tos is 0-th elem).
 */
void LocalStack::dup(size_t _index)
{
	auto val = get(_index);
	push(val);
}

/**
 *  Swaps tos with _index-th element (tos is 0-th elem).
 *  _index must be > 0.
 */
void LocalStack::swap(size_t _index)
{
	assert(_index > 0);
	auto val = get(_index);
	auto tos = get(0);
	set(_index, tos);
	set(0, val);
}

llvm::Value* LocalStack::get(size_t _index)
{
	if (_index < m_local.size())
		return *(m_local.rbegin() + _index); // count from back

	auto idx = _index - m_local.size() + m_globalPops;
	if (idx >= m_input.size())
		m_input.resize(idx + 1);
	auto& item = m_input[idx];

	if (!item)
		item = m_global.get(idx);

	return item;
}

void LocalStack::set(size_t _index, llvm::Value* _word)
{
	if (_index < m_local.size())
	{
		*(m_local.rbegin() + _index) = _word;
		return;
	}

	auto idx = _index - m_local.size() + m_globalPops;
	assert(idx < m_input.size());
	m_input[idx] = _word;
}

void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb)
{
	auto blockTerminator = _bb.getTerminator();
	assert(blockTerminator);
	if (blockTerminator->getOpcode() != llvm::Instruction::Ret)
	{
		// Not needed in case of ret instruction. Ret invalidates the stack.
		_builder.SetInsertPoint(blockTerminator);

		// Update items fetched from global stack ignoring the poped ones
		assert(m_globalPops <= m_input.size()); // pop() always does get()
		for (auto i = m_globalPops; i < m_input.size(); ++i)
		{
			if (m_input[i])
				m_global.set(i, m_input[i]);
		}

		// Add new items
		auto pops = m_globalPops;			// Copy pops counter to keep original value
		for (auto& item: m_local)
		{
			if (pops) 						// Override poped global items
				m_global.set(--pops, item);	// using pops counter as the index
			else
				m_global.push(item);
		}

		// Pop not overriden items
		if (pops)
			m_global.pop(pops);
	}
}

}
}
}
<|endoftext|>


<commit_before>/*****************************************************************************\
 *                        ANALYSIS PERFORMANCE TOOLS                         *
 *                                   Extrae                                  *
 *              Instrumentation package for parallel applications            *
 *****************************************************************************
 *     ___     This library is free software; you can redistribute it and/or *
 *    /  __         modify it under the terms of the GNU LGPL as published   *
 *   /  /  _____    by the Free Software Foundation; either version 2.1      *
 *  /  /  /     \   of the License, or (at your option) any later version.   *
 * (  (  ( B S C )                                                           *
 *  \  \  \_____/   This library is distributed in 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 LGPL for more details.   *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public License  *
 * along with this library; if not, write to the Free Software Foundation,   *
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA          *
 * The GNU LEsser General Public License is contained in the file COPYING.   *
 *                                 ---------                                 *
 *   Barcelona Supercomputing Center - Centro Nacional de Supercomputacion   *
\*****************************************************************************/

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *\
 | @file: $HeadURL$
 | @last_commit: $Date$
 | @version:     $Revision$
\* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
#include "common.h"

static char UNUSED rcsid[] = "$Id$";

#include "applicationType.h"

#include <BPatch.h>
#include <BPatch_function.h>

#include <commonSnippets.h>

#include <iostream>

/* For Intel 8.x - 9.x (maybe 10.x?) */
#define INTEL_PARLOOP_RTNS     ".*_[0-9]+__par_loop[0-9]+.*"
#define INTEL_PARRGN_RTNS      ".*_[0-9]+__par_region[0-9]+.*"

string ApplicationType::TranslatePFToUF (string PF, OMP_rte_t type)
{
	string result;

	if (type == Intel_v8_1 || type == Intel_v9_1)
	{
		string tmp1 = PF.substr (0, PF.rfind("__par_"));
		string tmp2 = tmp1.substr (0, tmp1.rfind("_"));

		if (type == Intel_v8_1)
			result = tmp2.substr (1, tmp2.length()-1);
		else
			result = tmp2.substr (2, tmp2.length()-1);
	}
	else if (type == Intel_v11)
	{
		/* We don't know how to handle this, if possible */
		result = PF;
	}
	else if (type == IBM_v16)
	{
		result = PF.substr (0, PF.find ("$.OL$."));
	}
	else if (type == GNU_v42)
	{
		if (PF.find (".omp_fn.") != string::npos)
			result = PF.substr (0, PF.find (".omp_fn."));
		else if (PF.find ("._omp_fn.") != string::npos)
			result = PF.substr (0, PF.find ("._omp_fn."));
		else /* unhandled */
			result = PF;
	}

	return result;
}

string ApplicationType::demangleOpenMProutine (string name)
{
	return TranslatePFToUF (name, OpenMP_runtime);
}

ApplicationType::OMP_rte_t ApplicationType::checkIntelOpenMPRuntime (BPatch_image *appImage)
{
	BPatch_Vector<BPatch_function *> found_funcs;

	/* Check if there's any OpenMP parallel routine in the binary */
	if (appImage->findFunction (INTEL_PARLOOP_RTNS, found_funcs) != NULL)
		if (found_funcs.size() == 0)
			appImage->findFunction (INTEL_PARRGN_RTNS, found_funcs);

	if (found_funcs.size() > 0)
	{
		char functionName[1024];
		found_funcs[0]->getName (functionName, 1024);
		string routinev8_1 = TranslatePFToUF (functionName, Intel_v8_1);
		string routinev9_1 = TranslatePFToUF (functionName, Intel_v9_1);

		if (appImage->findFunction (routinev8_1.c_str(), found_funcs) != NULL)
			if (found_funcs.size() > 0)
				return Intel_v8_1;

		if (appImage->findFunction (routinev9_1.c_str(), found_funcs) != NULL)
			if (found_funcs.size() > 0)
				return Intel_v9_1;
	}

	/* If we don't know about the runtime, try with icc v11 */
	return Intel_v11;
}

bool ApplicationType::detectApplicationType_checkOpenMPrte (
	vector<string> &routines, string library, BPatch_image *appImage)
{
	bool result = false;

	unsigned numroutines = routines.size();
	for (unsigned r = 0; r < numroutines && !result; r++)
	{
		BPatch_Vector<BPatch_function *> found_funcs;
		found_funcs = getRoutines (routines[r], appImage, false);
		unsigned numfuncs = found_funcs.size();
		for (unsigned u = 0; u < numfuncs; u++)
		{
			char buffer[1024];
			char *module = found_funcs[u]->getModuleName (buffer, sizeof(buffer));
			result = library == module;
		}
	}

	return result;
}

bool ApplicationType::detectApplicationType_checkGNUOpenMPrte (BPatch_image *appImage)
{
	const char *functions[] = {"GOMP_parallel_start",
		"GOMP_loop_end",
		"GOMP_barrier"};
	vector<string> v (functions, functions+3);

	return detectApplicationType_checkOpenMPrte (v, "libgomp.so.1", appImage);
}

bool ApplicationType::detectApplicationType_checkIntelOpenMPrte (BPatch_image *appImage)
{
	const char *functions[] = {"__kmpc_fork_call",
		"__kmpc_invoke_task_func",
		"__kmpc_barrier"};
	vector<string> v (functions, functions+3);

	return detectApplicationType_checkOpenMPrte (v, "libiomp5.so", appImage);
}

bool ApplicationType::detectApplicationType_checkIBMOpenMPrte (BPatch_image *appImage)
{
	const char *functions[] = {"_xlsmpParallelDoSetup_TPO",
		"_xlsmpParRegionSetup_TPO",
		"_xlsmpWSDoSetup_TPO" };
	vector<string> v (functions, functions+3);

	return detectApplicationType_checkOpenMPrte (v, "libxlsmp.so.1", appImage);
}

void ApplicationType::detectApplicationType (BPatch_image *appImage)
{
	BPatch_Vector<BPatch_function *> found_funcs;

	isOpenMP = isMPI = isCUDA = false;

	/* Check for different implementation of OpenMP rte */
	if (detectApplicationType_checkIntelOpenMPrte (appImage))
	{
		isOpenMP = true;
		OpenMP_runtime = checkIntelOpenMPRuntime (appImage);
	}
	else if (detectApplicationType_checkGNUOpenMPrte (appImage))
	{
		isOpenMP = true;
		OpenMP_runtime = GNU_v42;
	}
	else if (detectApplicationType_checkIBMOpenMPrte (appImage))
	{
		OpenMP_runtime = IBM_v16;
		isOpenMP = true;
	}

	/* MPI applications must have MPI_Init (or mpi_init fortran names) */
	if ((appImage->findFunction ("MPI_Init", found_funcs) != NULL) ||
	    (appImage->findFunction ("mpi_init", found_funcs) != NULL) ||
	    (appImage->findFunction ("mpi_init_", found_funcs) != NULL) ||
	    (appImage->findFunction ("mpi_init__", found_funcs) != NULL) ||
	    (appImage->findFunction ("MPI_INIT", found_funcs) != NULL))
	{
		isMPI = true;
		if (appImage->findFunction ("mpi_init", found_funcs) != NULL)
			MPI_type = MPI_Fortran_0u;
		else if (appImage->findFunction ("mpi_init__", found_funcs) != NULL)
			MPI_type = MPI_Fortran_2u;
		else if (appImage->findFunction ("mpi_init_", found_funcs) != NULL)
			MPI_type = MPI_Fortran_1u;
		else if (appImage->findFunction ("MPI_INIT", found_funcs) != NULL)
			MPI_type = MPI_Fortran_ucase;
		else
			MPI_type = MPI_C;
	}

	/* Check for two typical CUDA routines in CUDA apps */
	if ((appImage->findFunction ("cudaLaunch", found_funcs) != NULL) &&
	    (appImage->findFunction ("cudaConfigureCall", found_funcs) != NULL))
		isCUDA = true;
}

void ApplicationType::dumpApplicationType (void)
{
	cout << PACKAGE_NAME << ": Detected application type: ";
	if (isOpenMP)
	{
		cout << "OpenMP ";
		if (OpenMP_runtime == GNU_v42)
			cout << "(GNU >= 4.2) ";
		else if (OpenMP_runtime == IBM_v16)
			cout << "(IBM >= 1.6) ";
		else if (OpenMP_runtime == Intel_v8_1)
			cout << "(Intel 8.x) ";
		else if (OpenMP_runtime == Intel_v9_1)
			cout << "(Intel 9.x or 10.x) ";
		else if (OpenMP_runtime == Intel_v11)
			cout << "(Intel >= 11.x) ";
		else
			cout << "(Unknown) ";
	}
	if (isMPI)
	{
		cout << "MPI ";
		if (MPI_type == MPI_C)
			cout << "(C language) ";
		else if (MPI_type == MPI_Fortran_0u)
			cout << "(Fortran language with 0 underscores) ";
		else if (MPI_type == MPI_Fortran_0u)
			cout << "(Fortran language with 1 underscore) ";
		else if (MPI_type == MPI_Fortran_0u)
			cout << "(Fortran language with 2 underscores) ";
		else if (MPI_type == MPI_Fortran_ucase)
			cout << "(Fortran language in uppercase) ";
	}
	if (!isOpenMP && !isMPI)
		cout << "Sequential ";

	if (isCUDA)
		cout << "CUDA-accelerated";

	cout << endl;
}

bool ApplicationType::isMangledOpenMProutine (string name)
{
	bool result = false;

	if (OpenMP_runtime == Intel_v8_1 || OpenMP_runtime == Intel_v9_1)
	{
		result = name.find ("__par_loop") != string::npos
		         ||
		         name.find ("__par_region") != string::npos;
	}
	else if (OpenMP_runtime == Intel_v11)
	{
		/* We don't know how to handle these */
		result = false;
	}
	else if (OpenMP_runtime == IBM_v16)
	{
		result = name.find ("$.OL$.") != string::npos;
	}
	else if (OpenMP_runtime == GNU_v42)
	{
		result = name.find (".omp_fn.") != string::npos || name.find ("._omp_fn.") != string::npos;
	}

	return result;
}
<commit_msg>Small fixes to allow insturmenting<commit_after>/*****************************************************************************\
 *                        ANALYSIS PERFORMANCE TOOLS                         *
 *                                   Extrae                                  *
 *              Instrumentation package for parallel applications            *
 *****************************************************************************
 *     ___     This library is free software; you can redistribute it and/or *
 *    /  __         modify it under the terms of the GNU LGPL as published   *
 *   /  /  _____    by the Free Software Foundation; either version 2.1      *
 *  /  /  /     \   of the License, or (at your option) any later version.   *
 * (  (  ( B S C )                                                           *
 *  \  \  \_____/   This library is distributed in 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 LGPL for more details.   *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public License  *
 * along with this library; if not, write to the Free Software Foundation,   *
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA          *
 * The GNU LEsser General Public License is contained in the file COPYING.   *
 *                                 ---------                                 *
 *   Barcelona Supercomputing Center - Centro Nacional de Supercomputacion   *
\*****************************************************************************/

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *\
 | @file: $HeadURL$
 | @last_commit: $Date$
 | @version:     $Revision$
\* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
#include "common.h"

static char UNUSED rcsid[] = "$Id$";

#include "applicationType.h"

#include <BPatch.h>
#include <BPatch_function.h>

#include <commonSnippets.h>

#include <iostream>

/* For Intel 8.x - 9.x (maybe 10.x?) */
#define INTEL_PARLOOP_RTNS     ".*_[0-9]+__par_loop[0-9]+.*"
#define INTEL_PARRGN_RTNS      ".*_[0-9]+__par_region[0-9]+.*"

string ApplicationType::TranslatePFToUF (string PF, OMP_rte_t type)
{
	string result;

	if (type == Intel_v8_1 || type == Intel_v9_1)
	{
		string tmp1 = PF.substr (0, PF.rfind("__par_"));
		string tmp2 = tmp1.substr (0, tmp1.rfind("_"));

		if (type == Intel_v8_1)
			result = tmp2.substr (1, tmp2.length()-1);
		else
			result = tmp2.substr (2, tmp2.length()-1);
	}
	else if (type == Intel_v11)
	{
		/* We don't know how to handle this, if possible */
		result = PF;
	}
	else if (type == IBM_v16)
	{
		result = PF.substr (0, PF.find ("$.OL$."));
	}
	else if (type == GNU_v42)
	{
		if (PF.find (".omp_fn.") != string::npos)
			result = PF.substr (0, PF.find (".omp_fn."));
		else if (PF.find ("._omp_fn.") != string::npos)
			result = PF.substr (0, PF.find ("._omp_fn."));
		else /* unhandled */
			result = PF;
	}

	return result;
}

string ApplicationType::demangleOpenMProutine (string name)
{
	return TranslatePFToUF (name, OpenMP_runtime);
}

ApplicationType::OMP_rte_t ApplicationType::checkIntelOpenMPRuntime (BPatch_image *appImage)
{
	BPatch_Vector<BPatch_function *> found_funcs;

	/* Check if there's any OpenMP parallel routine in the binary */
	if (appImage->findFunction (INTEL_PARLOOP_RTNS, found_funcs) != NULL)
		if (found_funcs.size() == 0)
			appImage->findFunction (INTEL_PARRGN_RTNS, found_funcs);

	if (found_funcs.size() > 0)
	{
		char functionName[1024];
		found_funcs[0]->getName (functionName, 1024);
		string routinev8_1 = TranslatePFToUF (functionName, Intel_v8_1);
		string routinev9_1 = TranslatePFToUF (functionName, Intel_v9_1);

		if (appImage->findFunction (routinev8_1.c_str(), found_funcs) != NULL)
			if (found_funcs.size() > 0)
				return Intel_v8_1;

		if (appImage->findFunction (routinev9_1.c_str(), found_funcs) != NULL)
			if (found_funcs.size() > 0)
				return Intel_v9_1;
	}

	/* If we don't know about the runtime, try with icc v11 */
	return Intel_v11;
}

bool ApplicationType::detectApplicationType_checkOpenMPrte (
	vector<string> &routines, string library, BPatch_image *appImage)
{
	bool result = false;

	unsigned numroutines = routines.size();
	for (unsigned r = 0; r < numroutines && !result; r++)
	{
		BPatch_Vector<BPatch_function *> found_funcs;
		found_funcs = getRoutines (routines[r], appImage, false);
		unsigned numfuncs = found_funcs.size();
		for (unsigned u = 0; u < numfuncs && !result; u++)
		{
			char buffer[1024];
			char *module = found_funcs[u]->getModuleName (buffer, sizeof(buffer));
			string smodule (module);
			result = smodule.find (library) == 0;
		}
	}

	return result;
}

bool ApplicationType::detectApplicationType_checkGNUOpenMPrte (BPatch_image *appImage)
{
	const char *functions[] = {"GOMP_parallel_start",
		"GOMP_loop_end",
		"GOMP_barrier"};
	vector<string> v (functions, functions+3);

	return detectApplicationType_checkOpenMPrte (v, "libgomp.so.1", appImage);
}

bool ApplicationType::detectApplicationType_checkIntelOpenMPrte (BPatch_image *appImage)
{
	const char *functions[] = {"__kmpc_fork_call",
		"__kmpc_invoke_task_func",
		"__kmpc_barrier"};
	vector<string> v (functions, functions+3);

	return detectApplicationType_checkOpenMPrte (v, "libiomp5.so", appImage);
}

bool ApplicationType::detectApplicationType_checkIBMOpenMPrte (BPatch_image *appImage)
{
	const char *functions[] = {"_xlsmpParallelDoSetup_TPO",
		"_xlsmpParRegionSetup_TPO",
		"_xlsmpWSDoSetup_TPO" };
	vector<string> v (functions, functions+3);

	return detectApplicationType_checkOpenMPrte (v, "libxlsmp.so.1", appImage);
}

void ApplicationType::detectApplicationType (BPatch_image *appImage)
{
	BPatch_Vector<BPatch_function *> found_funcs;

	isOpenMP = isMPI = isCUDA = false;

	/* Check for different implementation of OpenMP rte */
	if (detectApplicationType_checkIntelOpenMPrte (appImage))
	{
		isOpenMP = true;
		OpenMP_runtime = checkIntelOpenMPRuntime (appImage);
	}
	else if (detectApplicationType_checkGNUOpenMPrte (appImage))
	{
		isOpenMP = true;
		OpenMP_runtime = GNU_v42;
	}
	else if (detectApplicationType_checkIBMOpenMPrte (appImage))
	{
		isOpenMP = true;
		OpenMP_runtime = IBM_v16;
	}

	/* MPI applications must have MPI_Init (or mpi_init fortran names) */
	if ((appImage->findFunction ("MPI_Init", found_funcs) != NULL) ||
	    (appImage->findFunction ("mpi_init", found_funcs) != NULL) ||
	    (appImage->findFunction ("mpi_init_", found_funcs) != NULL) ||
	    (appImage->findFunction ("mpi_init__", found_funcs) != NULL) ||
	    (appImage->findFunction ("MPI_INIT", found_funcs) != NULL))
	{
		isMPI = true;
		if (appImage->findFunction ("mpi_init", found_funcs) != NULL)
			MPI_type = MPI_Fortran_0u;
		else if (appImage->findFunction ("mpi_init__", found_funcs) != NULL)
			MPI_type = MPI_Fortran_2u;
		else if (appImage->findFunction ("mpi_init_", found_funcs) != NULL)
			MPI_type = MPI_Fortran_1u;
		else if (appImage->findFunction ("MPI_INIT", found_funcs) != NULL)
			MPI_type = MPI_Fortran_ucase;
		else
			MPI_type = MPI_C;
	}

	/* Check for two typical CUDA routines in CUDA apps */
	if ((appImage->findFunction ("cudaLaunch", found_funcs) != NULL) &&
	    (appImage->findFunction ("cudaConfigureCall", found_funcs) != NULL))
		isCUDA = true;
}

void ApplicationType::dumpApplicationType (void)
{
	cout << PACKAGE_NAME << ": Detected application type: ";
	if (isOpenMP)
	{
		cout << "OpenMP ";
		if (OpenMP_runtime == GNU_v42)
			cout << "(GNU >= 4.2) ";
		else if (OpenMP_runtime == IBM_v16)
			cout << "(IBM >= 1.6) ";
		else if (OpenMP_runtime == Intel_v8_1)
			cout << "(Intel 8.x) ";
		else if (OpenMP_runtime == Intel_v9_1)
			cout << "(Intel 9.x or 10.x) ";
		else if (OpenMP_runtime == Intel_v11)
			cout << "(Intel >= 11.x) ";
		else
			cout << "(Unknown) ";
	}
	if (isMPI)
	{
		cout << "MPI ";
		if (MPI_type == MPI_C)
			cout << "(C language) ";
		else if (MPI_type == MPI_Fortran_0u)
			cout << "(Fortran language with 0 underscores) ";
		else if (MPI_type == MPI_Fortran_0u)
			cout << "(Fortran language with 1 underscore) ";
		else if (MPI_type == MPI_Fortran_0u)
			cout << "(Fortran language with 2 underscores) ";
		else if (MPI_type == MPI_Fortran_ucase)
			cout << "(Fortran language in uppercase) ";
	}
	if (!isOpenMP && !isMPI)
		cout << "Sequential ";

	if (isCUDA)
		cout << "CUDA-accelerated";

	cout << endl;
}

bool ApplicationType::isMangledOpenMProutine (string name)
{
	bool result = false;

	if (OpenMP_runtime == Intel_v8_1 || OpenMP_runtime == Intel_v9_1)
	{
		result = name.find ("__par_loop") != string::npos
		         ||
		         name.find ("__par_region") != string::npos;
	}
	else if (OpenMP_runtime == Intel_v11)
	{
		/* We don't know how to handle these */
		result = false;
	}
	else if (OpenMP_runtime == IBM_v16)
	{
		result = name.find ("$.OL$.") != string::npos;
	}
	else if (OpenMP_runtime == GNU_v42)
	{
		result = name.find (".omp_fn.") != string::npos || name.find ("._omp_fn.") != string::npos;
	}

	return result;
}
<|endoftext|>


<commit_before>/*****************************************************************************
 * Project: RooFit                                                           *
 * Package: RooFitCore                                                       *
 *    File: $Id: RooIntegratorConfig.cc,v 1.5 2002/09/05 04:33:35 verkerke Exp $
 * Authors:                                                                  *
 *   WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu       *
 *   DK, David Kirkby,    UC Irvine,         dkirkby@uci.edu                 *
 *                                                                           *
 * Copyright (c) 2000-2002, Regents of the University of California          *
 *                          and Stanford University. All rights reserved.    *
 *                                                                           *
 * Redistribution and use in source and binary forms,                        *
 * with or without modification, are permitted according to the terms        *
 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)             *
 *****************************************************************************/

// -- CLASS DESCRIPTION [MISC] --
// RooIntegratorConfig holds the configuration parameters of the various
// numeric integrators used by RooRealIntegral. RooRealIntegral and RooAbsPdf
// use this class in the (normalization) integral configuration interface

#include "RooFitCore/RooIntegratorConfig.hh"

ClassImp(RooIntegratorConfig)
;

RooIntegratorConfig::RooIntegratorConfig()
{
  // 1D integrator
  _rule = RooIntegrator1D::Trapezoid ;
  _maxSteps = 20 ;
  _epsRel = 1e-6 ;
  _epsAbs = 1e-6 ;

  // 2D integrator
  _useMCFor2D = kFALSE ;

  // MC Integrator
  _mode = RooMCIntegrator::Importance ;
  _genType = RooMCIntegrator::QuasiRandom ;
  _verboseMC = kFALSE ;
  _alpha = 1.5  ;
  _nRefineIter = 5  ;
  _nRefinePerDim = 1000 ;
  _nIntegratePerDim = 5000 ;
}

RooIntegratorConfig::~RooIntegratorConfig()
{
}

RooIntegratorConfig::RooIntegratorConfig(const RooIntegratorConfig& other)
{
  // 1D integrator
  _rule = other._rule ;
  _maxSteps = other._maxSteps ;
  _epsRel = other._epsRel ;
  _epsAbs = other._epsAbs ;

  // 2D integrator
  _useMCFor2D = other._useMCFor2D ;

  // MC Integrator
  _mode = other._mode ;
  _genType = other._genType ;
  _verboseMC = other._verboseMC ;
  _alpha = other._alpha ;
  _nRefineIter = other._nRefineIter ;
  _nRefinePerDim = other._nRefinePerDim ;
  _nIntegratePerDim = other._nIntegratePerDim ;
}
<commit_msg><commit_after>/*****************************************************************************
 * Project: RooFit                                                           *
 * Package: RooFitCore                                                       *
 *    File: $Id: RooIntegratorConfig.cc,v 1.6 2003/05/07 21:06:25 wverkerke Exp $
 * Authors:                                                                  *
 *   WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu       *
 *   DK, David Kirkby,    UC Irvine,         dkirkby@uci.edu                 *
 *                                                                           *
 * Copyright (c) 2000-2002, Regents of the University of California          *
 *                          and Stanford University. All rights reserved.    *
 *                                                                           *
 * Redistribution and use in source and binary forms,                        *
 * with or without modification, are permitted according to the terms        *
 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)             *
 *****************************************************************************/

// -- CLASS DESCRIPTION [MISC] --
// RooIntegratorConfig holds the configuration parameters of the various
// numeric integrators used by RooRealIntegral. RooRealIntegral and RooAbsPdf
// use this class in the (normalization) integral configuration interface

#include "RooFitCore/RooIntegratorConfig.hh"

ClassImp(RooIntegratorConfig)
;

RooIntegratorConfig::RooIntegratorConfig()
{
  // 1D integrator
  _rule = RooIntegrator1D::Trapezoid ;
  _maxSteps = 20 ;
  _epsRel = 1e-6 ;
  _epsAbs = 1e-6 ;

  // 2D integrator
  _useMCFor2D = kTRUE ;

  // MC Integrator
  _mode = RooMCIntegrator::Importance ;
  _genType = RooMCIntegrator::QuasiRandom ;
  _verboseMC = kFALSE ;
  _alpha = 1.5  ;
  _nRefineIter = 5  ;
  _nRefinePerDim = 1000 ;
  _nIntegratePerDim = 5000 ;
}

RooIntegratorConfig::~RooIntegratorConfig()
{
}

RooIntegratorConfig::RooIntegratorConfig(const RooIntegratorConfig& other)
{
  // 1D integrator
  _rule = other._rule ;
  _maxSteps = other._maxSteps ;
  _epsRel = other._epsRel ;
  _epsAbs = other._epsAbs ;

  // 2D integrator
  _useMCFor2D = other._useMCFor2D ;

  // MC Integrator
  _mode = other._mode ;
  _genType = other._genType ;
  _verboseMC = other._verboseMC ;
  _alpha = other._alpha ;
  _nRefineIter = other._nRefineIter ;
  _nRefinePerDim = other._nRefinePerDim ;
  _nIntegratePerDim = other._nIntegratePerDim ;
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

//TESTED_COMPONENT=src/systeminfo

#include <QtTest/QtTest>
#include "qsysteminfo.h"
#include "../qsysteminfotestcommon.h"

QTM_USE_NAMESPACE
Q_DECLARE_METATYPE(QSystemDeviceInfo::BatteryStatus);
Q_DECLARE_METATYPE(QSystemDeviceInfo::PowerState);
Q_DECLARE_METATYPE(QSystemDeviceInfo::InputMethodFlags);
Q_DECLARE_METATYPE(QSystemDeviceInfo::Profile);
Q_DECLARE_METATYPE(QSystemDeviceInfo::SimStatus);

Q_DECLARE_METATYPE(QSystemDeviceInfo::KeyboardTypeFlags);

class tst_QSystemDeviceInfo : public QObject
{
    Q_OBJECT

private slots:

    void initTestCase();
    void tst_inputMethodType();

    void tst_imei();
    void tst_imsi();
    void tst_manufacturer();
    void tst_model();
    void tst_productName();

    void tst_batteryLevel();
    void tst_batteryStatus();

    void tst_currentProfile();

    void tst_simStatus();

    void tst_isDeviceLocked();

    void tst_currentPowerState();

    void tst_currentBluetoothPowerState();

    void tst_keyboardType();
    void tst_isWirelessKeyboardConnected();
    void tst_isKeyboardFlipOpen();

};
/*
signal todo:
//    void profileChanged(QSystemDeviceInfo::Profile);
    void batteryLevelChanged(QSystemDeviceInfo::BatteryLevel);
    void batteryLevelCritical(qint32);
    void powerStateChanged(QSystemDeviceInfo::PowerState);
  */

void tst_QSystemDeviceInfo::initTestCase()
{
    qRegisterMetaType<QSystemDeviceInfo::BatteryStatus>("QSystemDeviceInfo::BatteryStatus");
    qRegisterMetaType<QSystemDeviceInfo::PowerState>("QSystemDeviceInfo::PowerState");

    qRegisterMetaType<QSystemDeviceInfo::InputMethodFlags>("QSystemDeviceInfo::InputMethodFlags");
    qRegisterMetaType<QSystemDeviceInfo::Profile>("QSystemDeviceInfo::Profile");

    qRegisterMetaType<QSystemDeviceInfo::SimStatus>("QSystemDeviceInfo::SimStatus");

    qRegisterMetaType<QSystemDeviceInfo::KeyboardTypeFlags>("QSystemDeviceInfo::KeyboardTypeFlags");

}

void tst_QSystemDeviceInfo::tst_inputMethodType()
{
    QSystemDeviceInfo di;
    QVERIFY( di.inputMethodType() != 0
        || di.inputMethodType() == 0);
}

void tst_QSystemDeviceInfo::tst_imei()
{
    QSystemDeviceInfo di;
    QString imeiStr =di.imei();
    QVERIFY(!imeiStr.isEmpty() || imeiStr.isEmpty());

}

void tst_QSystemDeviceInfo::tst_imsi()
{
    QSystemDeviceInfo di;
    QString imsiStr = di.imsi();
    QVERIFY(!imsiStr.isEmpty() || imsiStr.isEmpty());

}

void tst_QSystemDeviceInfo::tst_manufacturer()
{
    QSystemDeviceInfo di;
    QString manu = di.manufacturer();
    QVERIFY(!manu.isEmpty() || manu.isEmpty());

}

void tst_QSystemDeviceInfo::tst_model()
{
    QSystemDeviceInfo di;
    QString model = di.model();
    QVERIFY(!model.isEmpty() || model.isEmpty());

}

void tst_QSystemDeviceInfo::tst_productName()
{
    QSystemDeviceInfo di;
    QString product = di.productName();
    QVERIFY(!product.isEmpty() | product.isEmpty());

}

void tst_QSystemDeviceInfo::tst_batteryLevel()
{
    QSystemDeviceInfo di;
    QVERIFY(di.batteryLevel() > -1);

// until we simulate this, or wait the signalspy to olong, this will always fail
//    if(di.currentPowerState() == QSystemDeviceInfo::WallPowerChargingBattery) {
//        QSignalSpy batSpy(&di, SIGNAL(batteryLevelChanged(int)));
//        QVERIFY(!batSpy.isEmpty());
//        int level = batSpy.first().at(0).toInt();
//        QVERIFY( level > -1 || level < 101);
//    }
}

void tst_QSystemDeviceInfo::tst_batteryStatus()
{
    QSystemDeviceInfo di;
    int level = di.batteryLevel();
    if(level < 4) {
        QVERIFY(di.batteryStatus() ==QSystemDeviceInfo::BatteryCritical );
    }   else if(level < 11) {
        QVERIFY(di.batteryStatus() == QSystemDeviceInfo::BatteryVeryLow);
    }  else if(level < 41) {
        QVERIFY(di.batteryStatus() == QSystemDeviceInfo::BatteryLow);
    }   else if(level > 40) {
        QVERIFY(di.batteryStatus() == QSystemDeviceInfo::BatteryNormal);
    }

    // until we simulate this, or wait the signalspy to olong, this will always fail
//    if(di.currentPowerState() == QSystemDeviceInfo::WallPowerChargingBattery) {
//        QSignalSpy batSpy(&di, SIGNAL(batteryStatusChanged(QSystemDeviceInfo::BatteryStatus)));
//        QVERIFY(!batSpy.isEmpty());
//        QSystemDeviceInfo::BatteryStatus status = qvariant_cast<QSystemDeviceInfo::BatteryStatus>(batSpy.first().at(0));
//        QVERIFY( status == QSystemDeviceInfo::NoBatteryLevel
//                 || status == QSystemDeviceInfo::BatteryCritical
//                 || status == QSystemDeviceInfo::BatteryVeryLow
//                 || status == QSystemDeviceInfo::BatteryLow
//                 || status == QSystemDeviceInfo::BatteryNormal);
//    }
}

void tst_QSystemDeviceInfo::tst_currentProfile()
{
    QSystemDeviceInfo di;
    QSystemDeviceInfo::Profile profile = di.currentProfile();
    QVERIFY( profile == QSystemDeviceInfo::UnknownProfile
             || profile == QSystemDeviceInfo::SilentProfile
             || profile == QSystemDeviceInfo::NormalProfile
             || profile == QSystemDeviceInfo::LoudProfile
             || profile == QSystemDeviceInfo::VibProfile
             || profile == QSystemDeviceInfo::OfflineProfile
             || profile == QSystemDeviceInfo::PowersaveProfile
             || profile == QSystemDeviceInfo::CustomProfile);
}

void tst_QSystemDeviceInfo::tst_simStatus()
{
    QSystemDeviceInfo di;
    bool simStat = di.simStatus();
    QVERIFY(simStat == true || simStat == false);

}

void tst_QSystemDeviceInfo::tst_isDeviceLocked()
{
    QSystemDeviceInfo di;
    bool devLock = di.isDeviceLocked();
    QVERIFY(devLock == true || devLock == false);
}

void tst_QSystemDeviceInfo::tst_currentPowerState()
{
    QSystemDeviceInfo di;
    QSystemDeviceInfo::PowerState state = di.currentPowerState();
    QVERIFY( state == QSystemDeviceInfo::UnknownPower
             || state == QSystemDeviceInfo::BatteryPower
             || state == QSystemDeviceInfo::WallPower
             || state == QSystemDeviceInfo::WallPowerChargingBattery);
}

void tst_QSystemDeviceInfo::tst_currentBluetoothPowerState()
{
    QSystemDeviceInfo di;
    bool state = di.currentPowerState();
    QVERIFY(state || !state);
}

void tst_QSystemDeviceInfo::tst_keyboardType()
{
    QSystemDeviceInfo di;
    QSystemDeviceInfo::KeyboardTypeFlags  flags = di.keyboardType();

    QVERIFY( (flags && QSystemDeviceInfo::UnknownKeyboard == QSystemDeviceInfo::UnknownKeyboard)
             || (flags && QSystemDeviceInfo::SoftwareKeyboard ==  QSystemDeviceInfo::SoftwareKeyboard)
             || (flags && QSystemDeviceInfo::ITUKeypad ==  QSystemDeviceInfo::ITUKeypad)
             || (flags && QSystemDeviceInfo::HalfQwertyKeyboard == QSystemDeviceInfo::HalfQwertyKeyboard)
             || (flags && QSystemDeviceInfo::FullQwertyKeyboard == QSystemDeviceInfo::FullQwertyKeyboard)
             || (flags && QSystemDeviceInfo::WirelessKeyboard ==  QSystemDeviceInfo::WirelessKeyboard));
}

void tst_QSystemDeviceInfo::tst_isWirelessKeyboardConnected()
{
    QSystemDeviceInfo di;
   bool on = di.isWirelessKeyboardConnected();
   QVERIFY(on || !on);
}

void tst_QSystemDeviceInfo::tst_isKeyboardFlipOpen()
{
    QSystemDeviceInfo di;
   bool on = di.isKeyboardFlipOpen();
   QVERIFY(on || !on);
}

QTEST_MAIN(tst_QSystemDeviceInfo)
#include "tst_qsystemdeviceinfo.moc"
<commit_msg>add some tests fro new functions<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

//TESTED_COMPONENT=src/systeminfo

#include <QtTest/QtTest>
#include "qsysteminfo.h"
#include "../qsysteminfotestcommon.h"

QTM_USE_NAMESPACE
Q_DECLARE_METATYPE(QSystemDeviceInfo::BatteryStatus);
Q_DECLARE_METATYPE(QSystemDeviceInfo::PowerState);
Q_DECLARE_METATYPE(QSystemDeviceInfo::InputMethodFlags);
Q_DECLARE_METATYPE(QSystemDeviceInfo::Profile);
Q_DECLARE_METATYPE(QSystemDeviceInfo::SimStatus);

Q_DECLARE_METATYPE(QSystemDeviceInfo::KeyboardTypeFlags);

class tst_QSystemDeviceInfo : public QObject
{
    Q_OBJECT

private slots:

    void initTestCase();
    void tst_inputMethodType();

    void tst_imei();
    void tst_imsi();
    void tst_manufacturer();
    void tst_model();
    void tst_productName();

    void tst_batteryLevel();
    void tst_batteryStatus();

    void tst_currentProfile();

    void tst_simStatus();

    void tst_isDeviceLocked();

    void tst_currentPowerState();

    void tst_currentBluetoothPowerState();

    void tst_keyboardType();
    void tst_isWirelessKeyboardConnected();
    void tst_isKeyboardFlipOpen();
    void tst_keypadLightOn();
    void tst_backLightOn();
    void tst_hostId();

};
/*
signal todo:
//    void profileChanged(QSystemDeviceInfo::Profile);
    void batteryLevelChanged(QSystemDeviceInfo::BatteryLevel);
    void batteryLevelCritical(qint32);
    void powerStateChanged(QSystemDeviceInfo::PowerState);
  */

void tst_QSystemDeviceInfo::initTestCase()
{
    qRegisterMetaType<QSystemDeviceInfo::BatteryStatus>("QSystemDeviceInfo::BatteryStatus");
    qRegisterMetaType<QSystemDeviceInfo::PowerState>("QSystemDeviceInfo::PowerState");

    qRegisterMetaType<QSystemDeviceInfo::InputMethodFlags>("QSystemDeviceInfo::InputMethodFlags");
    qRegisterMetaType<QSystemDeviceInfo::Profile>("QSystemDeviceInfo::Profile");

    qRegisterMetaType<QSystemDeviceInfo::SimStatus>("QSystemDeviceInfo::SimStatus");

    qRegisterMetaType<QSystemDeviceInfo::KeyboardTypeFlags>("QSystemDeviceInfo::KeyboardTypeFlags");

}

void tst_QSystemDeviceInfo::tst_inputMethodType()
{
    QSystemDeviceInfo di;
    QVERIFY( di.inputMethodType() != 0
        || di.inputMethodType() == 0);
}

void tst_QSystemDeviceInfo::tst_imei()
{
    QSystemDeviceInfo di;
    QString imeiStr =di.imei();
    QVERIFY(!imeiStr.isEmpty() || imeiStr.isEmpty());

}

void tst_QSystemDeviceInfo::tst_imsi()
{
    QSystemDeviceInfo di;
    QString imsiStr = di.imsi();
    QVERIFY(!imsiStr.isEmpty() || imsiStr.isEmpty());

}

void tst_QSystemDeviceInfo::tst_manufacturer()
{
    QSystemDeviceInfo di;
    QString manu = di.manufacturer();
    QVERIFY(!manu.isEmpty() || manu.isEmpty());

}

void tst_QSystemDeviceInfo::tst_model()
{
    QSystemDeviceInfo di;
    QString model = di.model();
    QVERIFY(!model.isEmpty() || model.isEmpty());

}

void tst_QSystemDeviceInfo::tst_productName()
{
    QSystemDeviceInfo di;
    QString product = di.productName();
    QVERIFY(!product.isEmpty() | product.isEmpty());

}

void tst_QSystemDeviceInfo::tst_batteryLevel()
{
    QSystemDeviceInfo di;
    QVERIFY(di.batteryLevel() > -1);

// until we simulate this, or wait the signalspy to olong, this will always fail
//    if(di.currentPowerState() == QSystemDeviceInfo::WallPowerChargingBattery) {
//        QSignalSpy batSpy(&di, SIGNAL(batteryLevelChanged(int)));
//        QVERIFY(!batSpy.isEmpty());
//        int level = batSpy.first().at(0).toInt();
//        QVERIFY( level > -1 || level < 101);
//    }
}

void tst_QSystemDeviceInfo::tst_batteryStatus()
{
    QSystemDeviceInfo di;
    int level = di.batteryLevel();
    if(level < 4) {
        QVERIFY(di.batteryStatus() ==QSystemDeviceInfo::BatteryCritical );
    }   else if(level < 11) {
        QVERIFY(di.batteryStatus() == QSystemDeviceInfo::BatteryVeryLow);
    }  else if(level < 41) {
        QVERIFY(di.batteryStatus() == QSystemDeviceInfo::BatteryLow);
    }   else if(level > 40) {
        QVERIFY(di.batteryStatus() == QSystemDeviceInfo::BatteryNormal);
    }

    // until we simulate this, or wait the signalspy to olong, this will always fail
//    if(di.currentPowerState() == QSystemDeviceInfo::WallPowerChargingBattery) {
//        QSignalSpy batSpy(&di, SIGNAL(batteryStatusChanged(QSystemDeviceInfo::BatteryStatus)));
//        QVERIFY(!batSpy.isEmpty());
//        QSystemDeviceInfo::BatteryStatus status = qvariant_cast<QSystemDeviceInfo::BatteryStatus>(batSpy.first().at(0));
//        QVERIFY( status == QSystemDeviceInfo::NoBatteryLevel
//                 || status == QSystemDeviceInfo::BatteryCritical
//                 || status == QSystemDeviceInfo::BatteryVeryLow
//                 || status == QSystemDeviceInfo::BatteryLow
//                 || status == QSystemDeviceInfo::BatteryNormal);
//    }
}

void tst_QSystemDeviceInfo::tst_currentProfile()
{
    QSystemDeviceInfo di;
    QSystemDeviceInfo::Profile profile = di.currentProfile();
    QVERIFY( profile == QSystemDeviceInfo::UnknownProfile
             || profile == QSystemDeviceInfo::SilentProfile
             || profile == QSystemDeviceInfo::NormalProfile
             || profile == QSystemDeviceInfo::LoudProfile
             || profile == QSystemDeviceInfo::VibProfile
             || profile == QSystemDeviceInfo::OfflineProfile
             || profile == QSystemDeviceInfo::PowersaveProfile
             || profile == QSystemDeviceInfo::CustomProfile);
}

void tst_QSystemDeviceInfo::tst_simStatus()
{
    QSystemDeviceInfo di;
    bool simStat = di.simStatus();
    QVERIFY(simStat == true || simStat == false);

}

void tst_QSystemDeviceInfo::tst_isDeviceLocked()
{
    QSystemDeviceInfo di;
    bool devLock = di.isDeviceLocked();
    QVERIFY(devLock == true || devLock == false);
}

void tst_QSystemDeviceInfo::tst_currentPowerState()
{
    QSystemDeviceInfo di;
    QSystemDeviceInfo::PowerState state = di.currentPowerState();
    QVERIFY( state == QSystemDeviceInfo::UnknownPower
             || state == QSystemDeviceInfo::BatteryPower
             || state == QSystemDeviceInfo::WallPower
             || state == QSystemDeviceInfo::WallPowerChargingBattery);
}

void tst_QSystemDeviceInfo::tst_currentBluetoothPowerState()
{
    QSystemDeviceInfo di;
    bool state = di.currentPowerState();
    QVERIFY(state || !state);
}

void tst_QSystemDeviceInfo::tst_keyboardType()
{
    QSystemDeviceInfo di;
    QSystemDeviceInfo::KeyboardTypeFlags  flags = di.keyboardType();

    QVERIFY( (flags && QSystemDeviceInfo::UnknownKeyboard == QSystemDeviceInfo::UnknownKeyboard)
             || (flags && QSystemDeviceInfo::SoftwareKeyboard ==  QSystemDeviceInfo::SoftwareKeyboard)
             || (flags && QSystemDeviceInfo::ITUKeypad ==  QSystemDeviceInfo::ITUKeypad)
             || (flags && QSystemDeviceInfo::HalfQwertyKeyboard == QSystemDeviceInfo::HalfQwertyKeyboard)
             || (flags && QSystemDeviceInfo::FullQwertyKeyboard == QSystemDeviceInfo::FullQwertyKeyboard)
             || (flags && QSystemDeviceInfo::WirelessKeyboard ==  QSystemDeviceInfo::WirelessKeyboard));
}

void tst_QSystemDeviceInfo::tst_isWirelessKeyboardConnected()
{
    QSystemDeviceInfo di;
   bool on = di.isWirelessKeyboardConnected();
   QVERIFY(on || !on);
}

void tst_QSystemDeviceInfo::tst_isKeyboardFlipOpen()
{
    QSystemDeviceInfo di;
    bool on = di.isKeyboardFlipOpen();
    QVERIFY(on || !on);
}

void tst_QSystemDeviceInfo::tst_keypadLightOn()
{
    QSystemDeviceInfo di;
    bool on = di.keypadLightOn();
    QVERIFY(on || !on);
}

void tst_QSystemDeviceInfo::tst_backLightOn()
{
    QSystemDeviceInfo di;
    bool on = di.backLightOn();
    QVERIFY(on || !on);
}

void tst_QSystemDeviceInfo::tst_hostId()
{
    QSystemDeviceInfo di;
    quint64 id = di.hostId();
    qDebug() << id;
    QVERIFY(id == 0 || id > 0);
}

QTEST_MAIN(tst_QSystemDeviceInfo)
#include "tst_qsystemdeviceinfo.moc"
<|endoftext|>


<commit_before>// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <chainparams.h>
#include <compat/compat.h>
#include <compat/endian.h>
#include <crypto/sha256.h>
#include <fs.h>
#include <i2p.h>
#include <logging.h>
#include <netaddress.h>
#include <netbase.h>
#include <random.h>
#include <tinyformat.h>
#include <util/readwritefile.h>
#include <util/sock.h>
#include <util/spanparsing.h>
#include <util/strencodings.h>
#include <util/system.h>

#include <chrono>
#include <memory>
#include <stdexcept>
#include <string>

namespace i2p {

/**
 * Swap Standard Base64 <-> I2P Base64.
 * Standard Base64 uses `+` and `/` as last two characters of its alphabet.
 * I2P Base64 uses `-` and `~` respectively.
 * So it is easy to detect in which one is the input and convert to the other.
 * @param[in] from Input to convert.
 * @return converted `from`
 */
static std::string SwapBase64(const std::string& from)
{
    std::string to;
    to.resize(from.size());
    for (size_t i = 0; i < from.size(); ++i) {
        switch (from[i]) {
        case '-':
            to[i] = '+';
            break;
        case '~':
            to[i] = '/';
            break;
        case '+':
            to[i] = '-';
            break;
        case '/':
            to[i] = '~';
            break;
        default:
            to[i] = from[i];
            break;
        }
    }
    return to;
}

/**
 * Decode an I2P-style Base64 string.
 * @param[in] i2p_b64 I2P-style Base64 string.
 * @return decoded `i2p_b64`
 * @throw std::runtime_error if decoding fails
 */
static Binary DecodeI2PBase64(const std::string& i2p_b64)
{
    const std::string& std_b64 = SwapBase64(i2p_b64);
    auto decoded = DecodeBase64(std_b64);
    if (!decoded) {
        throw std::runtime_error(strprintf("Cannot decode Base64: \"%s\"", i2p_b64));
    }
    return std::move(*decoded);
}

/**
 * Derive the .b32.i2p address of an I2P destination (binary).
 * @param[in] dest I2P destination.
 * @return the address that corresponds to `dest`
 * @throw std::runtime_error if conversion fails
 */
static CNetAddr DestBinToAddr(const Binary& dest)
{
    CSHA256 hasher;
    hasher.Write(dest.data(), dest.size());
    unsigned char hash[CSHA256::OUTPUT_SIZE];
    hasher.Finalize(hash);

    CNetAddr addr;
    const std::string addr_str = EncodeBase32(hash, false) + ".b32.i2p";
    if (!addr.SetSpecial(addr_str)) {
        throw std::runtime_error(strprintf("Cannot parse I2P address: \"%s\"", addr_str));
    }

    return addr;
}

/**
 * Derive the .b32.i2p address of an I2P destination (I2P-style Base64).
 * @param[in] dest I2P destination.
 * @return the address that corresponds to `dest`
 * @throw std::runtime_error if conversion fails
 */
static CNetAddr DestB64ToAddr(const std::string& dest)
{
    const Binary& decoded = DecodeI2PBase64(dest);
    return DestBinToAddr(decoded);
}

namespace sam {

Session::Session(const fs::path& private_key_file,
                 const CService& control_host,
                 CThreadInterrupt* interrupt)
    : m_private_key_file{private_key_file},
      m_control_host{control_host},
      m_interrupt{interrupt},
      m_control_sock{std::make_unique<Sock>(INVALID_SOCKET)},
      m_transient{false}
{
}

Session::Session(const CService& control_host, CThreadInterrupt* interrupt)
    : m_control_host{control_host},
      m_interrupt{interrupt},
      m_control_sock{std::make_unique<Sock>(INVALID_SOCKET)},
      m_transient{true}
{
}

Session::~Session()
{
    LOCK(m_mutex);
    Disconnect();
}

bool Session::Listen(Connection& conn)
{
    try {
        LOCK(m_mutex);
        CreateIfNotCreatedAlready();
        conn.me = m_my_addr;
        conn.sock = StreamAccept();
        return true;
    } catch (const std::runtime_error& e) {
        Log("Error listening: %s", e.what());
        CheckControlSock();
    }
    return false;
}

bool Session::Accept(Connection& conn)
{
    try {
        while (!*m_interrupt) {
            Sock::Event occurred;
            if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
                throw std::runtime_error("wait on socket failed");
            }

            if (occurred == 0) {
                // Timeout, no incoming connections or errors within MAX_WAIT_FOR_IO.
                continue;
            }

            const std::string& peer_dest =
                conn.sock->RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);

            conn.peer = CService(DestB64ToAddr(peer_dest), I2P_SAM31_PORT);

            return true;
        }
    } catch (const std::runtime_error& e) {
        Log("Error accepting: %s", e.what());
        CheckControlSock();
    }
    return false;
}

bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
{
    // Refuse connecting to arbitrary ports. We don't specify any destination port to the SAM proxy
    // when connecting (SAM 3.1 does not use ports) and it forces/defaults it to I2P_SAM31_PORT.
    if (to.GetPort() != I2P_SAM31_PORT) {
        proxy_error = false;
        return false;
    }

    proxy_error = true;

    std::string session_id;
    std::unique_ptr<Sock> sock;
    conn.peer = to;

    try {
        {
            LOCK(m_mutex);
            CreateIfNotCreatedAlready();
            session_id = m_session_id;
            conn.me = m_my_addr;
            sock = Hello();
        }

        const Reply& lookup_reply =
            SendRequestAndGetReply(*sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringIP()));

        const std::string& dest = lookup_reply.Get("VALUE");

        const Reply& connect_reply = SendRequestAndGetReply(
            *sock, strprintf("STREAM CONNECT ID=%s DESTINATION=%s SILENT=false", session_id, dest),
            false);

        const std::string& result = connect_reply.Get("RESULT");

        if (result == "OK") {
            conn.sock = std::move(sock);
            return true;
        }

        if (result == "INVALID_ID") {
            LOCK(m_mutex);
            Disconnect();
            throw std::runtime_error("Invalid session id");
        }

        if (result == "CANT_REACH_PEER" || result == "TIMEOUT") {
            proxy_error = false;
        }

        throw std::runtime_error(strprintf("\"%s\"", connect_reply.full));
    } catch (const std::runtime_error& e) {
        Log("Error connecting to %s: %s", to.ToString(), e.what());
        CheckControlSock();
        return false;
    }
}

// Private methods

std::string Session::Reply::Get(const std::string& key) const
{
    const auto& pos = keys.find(key);
    if (pos == keys.end() || !pos->second.has_value()) {
        throw std::runtime_error(
            strprintf("Missing %s= in the reply to \"%s\": \"%s\"", key, request, full));
    }
    return pos->second.value();
}

template <typename... Args>
void Session::Log(const std::string& fmt, const Args&... args) const
{
    LogPrint(BCLog::I2P, "%s\n", tfm::format(fmt, args...));
}

Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
                                               const std::string& request,
                                               bool check_result_ok) const
{
    sock.SendComplete(request + "\n", MAX_WAIT_FOR_IO, *m_interrupt);

    Reply reply;

    // Don't log the full "SESSION CREATE ..." because it contains our private key.
    reply.request = request.substr(0, 14) == "SESSION CREATE" ? "SESSION CREATE ..." : request;

    // It could take a few minutes for the I2P router to reply as it is querying the I2P network
    // (when doing name lookup, for example). Notice: `RecvUntilTerminator()` is checking
    // `m_interrupt` more often, so we would not be stuck here for long if `m_interrupt` is
    // signaled.
    static constexpr auto recv_timeout = 3min;

    reply.full = sock.RecvUntilTerminator('\n', recv_timeout, *m_interrupt, MAX_MSG_SIZE);

    for (const auto& kv : spanparsing::Split(reply.full, ' ')) {
        const auto& pos = std::find(kv.begin(), kv.end(), '=');
        if (pos != kv.end()) {
            reply.keys.emplace(std::string{kv.begin(), pos}, std::string{pos + 1, kv.end()});
        } else {
            reply.keys.emplace(std::string{kv.begin(), kv.end()}, std::nullopt);
        }
    }

    if (check_result_ok && reply.Get("RESULT") != "OK") {
        throw std::runtime_error(
            strprintf("Unexpected reply to \"%s\": \"%s\"", request, reply.full));
    }

    return reply;
}

std::unique_ptr<Sock> Session::Hello() const
{
    auto sock = CreateSock(m_control_host);

    if (!sock) {
        throw std::runtime_error("Cannot create socket");
    }

    if (!ConnectSocketDirectly(m_control_host, *sock, nConnectTimeout, true)) {
        throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
    }

    SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");

    return sock;
}

void Session::CheckControlSock()
{
    LOCK(m_mutex);

    std::string errmsg;
    if (!m_control_sock->IsConnected(errmsg)) {
        Log("Control socket error: %s", errmsg);
        Disconnect();
    }
}

void Session::DestGenerate(const Sock& sock)
{
    // https://geti2p.net/spec/common-structures#key-certificates
    // "7" or "EdDSA_SHA512_Ed25519" - "Recent Router Identities and Destinations".
    // Use "7" because i2pd <2.24.0 does not recognize the textual form.
    const Reply& reply = SendRequestAndGetReply(sock, "DEST GENERATE SIGNATURE_TYPE=7", false);

    m_private_key = DecodeI2PBase64(reply.Get("PRIV"));
}

void Session::GenerateAndSavePrivateKey(const Sock& sock)
{
    DestGenerate(sock);

    // umask is set to 077 in init.cpp, which is ok (unless -sysperms is given)
    if (!WriteBinaryFile(m_private_key_file,
                         std::string(m_private_key.begin(), m_private_key.end()))) {
        throw std::runtime_error(
            strprintf("Cannot save I2P private key to %s", fs::quoted(fs::PathToString(m_private_key_file))));
    }
}

Binary Session::MyDestination() const
{
    // From https://geti2p.net/spec/common-structures#destination:
    // "They are 387 bytes plus the certificate length specified at bytes 385-386, which may be
    // non-zero"
    static constexpr size_t DEST_LEN_BASE = 387;
    static constexpr size_t CERT_LEN_POS = 385;

    uint16_t cert_len;
    memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS), sizeof(cert_len));
    cert_len = be16toh(cert_len);

    const size_t dest_len = DEST_LEN_BASE + cert_len;

    return Binary{m_private_key.begin(), m_private_key.begin() + dest_len};
}

void Session::CreateIfNotCreatedAlready()
{
    std::string errmsg;
    if (m_control_sock->IsConnected(errmsg)) {
        return;
    }

    const auto session_type = m_transient ? "transient" : "persistent";
    const auto session_id = GetRandHash().GetHex().substr(0, 10); // full is overkill, too verbose in the logs

    Log("Creating %s SAM session %s with %s", session_type, session_id, m_control_host.ToString());

    auto sock = Hello();

    if (m_transient) {
        // The destination (private key) is generated upon session creation and returned
        // in the reply in DESTINATION=.
        const Reply& reply = SendRequestAndGetReply(
            *sock,
            strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT", session_id));

        m_private_key = DecodeI2PBase64(reply.Get("DESTINATION"));
    } else {
        // Read our persistent destination (private key) from disk or generate
        // one and save it to disk. Then use it when creating the session.
        const auto& [read_ok, data] = ReadBinaryFile(m_private_key_file);
        if (read_ok) {
            m_private_key.assign(data.begin(), data.end());
        } else {
            GenerateAndSavePrivateKey(*sock);
        }

        const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));

        SendRequestAndGetReply(*sock,
                               strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
                                         session_id,
                                         private_key_b64));
    }

    m_my_addr = CService(DestBinToAddr(MyDestination()), I2P_SAM31_PORT);
    m_session_id = session_id;
    m_control_sock = std::move(sock);

    Log("%s SAM session %s created, my address=%s",
        Capitalize(session_type),
        m_session_id,
        m_my_addr.ToString());
}

std::unique_ptr<Sock> Session::StreamAccept()
{
    auto sock = Hello();

    const Reply& reply = SendRequestAndGetReply(
        *sock, strprintf("STREAM ACCEPT ID=%s SILENT=false", m_session_id), false);

    const std::string& result = reply.Get("RESULT");

    if (result == "OK") {
        return sock;
    }

    if (result == "INVALID_ID") {
        // If our session id is invalid, then force session re-creation on next usage.
        Disconnect();
    }

    throw std::runtime_error(strprintf("\"%s\"", reply.full));
}

void Session::Disconnect()
{
    if (m_control_sock->Get() != INVALID_SOCKET) {
        if (m_session_id.empty()) {
            Log("Destroying incomplete session");
        } else {
            Log("Destroying session %s", m_session_id);
        }
    }
    m_control_sock = std::make_unique<Sock>(INVALID_SOCKET);
    m_session_id.clear();
}
} // namespace sam
} // namespace i2p
<commit_msg>i2p: log "SAM session" instead of "session"<commit_after>// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <chainparams.h>
#include <compat/compat.h>
#include <compat/endian.h>
#include <crypto/sha256.h>
#include <fs.h>
#include <i2p.h>
#include <logging.h>
#include <netaddress.h>
#include <netbase.h>
#include <random.h>
#include <tinyformat.h>
#include <util/readwritefile.h>
#include <util/sock.h>
#include <util/spanparsing.h>
#include <util/strencodings.h>
#include <util/system.h>

#include <chrono>
#include <memory>
#include <stdexcept>
#include <string>

namespace i2p {

/**
 * Swap Standard Base64 <-> I2P Base64.
 * Standard Base64 uses `+` and `/` as last two characters of its alphabet.
 * I2P Base64 uses `-` and `~` respectively.
 * So it is easy to detect in which one is the input and convert to the other.
 * @param[in] from Input to convert.
 * @return converted `from`
 */
static std::string SwapBase64(const std::string& from)
{
    std::string to;
    to.resize(from.size());
    for (size_t i = 0; i < from.size(); ++i) {
        switch (from[i]) {
        case '-':
            to[i] = '+';
            break;
        case '~':
            to[i] = '/';
            break;
        case '+':
            to[i] = '-';
            break;
        case '/':
            to[i] = '~';
            break;
        default:
            to[i] = from[i];
            break;
        }
    }
    return to;
}

/**
 * Decode an I2P-style Base64 string.
 * @param[in] i2p_b64 I2P-style Base64 string.
 * @return decoded `i2p_b64`
 * @throw std::runtime_error if decoding fails
 */
static Binary DecodeI2PBase64(const std::string& i2p_b64)
{
    const std::string& std_b64 = SwapBase64(i2p_b64);
    auto decoded = DecodeBase64(std_b64);
    if (!decoded) {
        throw std::runtime_error(strprintf("Cannot decode Base64: \"%s\"", i2p_b64));
    }
    return std::move(*decoded);
}

/**
 * Derive the .b32.i2p address of an I2P destination (binary).
 * @param[in] dest I2P destination.
 * @return the address that corresponds to `dest`
 * @throw std::runtime_error if conversion fails
 */
static CNetAddr DestBinToAddr(const Binary& dest)
{
    CSHA256 hasher;
    hasher.Write(dest.data(), dest.size());
    unsigned char hash[CSHA256::OUTPUT_SIZE];
    hasher.Finalize(hash);

    CNetAddr addr;
    const std::string addr_str = EncodeBase32(hash, false) + ".b32.i2p";
    if (!addr.SetSpecial(addr_str)) {
        throw std::runtime_error(strprintf("Cannot parse I2P address: \"%s\"", addr_str));
    }

    return addr;
}

/**
 * Derive the .b32.i2p address of an I2P destination (I2P-style Base64).
 * @param[in] dest I2P destination.
 * @return the address that corresponds to `dest`
 * @throw std::runtime_error if conversion fails
 */
static CNetAddr DestB64ToAddr(const std::string& dest)
{
    const Binary& decoded = DecodeI2PBase64(dest);
    return DestBinToAddr(decoded);
}

namespace sam {

Session::Session(const fs::path& private_key_file,
                 const CService& control_host,
                 CThreadInterrupt* interrupt)
    : m_private_key_file{private_key_file},
      m_control_host{control_host},
      m_interrupt{interrupt},
      m_control_sock{std::make_unique<Sock>(INVALID_SOCKET)},
      m_transient{false}
{
}

Session::Session(const CService& control_host, CThreadInterrupt* interrupt)
    : m_control_host{control_host},
      m_interrupt{interrupt},
      m_control_sock{std::make_unique<Sock>(INVALID_SOCKET)},
      m_transient{true}
{
}

Session::~Session()
{
    LOCK(m_mutex);
    Disconnect();
}

bool Session::Listen(Connection& conn)
{
    try {
        LOCK(m_mutex);
        CreateIfNotCreatedAlready();
        conn.me = m_my_addr;
        conn.sock = StreamAccept();
        return true;
    } catch (const std::runtime_error& e) {
        Log("Error listening: %s", e.what());
        CheckControlSock();
    }
    return false;
}

bool Session::Accept(Connection& conn)
{
    try {
        while (!*m_interrupt) {
            Sock::Event occurred;
            if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
                throw std::runtime_error("wait on socket failed");
            }

            if (occurred == 0) {
                // Timeout, no incoming connections or errors within MAX_WAIT_FOR_IO.
                continue;
            }

            const std::string& peer_dest =
                conn.sock->RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);

            conn.peer = CService(DestB64ToAddr(peer_dest), I2P_SAM31_PORT);

            return true;
        }
    } catch (const std::runtime_error& e) {
        Log("Error accepting: %s", e.what());
        CheckControlSock();
    }
    return false;
}

bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
{
    // Refuse connecting to arbitrary ports. We don't specify any destination port to the SAM proxy
    // when connecting (SAM 3.1 does not use ports) and it forces/defaults it to I2P_SAM31_PORT.
    if (to.GetPort() != I2P_SAM31_PORT) {
        proxy_error = false;
        return false;
    }

    proxy_error = true;

    std::string session_id;
    std::unique_ptr<Sock> sock;
    conn.peer = to;

    try {
        {
            LOCK(m_mutex);
            CreateIfNotCreatedAlready();
            session_id = m_session_id;
            conn.me = m_my_addr;
            sock = Hello();
        }

        const Reply& lookup_reply =
            SendRequestAndGetReply(*sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringIP()));

        const std::string& dest = lookup_reply.Get("VALUE");

        const Reply& connect_reply = SendRequestAndGetReply(
            *sock, strprintf("STREAM CONNECT ID=%s DESTINATION=%s SILENT=false", session_id, dest),
            false);

        const std::string& result = connect_reply.Get("RESULT");

        if (result == "OK") {
            conn.sock = std::move(sock);
            return true;
        }

        if (result == "INVALID_ID") {
            LOCK(m_mutex);
            Disconnect();
            throw std::runtime_error("Invalid session id");
        }

        if (result == "CANT_REACH_PEER" || result == "TIMEOUT") {
            proxy_error = false;
        }

        throw std::runtime_error(strprintf("\"%s\"", connect_reply.full));
    } catch (const std::runtime_error& e) {
        Log("Error connecting to %s: %s", to.ToString(), e.what());
        CheckControlSock();
        return false;
    }
}

// Private methods

std::string Session::Reply::Get(const std::string& key) const
{
    const auto& pos = keys.find(key);
    if (pos == keys.end() || !pos->second.has_value()) {
        throw std::runtime_error(
            strprintf("Missing %s= in the reply to \"%s\": \"%s\"", key, request, full));
    }
    return pos->second.value();
}

template <typename... Args>
void Session::Log(const std::string& fmt, const Args&... args) const
{
    LogPrint(BCLog::I2P, "%s\n", tfm::format(fmt, args...));
}

Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
                                               const std::string& request,
                                               bool check_result_ok) const
{
    sock.SendComplete(request + "\n", MAX_WAIT_FOR_IO, *m_interrupt);

    Reply reply;

    // Don't log the full "SESSION CREATE ..." because it contains our private key.
    reply.request = request.substr(0, 14) == "SESSION CREATE" ? "SESSION CREATE ..." : request;

    // It could take a few minutes for the I2P router to reply as it is querying the I2P network
    // (when doing name lookup, for example). Notice: `RecvUntilTerminator()` is checking
    // `m_interrupt` more often, so we would not be stuck here for long if `m_interrupt` is
    // signaled.
    static constexpr auto recv_timeout = 3min;

    reply.full = sock.RecvUntilTerminator('\n', recv_timeout, *m_interrupt, MAX_MSG_SIZE);

    for (const auto& kv : spanparsing::Split(reply.full, ' ')) {
        const auto& pos = std::find(kv.begin(), kv.end(), '=');
        if (pos != kv.end()) {
            reply.keys.emplace(std::string{kv.begin(), pos}, std::string{pos + 1, kv.end()});
        } else {
            reply.keys.emplace(std::string{kv.begin(), kv.end()}, std::nullopt);
        }
    }

    if (check_result_ok && reply.Get("RESULT") != "OK") {
        throw std::runtime_error(
            strprintf("Unexpected reply to \"%s\": \"%s\"", request, reply.full));
    }

    return reply;
}

std::unique_ptr<Sock> Session::Hello() const
{
    auto sock = CreateSock(m_control_host);

    if (!sock) {
        throw std::runtime_error("Cannot create socket");
    }

    if (!ConnectSocketDirectly(m_control_host, *sock, nConnectTimeout, true)) {
        throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
    }

    SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");

    return sock;
}

void Session::CheckControlSock()
{
    LOCK(m_mutex);

    std::string errmsg;
    if (!m_control_sock->IsConnected(errmsg)) {
        Log("Control socket error: %s", errmsg);
        Disconnect();
    }
}

void Session::DestGenerate(const Sock& sock)
{
    // https://geti2p.net/spec/common-structures#key-certificates
    // "7" or "EdDSA_SHA512_Ed25519" - "Recent Router Identities and Destinations".
    // Use "7" because i2pd <2.24.0 does not recognize the textual form.
    const Reply& reply = SendRequestAndGetReply(sock, "DEST GENERATE SIGNATURE_TYPE=7", false);

    m_private_key = DecodeI2PBase64(reply.Get("PRIV"));
}

void Session::GenerateAndSavePrivateKey(const Sock& sock)
{
    DestGenerate(sock);

    // umask is set to 077 in init.cpp, which is ok (unless -sysperms is given)
    if (!WriteBinaryFile(m_private_key_file,
                         std::string(m_private_key.begin(), m_private_key.end()))) {
        throw std::runtime_error(
            strprintf("Cannot save I2P private key to %s", fs::quoted(fs::PathToString(m_private_key_file))));
    }
}

Binary Session::MyDestination() const
{
    // From https://geti2p.net/spec/common-structures#destination:
    // "They are 387 bytes plus the certificate length specified at bytes 385-386, which may be
    // non-zero"
    static constexpr size_t DEST_LEN_BASE = 387;
    static constexpr size_t CERT_LEN_POS = 385;

    uint16_t cert_len;
    memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS), sizeof(cert_len));
    cert_len = be16toh(cert_len);

    const size_t dest_len = DEST_LEN_BASE + cert_len;

    return Binary{m_private_key.begin(), m_private_key.begin() + dest_len};
}

void Session::CreateIfNotCreatedAlready()
{
    std::string errmsg;
    if (m_control_sock->IsConnected(errmsg)) {
        return;
    }

    const auto session_type = m_transient ? "transient" : "persistent";
    const auto session_id = GetRandHash().GetHex().substr(0, 10); // full is overkill, too verbose in the logs

    Log("Creating %s SAM session %s with %s", session_type, session_id, m_control_host.ToString());

    auto sock = Hello();

    if (m_transient) {
        // The destination (private key) is generated upon session creation and returned
        // in the reply in DESTINATION=.
        const Reply& reply = SendRequestAndGetReply(
            *sock,
            strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT", session_id));

        m_private_key = DecodeI2PBase64(reply.Get("DESTINATION"));
    } else {
        // Read our persistent destination (private key) from disk or generate
        // one and save it to disk. Then use it when creating the session.
        const auto& [read_ok, data] = ReadBinaryFile(m_private_key_file);
        if (read_ok) {
            m_private_key.assign(data.begin(), data.end());
        } else {
            GenerateAndSavePrivateKey(*sock);
        }

        const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));

        SendRequestAndGetReply(*sock,
                               strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
                                         session_id,
                                         private_key_b64));
    }

    m_my_addr = CService(DestBinToAddr(MyDestination()), I2P_SAM31_PORT);
    m_session_id = session_id;
    m_control_sock = std::move(sock);

    Log("%s SAM session %s created, my address=%s",
        Capitalize(session_type),
        m_session_id,
        m_my_addr.ToString());
}

std::unique_ptr<Sock> Session::StreamAccept()
{
    auto sock = Hello();

    const Reply& reply = SendRequestAndGetReply(
        *sock, strprintf("STREAM ACCEPT ID=%s SILENT=false", m_session_id), false);

    const std::string& result = reply.Get("RESULT");

    if (result == "OK") {
        return sock;
    }

    if (result == "INVALID_ID") {
        // If our session id is invalid, then force session re-creation on next usage.
        Disconnect();
    }

    throw std::runtime_error(strprintf("\"%s\"", reply.full));
}

void Session::Disconnect()
{
    if (m_control_sock->Get() != INVALID_SOCKET) {
        if (m_session_id.empty()) {
            Log("Destroying incomplete SAM session");
        } else {
            Log("Destroying SAM session %s", m_session_id);
        }
    }
    m_control_sock = std::make_unique<Sock>(INVALID_SOCKET);
    m_session_id.clear();
}
} // namespace sam
} // namespace i2p
<|endoftext|>