<commit_before>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/fastos/fastos.h>
#include <vespa/log/log.h>
#include "cachedflushtarget.h"
#include "flush_all_strategy.h"
#include "flushengine.h"
#include "flushtask.h"
#include "tls_stats_map.h"
#include "tls_stats_factory.h"
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/vespalib/util/jsonwriter.h>
#include <vespa/vespalib/util/exceptions.h>

LOG_SETUP(".proton.flushengine.flushengine");

using vespalib::MonitorGuard;
typedef vespalib::Executor::Task Task;

namespace proton {

namespace {

search::SerialNum
findOldestFlushedSerial(const IFlushTarget::List &lst,
                        const IFlushHandler &handler,
                        const IFlushTarget *self)
{
    search::SerialNum ret(handler.getCurrentSerialNumber());
    for (const IFlushTarget::SP & target : lst) {
        if (self != target.get()) {
            ret = std::min(ret, target->getFlushedSerialNum());
        }
    }
    LOG(debug, "Oldest flushed serial for '%s' will be %" PRIu64 " after flush.", handler.getName().c_str(), ret);
    return ret;
}

}

FlushEngine::FlushInfo::FlushInfo() :
    FlushMeta("", fastos::ClockSystem::now(), 0),
    _target()
{
}

FlushEngine::FlushInfo::FlushInfo(uint32_t taskId,
                                  const IFlushTarget::SP &target,
                                  const vespalib::string & destination) :
    FlushMeta(destination, fastos::ClockSystem::now(), taskId),
    _target(target)
{
}

FlushEngine::FlushEngine(std::shared_ptr<flushengine::ITlsStatsFactory>
                         tlsStatsFactory,
                         IFlushStrategy::SP strategy, uint32_t numThreads,
                         uint32_t idleIntervalMS, bool enableAutoPrune)
    : _closed(false),
      _maxConcurrent(numThreads),
      _idleIntervalMS(idleIntervalMS),
      _enableAutoPrune(enableAutoPrune),
      _taskId(0),
      _threadPool(128 * 1024),
      _strategy(strategy),
      _priorityStrategy(),
      _executor(numThreads, 128 * 1024),
      _monitor(),
      _handlers(),
      _flushing(),
      _strategyLock(),
      _strategyMonitor(),
      _tlsStatsFactory(tlsStatsFactory)
{
    // empty
}

FlushEngine::~FlushEngine()
{
    close();
    _executor.sync();
}

FlushEngine &
FlushEngine::start()
{
    if (_threadPool.NewThread(this) == NULL) {
        throw vespalib::IllegalStateException("Failed to start engine thread.");
    }
    return *this;
}

FlushEngine &
FlushEngine::close()
{
    MonitorGuard strategyGuard(_strategyMonitor);
    {
        MonitorGuard guard(_monitor);
        _closed = true;
        guard.broadcast();
    }
    _threadPool.Close();
    return *this;
}

void
FlushEngine::triggerFlush()
{
    setStrategy(std::make_shared<FlushAllStrategy>());
}

void
FlushEngine::kick(void)
{
    MonitorGuard guard(_monitor);
    LOG(debug, "Kicking flush engine");
    guard.broadcast();
}

bool
FlushEngine::canFlushMore(const MonitorGuard & guard) const
{
    (void) guard;
    return _maxConcurrent > _flushing.size();
}

bool
FlushEngine::wait(size_t minimumWaitTimeIfReady)
{
    MonitorGuard guard(_monitor);
    if ( (minimumWaitTimeIfReady > 0) && canFlushMore(guard)) {
        guard.wait(minimumWaitTimeIfReady);
    }
    while ( ! canFlushMore(guard) ) {
        guard.wait(1000); // broadcast when flush done
    }
    return !_closed;
}

void
FlushEngine::Run(FastOS_ThreadInterface *thread, void *arg)
{
    (void)thread;
    (void)arg;
    bool shouldIdle = false;
    vespalib::string prevFlushName;
    while (wait(shouldIdle ? _idleIntervalMS : 0)) {
        shouldIdle = false;
        prevFlushName = flushNextTarget(prevFlushName);
        if ( ! prevFlushName.empty()) {
            // Sleep at least 10 ms after a successful flush in order to avoid busy loop in case
            // of strategy error or target error.
            FastOS_Thread::Sleep(10);
        } else {
            shouldIdle = true;
        }
        if (_enableAutoPrune) {
            prune();
        }
        LOG(debug, "Making another wait(idle=%s, timeMS=%d) last was '%s'", shouldIdle ? "true" : "false", shouldIdle ? _idleIntervalMS : 0, prevFlushName.c_str());
    }
}

void FlushEngine::prune()
{
    if (_flushing.empty()) {
        MonitorGuard guard(_monitor);
        for (const auto & it : _handlers) {
            IFlushHandler & handler(*it.second);
            IFlushTarget::List lst = handler.getFlushTargets();
            handler.flushDone(findOldestFlushedSerial(lst, handler, NULL));
        }
    }
}

bool FlushEngine::isFlushing(const MonitorGuard & guard, const vespalib::string & name) const
{
    (void) guard;
    for(const auto & it : _flushing) {
        if (name == it.second.getName()) {
            return true;
        }
    }
    return false;
}

FlushContext::List
FlushEngine::getTargetList(bool includeFlushingTargets) const
{
    FlushContext::List ret;
    {
        MonitorGuard guard(_monitor);
        for (const auto & it : _handlers) {
            IFlushHandler & handler(*it.second);
            search::SerialNum serial(handler.getCurrentSerialNumber());
            LOG(spam, "Checking FlushHandler '%s' current serial = %ld",
                handler.getName().c_str(), serial);
            IFlushTarget::List lst = handler.getFlushTargets();
            for (const IFlushTarget::SP & target : lst) {
                LOG(spam, "Checking target '%s' with flushedSerialNum = %ld", target->getName().c_str(), target->getFlushedSerialNum());
                if (!isFlushing(guard, FlushContext::createName(handler, *target)) || includeFlushingTargets) {
                    ret.push_back(FlushContext::SP(new FlushContext(it.second,
                                                                    IFlushTarget::SP(new CachedFlushTarget(target)),
                                                                    findOldestFlushedSerial(lst, handler, target.get()),
                                                                    serial)));
                } else {
                    LOG(debug, "Target '%s' with flushedSerialNum = %ld already has a flush going. Local last serial = %ld.",
                               target->getName().c_str(), target->getFlushedSerialNum(), serial);
                }
            }
        }
    }
    return ret;
}

std::pair<FlushContext::List,bool>
FlushEngine::getSortedTargetList(MonitorGuard &strategyGuard) const
{
    (void) strategyGuard;
    FlushContext::List unsortedTargets = getTargetList(false);
    std::pair<FlushContext::List, bool> ret;
    flushengine::TlsStatsMap tlsStatsMap(_tlsStatsFactory->create());
    if (_priorityStrategy) {
        ret = std::make_pair(_priorityStrategy->getFlushTargets(unsortedTargets, tlsStatsMap), true);
    } else {
        ret = std::make_pair(_strategy->getFlushTargets(unsortedTargets, tlsStatsMap), false);
    }
    return ret;
}

FlushContext::SP
FlushEngine::initNextFlush(const FlushContext::List &lst)
{
    FlushContext::SP ctx;
    for (const FlushContext::SP & it : lst) {
        if (LOG_WOULD_LOG(event)) {
            EventLogger::flushInit(it->getName());
        }
        if (it->initFlush()) {
            ctx = it;
            break;
        }
    }
    if (ctx.get() != NULL) {
        LOG(debug, "Target '%s' initiated flush of transactions %" PRIu64 " through %" PRIu64 ".",
                   ctx->getName().c_str(),
                   ctx->getTarget()->getFlushedSerialNum() + 1,
                   ctx->getHandler()->getCurrentSerialNumber());
    }
    return ctx;
}

void
FlushEngine::flushAll(const FlushContext::List &lst)
{
    LOG(debug, "%ld targets to flush.", lst.size());
    for (const FlushContext::SP & ctx : lst) {
        if (wait(0)) {
            if (ctx->initFlush()) {
                LOG(debug, "Target '%s' initiated flush of transactions %" PRIu64 " through %" PRIu64 ".",
                           ctx->getName().c_str(),
                           ctx->getTarget()->getFlushedSerialNum() + 1,
                           ctx->getHandler()->getCurrentSerialNumber());
                _executor.execute(Task::UP(new FlushTask(initFlush(*ctx), *this, ctx, ctx->getOldestFlushable())));
            } else {
                LOG(debug, "Target '%s' failed to initiate flush of transactions %" PRIu64 " through %" PRIu64 ".",
                           ctx->getName().c_str(),
                           ctx->getTarget()->getFlushedSerialNum() + 1,
                           ctx->getHandler()->getCurrentSerialNumber());
            }
        }

    }
}

vespalib::string
FlushEngine::flushNextTarget(const vespalib::string & name)
{
    MonitorGuard strategyGuard(_strategyMonitor);
    std::pair<FlushContext::List,bool> lst = getSortedTargetList(strategyGuard);
    if (lst.second) {
        // Everything returned from a priority strategy should be flushed
        flushAll(lst.first);
        _executor.sync();
        _priorityStrategy.reset();
        strategyGuard.broadcast();
        return "";
    }
    if (lst.first.empty()) {
        LOG(debug, "No target to flush.");
        return "";
    }
    FlushContext::SP ctx = initNextFlush(lst.first);
    if (ctx.get() == NULL) {
        LOG(debug, "All targets refused to flush.");
        return "";
    }
    if ( name == ctx->getName()) {
        LOG(info, "The same target %s out of %ld has been asked to flush again. "
                  "This might indicate flush logic flaw so I will wait 1s before doing it.",
                  name.c_str(), lst.first.size());
        FastOS_Thread::Sleep(1000);
    }
    _executor.execute(Task::UP(new FlushTask(initFlush(*ctx), *this, ctx, ctx->getOldestFlushable())));
    return ctx->getName();
}

uint32_t
FlushEngine::initFlush(const FlushContext &ctx)
{
    if (LOG_WOULD_LOG(event)) {
        IFlushTarget::MemoryGain mgain(ctx.getTarget()->getApproxMemoryGain());
        EventLogger::flushStart(ctx.getName(),
                                mgain.getBefore(),
                                mgain.getAfter(),
                                mgain.gain(),
                                ctx.getTarget()->getFlushedSerialNum() + 1,
                                ctx.getHandler()->getCurrentSerialNumber());
    }
    return initFlush(ctx.getHandler(), ctx.getTarget());
}

void
FlushEngine::flushDone(const FlushContext &ctx, uint32_t taskId)
{
    fastos::TimeStamp duration;
    {
        MonitorGuard guard(_monitor);
        duration = fastos::TimeStamp(fastos::ClockSystem::now()) - _flushing[taskId].getStart();
    }
    if (LOG_WOULD_LOG(event)) {
        FlushStats stats = ctx.getTarget()->getLastFlushStats();
        EventLogger::flushComplete(ctx.getName(),
                                   duration.ms(),
                                   stats.getPath(),
                                   stats.getPathElementsToLog());
    }
    LOG(debug, "FlushEngine::flushDone(taskId='%d') took '%f' secs", taskId, duration.sec());
    MonitorGuard guard(_monitor);
    _flushing.erase(taskId);
    guard.broadcast();
}

IFlushHandler::SP
FlushEngine::putFlushHandler(const DocTypeName &docTypeName,
                             const IFlushHandler::SP &flushHandler)
{
    MonitorGuard guard(_monitor);
    return _handlers.putHandler(docTypeName, flushHandler);
}

IFlushHandler::SP
FlushEngine::getFlushHandler(const DocTypeName &docTypeName) const
{
    MonitorGuard guard(_monitor);
    return _handlers.getHandler(docTypeName);
}

IFlushHandler::SP
FlushEngine::removeFlushHandler(const DocTypeName &docTypeName)
{
    MonitorGuard guard(_monitor);
    return _handlers.removeHandler(docTypeName);
}

FlushEngine::FlushMetaSet
FlushEngine::getCurrentlyFlushingSet() const
{
    FlushMetaSet s;
    vespalib::LockGuard guard(_monitor);
    for (const auto & it : _flushing) {
        s.insert(it.second);
    }
    return s;
}

uint32_t
FlushEngine::initFlush(const IFlushHandler::SP &handler, const IFlushTarget::SP &target)
{
    uint32_t taskId(0);
    {
        vespalib::LockGuard guard(_monitor);
        taskId = _taskId++;
        vespalib::string name(FlushContext::createName(*handler, *target));
        FlushInfo flush(taskId, target, name);
        _flushing[taskId] = flush;
    }
    LOG(debug, "FlushEngine::initFlush(handler='%s', target='%s') => taskId='%d'",
        handler->getName().c_str(), target->getName().c_str(), taskId);
    return taskId;
}

void
FlushEngine::setStrategy(IFlushStrategy::SP strategy)
{
    MonitorGuard strategyGuard(_strategyMonitor);
    if (_closed) {
        return;
    }
    assert(!_priorityStrategy);
    _priorityStrategy = strategy;
    {
        MonitorGuard guard(_monitor);
        guard.broadcast();
    }
    while (_priorityStrategy) {
        strategyGuard.wait();
    }
}

} // namespace proton
<commit_msg>Take lock at start of FlushEngine::setStrategy to avoid crash issues with concurrent calls.<commit_after>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/fastos/fastos.h>
#include <vespa/log/log.h>
#include "cachedflushtarget.h"
#include "flush_all_strategy.h"
#include "flushengine.h"
#include "flushtask.h"
#include "tls_stats_map.h"
#include "tls_stats_factory.h"
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/vespalib/util/jsonwriter.h>
#include <vespa/vespalib/util/exceptions.h>

LOG_SETUP(".proton.flushengine.flushengine");

using vespalib::MonitorGuard;
typedef vespalib::Executor::Task Task;

namespace proton {

namespace {

search::SerialNum
findOldestFlushedSerial(const IFlushTarget::List &lst,
                        const IFlushHandler &handler,
                        const IFlushTarget *self)
{
    search::SerialNum ret(handler.getCurrentSerialNumber());
    for (const IFlushTarget::SP & target : lst) {
        if (self != target.get()) {
            ret = std::min(ret, target->getFlushedSerialNum());
        }
    }
    LOG(debug, "Oldest flushed serial for '%s' will be %" PRIu64 " after flush.", handler.getName().c_str(), ret);
    return ret;
}

}

FlushEngine::FlushInfo::FlushInfo() :
    FlushMeta("", fastos::ClockSystem::now(), 0),
    _target()
{
}

FlushEngine::FlushInfo::FlushInfo(uint32_t taskId,
                                  const IFlushTarget::SP &target,
                                  const vespalib::string & destination) :
    FlushMeta(destination, fastos::ClockSystem::now(), taskId),
    _target(target)
{
}

FlushEngine::FlushEngine(std::shared_ptr<flushengine::ITlsStatsFactory>
                         tlsStatsFactory,
                         IFlushStrategy::SP strategy, uint32_t numThreads,
                         uint32_t idleIntervalMS, bool enableAutoPrune)
    : _closed(false),
      _maxConcurrent(numThreads),
      _idleIntervalMS(idleIntervalMS),
      _enableAutoPrune(enableAutoPrune),
      _taskId(0),
      _threadPool(128 * 1024),
      _strategy(strategy),
      _priorityStrategy(),
      _executor(numThreads, 128 * 1024),
      _monitor(),
      _handlers(),
      _flushing(),
      _strategyLock(),
      _strategyMonitor(),
      _tlsStatsFactory(tlsStatsFactory)
{
    // empty
}

FlushEngine::~FlushEngine()
{
    close();
    _executor.sync();
}

FlushEngine &
FlushEngine::start()
{
    if (_threadPool.NewThread(this) == NULL) {
        throw vespalib::IllegalStateException("Failed to start engine thread.");
    }
    return *this;
}

FlushEngine &
FlushEngine::close()
{
    MonitorGuard strategyGuard(_strategyMonitor);
    {
        MonitorGuard guard(_monitor);
        _closed = true;
        guard.broadcast();
    }
    _threadPool.Close();
    return *this;
}

void
FlushEngine::triggerFlush()
{
    setStrategy(std::make_shared<FlushAllStrategy>());
}

void
FlushEngine::kick(void)
{
    MonitorGuard guard(_monitor);
    LOG(debug, "Kicking flush engine");
    guard.broadcast();
}

bool
FlushEngine::canFlushMore(const MonitorGuard & guard) const
{
    (void) guard;
    return _maxConcurrent > _flushing.size();
}

bool
FlushEngine::wait(size_t minimumWaitTimeIfReady)
{
    MonitorGuard guard(_monitor);
    if ( (minimumWaitTimeIfReady > 0) && canFlushMore(guard)) {
        guard.wait(minimumWaitTimeIfReady);
    }
    while ( ! canFlushMore(guard) ) {
        guard.wait(1000); // broadcast when flush done
    }
    return !_closed;
}

void
FlushEngine::Run(FastOS_ThreadInterface *thread, void *arg)
{
    (void)thread;
    (void)arg;
    bool shouldIdle = false;
    vespalib::string prevFlushName;
    while (wait(shouldIdle ? _idleIntervalMS : 0)) {
        shouldIdle = false;
        prevFlushName = flushNextTarget(prevFlushName);
        if ( ! prevFlushName.empty()) {
            // Sleep at least 10 ms after a successful flush in order to avoid busy loop in case
            // of strategy error or target error.
            FastOS_Thread::Sleep(10);
        } else {
            shouldIdle = true;
        }
        if (_enableAutoPrune) {
            prune();
        }
        LOG(debug, "Making another wait(idle=%s, timeMS=%d) last was '%s'", shouldIdle ? "true" : "false", shouldIdle ? _idleIntervalMS : 0, prevFlushName.c_str());
    }
}

void FlushEngine::prune()
{
    if (_flushing.empty()) {
        MonitorGuard guard(_monitor);
        for (const auto & it : _handlers) {
            IFlushHandler & handler(*it.second);
            IFlushTarget::List lst = handler.getFlushTargets();
            handler.flushDone(findOldestFlushedSerial(lst, handler, NULL));
        }
    }
}

bool FlushEngine::isFlushing(const MonitorGuard & guard, const vespalib::string & name) const
{
    (void) guard;
    for(const auto & it : _flushing) {
        if (name == it.second.getName()) {
            return true;
        }
    }
    return false;
}

FlushContext::List
FlushEngine::getTargetList(bool includeFlushingTargets) const
{
    FlushContext::List ret;
    {
        MonitorGuard guard(_monitor);
        for (const auto & it : _handlers) {
            IFlushHandler & handler(*it.second);
            search::SerialNum serial(handler.getCurrentSerialNumber());
            LOG(spam, "Checking FlushHandler '%s' current serial = %ld",
                handler.getName().c_str(), serial);
            IFlushTarget::List lst = handler.getFlushTargets();
            for (const IFlushTarget::SP & target : lst) {
                LOG(spam, "Checking target '%s' with flushedSerialNum = %ld", target->getName().c_str(), target->getFlushedSerialNum());
                if (!isFlushing(guard, FlushContext::createName(handler, *target)) || includeFlushingTargets) {
                    ret.push_back(FlushContext::SP(new FlushContext(it.second,
                                                                    IFlushTarget::SP(new CachedFlushTarget(target)),
                                                                    findOldestFlushedSerial(lst, handler, target.get()),
                                                                    serial)));
                } else {
                    LOG(debug, "Target '%s' with flushedSerialNum = %ld already has a flush going. Local last serial = %ld.",
                               target->getName().c_str(), target->getFlushedSerialNum(), serial);
                }
            }
        }
    }
    return ret;
}

std::pair<FlushContext::List,bool>
FlushEngine::getSortedTargetList(MonitorGuard &strategyGuard) const
{
    (void) strategyGuard;
    FlushContext::List unsortedTargets = getTargetList(false);
    std::pair<FlushContext::List, bool> ret;
    flushengine::TlsStatsMap tlsStatsMap(_tlsStatsFactory->create());
    if (_priorityStrategy) {
        ret = std::make_pair(_priorityStrategy->getFlushTargets(unsortedTargets, tlsStatsMap), true);
    } else {
        ret = std::make_pair(_strategy->getFlushTargets(unsortedTargets, tlsStatsMap), false);
    }
    return ret;
}

FlushContext::SP
FlushEngine::initNextFlush(const FlushContext::List &lst)
{
    FlushContext::SP ctx;
    for (const FlushContext::SP & it : lst) {
        if (LOG_WOULD_LOG(event)) {
            EventLogger::flushInit(it->getName());
        }
        if (it->initFlush()) {
            ctx = it;
            break;
        }
    }
    if (ctx.get() != NULL) {
        LOG(debug, "Target '%s' initiated flush of transactions %" PRIu64 " through %" PRIu64 ".",
                   ctx->getName().c_str(),
                   ctx->getTarget()->getFlushedSerialNum() + 1,
                   ctx->getHandler()->getCurrentSerialNumber());
    }
    return ctx;
}

void
FlushEngine::flushAll(const FlushContext::List &lst)
{
    LOG(debug, "%ld targets to flush.", lst.size());
    for (const FlushContext::SP & ctx : lst) {
        if (wait(0)) {
            if (ctx->initFlush()) {
                LOG(debug, "Target '%s' initiated flush of transactions %" PRIu64 " through %" PRIu64 ".",
                           ctx->getName().c_str(),
                           ctx->getTarget()->getFlushedSerialNum() + 1,
                           ctx->getHandler()->getCurrentSerialNumber());
                _executor.execute(Task::UP(new FlushTask(initFlush(*ctx), *this, ctx, ctx->getOldestFlushable())));
            } else {
                LOG(debug, "Target '%s' failed to initiate flush of transactions %" PRIu64 " through %" PRIu64 ".",
                           ctx->getName().c_str(),
                           ctx->getTarget()->getFlushedSerialNum() + 1,
                           ctx->getHandler()->getCurrentSerialNumber());
            }
        }

    }
}

vespalib::string
FlushEngine::flushNextTarget(const vespalib::string & name)
{
    MonitorGuard strategyGuard(_strategyMonitor);
    std::pair<FlushContext::List,bool> lst = getSortedTargetList(strategyGuard);
    if (lst.second) {
        // Everything returned from a priority strategy should be flushed
        flushAll(lst.first);
        _executor.sync();
        _priorityStrategy.reset();
        strategyGuard.broadcast();
        return "";
    }
    if (lst.first.empty()) {
        LOG(debug, "No target to flush.");
        return "";
    }
    FlushContext::SP ctx = initNextFlush(lst.first);
    if (ctx.get() == NULL) {
        LOG(debug, "All targets refused to flush.");
        return "";
    }
    if ( name == ctx->getName()) {
        LOG(info, "The same target %s out of %ld has been asked to flush again. "
                  "This might indicate flush logic flaw so I will wait 1s before doing it.",
                  name.c_str(), lst.first.size());
        FastOS_Thread::Sleep(1000);
    }
    _executor.execute(Task::UP(new FlushTask(initFlush(*ctx), *this, ctx, ctx->getOldestFlushable())));
    return ctx->getName();
}

uint32_t
FlushEngine::initFlush(const FlushContext &ctx)
{
    if (LOG_WOULD_LOG(event)) {
        IFlushTarget::MemoryGain mgain(ctx.getTarget()->getApproxMemoryGain());
        EventLogger::flushStart(ctx.getName(),
                                mgain.getBefore(),
                                mgain.getAfter(),
                                mgain.gain(),
                                ctx.getTarget()->getFlushedSerialNum() + 1,
                                ctx.getHandler()->getCurrentSerialNumber());
    }
    return initFlush(ctx.getHandler(), ctx.getTarget());
}

void
FlushEngine::flushDone(const FlushContext &ctx, uint32_t taskId)
{
    fastos::TimeStamp duration;
    {
        MonitorGuard guard(_monitor);
        duration = fastos::TimeStamp(fastos::ClockSystem::now()) - _flushing[taskId].getStart();
    }
    if (LOG_WOULD_LOG(event)) {
        FlushStats stats = ctx.getTarget()->getLastFlushStats();
        EventLogger::flushComplete(ctx.getName(),
                                   duration.ms(),
                                   stats.getPath(),
                                   stats.getPathElementsToLog());
    }
    LOG(debug, "FlushEngine::flushDone(taskId='%d') took '%f' secs", taskId, duration.sec());
    MonitorGuard guard(_monitor);
    _flushing.erase(taskId);
    guard.broadcast();
}

IFlushHandler::SP
FlushEngine::putFlushHandler(const DocTypeName &docTypeName,
                             const IFlushHandler::SP &flushHandler)
{
    MonitorGuard guard(_monitor);
    return _handlers.putHandler(docTypeName, flushHandler);
}

IFlushHandler::SP
FlushEngine::getFlushHandler(const DocTypeName &docTypeName) const
{
    MonitorGuard guard(_monitor);
    return _handlers.getHandler(docTypeName);
}

IFlushHandler::SP
FlushEngine::removeFlushHandler(const DocTypeName &docTypeName)
{
    MonitorGuard guard(_monitor);
    return _handlers.removeHandler(docTypeName);
}

FlushEngine::FlushMetaSet
FlushEngine::getCurrentlyFlushingSet() const
{
    FlushMetaSet s;
    vespalib::LockGuard guard(_monitor);
    for (const auto & it : _flushing) {
        s.insert(it.second);
    }
    return s;
}

uint32_t
FlushEngine::initFlush(const IFlushHandler::SP &handler, const IFlushTarget::SP &target)
{
    uint32_t taskId(0);
    {
        vespalib::LockGuard guard(_monitor);
        taskId = _taskId++;
        vespalib::string name(FlushContext::createName(*handler, *target));
        FlushInfo flush(taskId, target, name);
        _flushing[taskId] = flush;
    }
    LOG(debug, "FlushEngine::initFlush(handler='%s', target='%s') => taskId='%d'",
        handler->getName().c_str(), target->getName().c_str(), taskId);
    return taskId;
}

void
FlushEngine::setStrategy(IFlushStrategy::SP strategy)
{
    vespalib::LockGuard strategyLock(_strategyLock);
    MonitorGuard strategyGuard(_strategyMonitor);
    if (_closed) {
        return;
    }
    assert(!_priorityStrategy);
    _priorityStrategy = strategy;
    {
        MonitorGuard guard(_monitor);
        guard.broadcast();
    }
    while (_priorityStrategy) {
        strategyGuard.wait();
    }
}

} // namespace proton
<|endoftext|>


<commit_before>// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "same_element_blueprint.h"
#include "same_element_search.h"
#include <vespa/searchlib/fef/termfieldmatchdata.h>
#include <vespa/searchlib/attribute/elementiterator.h>
#include <vespa/vespalib/objects/visit.hpp>
#include <algorithm>
#include <map>

namespace search::queryeval {

SameElementBlueprint::SameElementBlueprint()
    : ComplexLeafBlueprint(FieldSpecBaseList()),
      _estimate(),
      _layout(),
      _terms()
{
}

FieldSpec
SameElementBlueprint::getNextChildField(const vespalib::string &field_name, uint32_t field_id)
{
    return FieldSpec(field_name, field_id, _layout.allocTermField(field_id), false);
}

void
SameElementBlueprint::addTerm(Blueprint::UP term)
{
    const State &childState = term->getState();
    assert(childState.numFields() == 1);
    HitEstimate childEst = childState.estimate();
    if (_terms.empty() ||  (childEst < _estimate)) {
        _estimate = childEst;
        setEstimate(_estimate);
    }
    _terms.push_back(std::move(term));
}

void
SameElementBlueprint::optimize_self()
{
    std::sort(_terms.begin(), _terms.end(),
              [](const auto &a, const auto &b)
              {
                  return (a->getState().estimate() < b->getState().estimate());
              });
}

void
SameElementBlueprint::fetchPostings(bool strict)
{
    for (size_t i = 0; i < _terms.size(); ++i) {
        _terms[i]->fetchPostings(strict && (i == 0));
    }
}

SearchIterator::UP
SameElementBlueprint::createLeafSearch(const search::fef::TermFieldMatchDataArray &tfmda,
                                       bool strict) const
{
    (void) tfmda;
    assert(!tfmda.valid());
    fef::MatchData::UP md = _layout.createMatchData();
    search::fef::TermFieldMatchDataArray childMatch;
    std::vector<SearchIterator::UP> children(_terms.size());
    for (size_t i = 0; i < _terms.size(); ++i) {
        const State &childState = _terms[i]->getState();
        assert(childState.numFields() == 1);
        childMatch.add(childState.field(0).resolve(*md));
        SearchIterator::UP child = _terms[i]->createSearch(*md, (strict && (i == 0)));
        const attribute::ISearchContext *context = child->getAttributeSearchContext();
        if (context == nullptr) {
            children[i] = std::move(child);
        } else {
            children[i] = std::make_unique<attribute::ElementIterator>(std::move(child), *context, *childMatch[i]);
        }
    }
    return std::make_unique<SameElementSearch>(std::move(md), std::move(children), childMatch, strict);
}

void
SameElementBlueprint::visitMembers(vespalib::ObjectVisitor &visitor) const
{
    ComplexLeafBlueprint::visitMembers(visitor);
    visit(visitor, "terms", _terms);
}

}
<commit_msg>allocate separate termfieldmatchdata for wrapped iterators<commit_after>// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "same_element_blueprint.h"
#include "same_element_search.h"
#include <vespa/searchlib/fef/termfieldmatchdata.h>
#include <vespa/searchlib/attribute/elementiterator.h>
#include <vespa/vespalib/objects/visit.hpp>
#include <algorithm>
#include <map>

namespace search::queryeval {

SameElementBlueprint::SameElementBlueprint()
    : ComplexLeafBlueprint(FieldSpecBaseList()),
      _estimate(),
      _layout(),
      _terms()
{
}

FieldSpec
SameElementBlueprint::getNextChildField(const vespalib::string &field_name, uint32_t field_id)
{
    return FieldSpec(field_name, field_id, _layout.allocTermField(field_id), false);
}

void
SameElementBlueprint::addTerm(Blueprint::UP term)
{
    const State &childState = term->getState();
    assert(childState.numFields() == 1);
    HitEstimate childEst = childState.estimate();
    if (_terms.empty() ||  (childEst < _estimate)) {
        _estimate = childEst;
        setEstimate(_estimate);
    }
    _terms.push_back(std::move(term));
}

void
SameElementBlueprint::optimize_self()
{
    std::sort(_terms.begin(), _terms.end(),
              [](const auto &a, const auto &b)
              {
                  return (a->getState().estimate() < b->getState().estimate());
              });
}

void
SameElementBlueprint::fetchPostings(bool strict)
{
    for (size_t i = 0; i < _terms.size(); ++i) {
        _terms[i]->fetchPostings(strict && (i == 0));
    }
}

SearchIterator::UP
SameElementBlueprint::createLeafSearch(const search::fef::TermFieldMatchDataArray &tfmda,
                                       bool strict) const
{
    (void) tfmda;
    assert(!tfmda.valid());

    fef::MatchDataLayout my_layout = _layout;
    std::vector<fef::TermFieldHandle> extra_handles;
    for (size_t i = 0; i < _terms.size(); ++i) {
        const State &childState = _terms[i]->getState();
        assert(childState.numFields() == 1);
        extra_handles.push_back(my_layout.allocTermField(childState.field(0).getFieldId()));
    }
    fef::MatchData::UP md = my_layout.createMatchData();
    search::fef::TermFieldMatchDataArray childMatch;
    std::vector<SearchIterator::UP> children(_terms.size());
    for (size_t i = 0; i < _terms.size(); ++i) {
        const State &childState = _terms[i]->getState();
        SearchIterator::UP child = _terms[i]->createSearch(*md, (strict && (i == 0)));
        const attribute::ISearchContext *context = child->getAttributeSearchContext();
        if (context == nullptr) {
            children[i] = std::move(child);
            childMatch.add(childState.field(0).resolve(*md));
        } else {
            fef::TermFieldMatchData *child_tfmd = md->resolveTermField(extra_handles[i]);
            children[i] = std::make_unique<attribute::ElementIterator>(std::move(child), *context, *child_tfmd);
            childMatch.add(child_tfmd);
        }
    }
    return std::make_unique<SameElementSearch>(std::move(md), std::move(children), childMatch, strict);
}

void
SameElementBlueprint::visitMembers(vespalib::ObjectVisitor &visitor) const
{
    ComplexLeafBlueprint::visitMembers(visitor);
    visit(visitor, "terms", _terms);
}

}
<|endoftext|>


<commit_before>#include "gigasecond.h"
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

// See <http://www.boost.org/doc/libs/1_59_0/doc/html/date_time/posix_time.html>
// for documentation on boost::posix_time

using namespace boost::posix_time;

BOOST_AUTO_TEST_CASE(test_1)
{
    const auto actual = gigasecond::advance(time_from_string("2011-04-25 00:00:00"));

    const ptime expected(time_from_string("2043-01-01 01:46:40"));
    BOOST_REQUIRE_EQUAL(expected, actual);
}

#if defined(EXERCISM_RUN_ALL_TESTS)
BOOST_AUTO_TEST_CASE(test_2)
{
    const auto actual = gigasecond::advance(time_from_string("1977-06-13 00:00:00"));

    const ptime expected(time_from_string("2009-02-19 01:46:40"));
    BOOST_REQUIRE_EQUAL(expected, actual);
}

BOOST_AUTO_TEST_CASE(test_3)
{
    const auto actual = gigasecond::advance(time_from_string("1959-07-19 00:00:00"));

    const ptime expected(time_from_string("1991-03-27 01:46:40"));
    BOOST_REQUIRE_EQUAL(expected, actual);
}

BOOST_AUTO_TEST_CASE(test_4)
{
    const auto actual = gigasecond::advance(time_from_string("2015-01-24 22:00:00"));

    const ptime expected(time_from_string("2046-10-02 23:46:40"));
    BOOST_REQUIRE_EQUAL(expected, actual);
}

BOOST_AUTO_TEST_CASE(test_5)
{
    const auto actual = gigasecond::advance(time_from_string("2015-01-24 23:59:59"));

    const ptime expected(time_from_string("2046-10-03 01:46:39"));
    BOOST_REQUIRE_EQUAL(expected, actual);
}
#endif
<commit_msg>Clarify type of gigasecond return value to avoid template error edgecase. (#146)<commit_after>#include "gigasecond.h"
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

// See <http://www.boost.org/doc/libs/1_59_0/doc/html/date_time/posix_time.html>
// for documentation on boost::posix_time

using namespace boost::posix_time;

BOOST_AUTO_TEST_CASE(test_1)
{
    const ptime actual = gigasecond::advance(time_from_string("2011-04-25 00:00:00"));

    const ptime expected(time_from_string("2043-01-01 01:46:40"));
    BOOST_REQUIRE_EQUAL(expected, actual);
}

#if defined(EXERCISM_RUN_ALL_TESTS)
BOOST_AUTO_TEST_CASE(test_2)
{
    const auto actual = gigasecond::advance(time_from_string("1977-06-13 00:00:00"));

    const ptime expected(time_from_string("2009-02-19 01:46:40"));
    BOOST_REQUIRE_EQUAL(expected, actual);
}

BOOST_AUTO_TEST_CASE(test_3)
{
    const auto actual = gigasecond::advance(time_from_string("1959-07-19 00:00:00"));

    const ptime expected(time_from_string("1991-03-27 01:46:40"));
    BOOST_REQUIRE_EQUAL(expected, actual);
}

BOOST_AUTO_TEST_CASE(test_4)
{
    const auto actual = gigasecond::advance(time_from_string("2015-01-24 22:00:00"));

    const ptime expected(time_from_string("2046-10-02 23:46:40"));
    BOOST_REQUIRE_EQUAL(expected, actual);
}

BOOST_AUTO_TEST_CASE(test_5)
{
    const auto actual = gigasecond::advance(time_from_string("2015-01-24 23:59:59"));

    const ptime expected(time_from_string("2046-10-03 01:46:39"));
    BOOST_REQUIRE_EQUAL(expected, actual);
}
#endif
<|endoftext|>


<commit_before>#include <list>
#include <map>
#include <llvm/IR/Module.h>
#include <llvm/Support/raw_ostream.h>
#include "DebugInfoProcessor.hpp"
#include "RefmodePolicy.hpp"
#include "predicate_groups.hpp"

using namespace llvm;
using std::list;
using std::map;
using std::string;
namespace pred = predicates;

template
void DebugInfoProcessor::printScope<raw_string_ostream>(
    raw_string_ostream &, const DIScopeRef &);

template<typename Stream> void
DebugInfoProcessor::printScope(Stream &stream, const DIScopeRef &outerScope)
{
    DIScopeRef iScope = outerScope;
    list<string> nsComponents;

    // Find all enclosing namespaces
    while (iScope) {
        const Metadata &scope = *iScope;

        if (const MDString *mds = dyn_cast<MDString>(&scope)) {
            nsComponents.push_front(mds->getString());
            iScope = nullptr;
        }
        else {
            const DIScope *dis = dyn_cast<DIScope>(&scope);
            nsComponents.push_front(dis->getName());
            iScope = dis->getScope();
        }
    }

    // Append namespaces in reverse visiting order
    for (list<string>::iterator it = nsComponents.begin(),
             end = nsComponents.end(); it != end; ++it)
        stream << demangle(*it) << "::";
}

void
DebugInfoProcessor::postProcess(const Module &m)
{
    typedef DebugInfoFinder::type_iterator di_type_iterator;

    // Get iterator over all module types
    llvm::iterator_range<di_type_iterator> allTypes = debugInfoFinder.types();

    // Construct a mapping from Type ID to Type name
    CollectTypeIDs();

    // iterate over types
    for (di_type_iterator iType = allTypes.begin(), E = allTypes.end();
         iType != E; ++iType)
    {
        const DIType &dbgType = **iType;

        // dbgType.dump();

        switch (dbgType.getTag()) {
          case dwarf::Tag::DW_TAG_typedef: {
              postProcessTypedef(cast<DIDerivedType>(dbgType), dbgType.getName().str());
              break;
          }
          case dwarf::Tag::DW_TAG_inheritance: {
              const DIDerivedType &did = cast<DIDerivedType>(dbgType);
              const Metadata *baseType = did.getRawBaseType();
              const DIScopeRef scope = did.getScope();

              if (const MDString *mds = dyn_cast<MDString>(baseType)) {
                  if (const MDString *mdscope = dyn_cast<MDString>(scope)) {
                      string subtypeID = mdscope->getString();
                      string supertypeID = mds->getString();

                      if (!typeNameByID.count(subtypeID))
                          continue;

                      if (!typeNameByID.count(supertypeID))
                          continue;

                      refmode_t refmode = typeNameByID[subtypeID];
                      refmode_t baseRefmode = typeNameByID[supertypeID];
                      uint64_t bitOffset = did.getOffsetInBits();

                      // Inheritance relation accounts for a subobject
                      // field at some given offset

                      size_t pos = baseRefmode.find_first_of(".");
                      refmode_t basename = baseRefmode.substr(pos + 1);
                      refmode_t subobjField = "_subobj$" + basename;

                      // UPDATE - In fact, it can account for more
                      // than one fields at the same offset, since
                      // there all super-classes of zero size would be
                      // mapped to offset 0.

                      writeFact(pred::struct_type::field_name,
                                refmode, bitOffset, subobjField);
                  }
              }
              break;
          }
          case dwarf::Tag::DW_TAG_structure_type: {
              postProcessType(cast<DICompositeType>(dbgType));
              break;
          }
          case dwarf::Tag::DW_TAG_class_type: {
              postProcessType(cast<DICompositeType>(dbgType));
              break;
          }
          default: {
              break;
          }
        }
    }
}

refmode_t DebugInfoProcessor::refmodeOf(const DICompositeType &type, const string &altName)
{
    // Construct refmode
    refmode_t refmode;
    raw_string_ostream rso(refmode);

    // Append prefix based on dwarf tag
    switch (type.getTag()) {
      case dwarf::Tag::DW_TAG_class_type: {
          rso << "%class.";
          break;
      }
      case dwarf::Tag::DW_TAG_structure_type: {
          rso << "%struct.";
          break;
      }
      default: {
          return "";
      }
    }

    // Append scope to name
    printScope(rso, type.getScope());

    // Append class name to refmode
    string typeName = type.getName();
    typeName = (typeName.empty() ? altName : typeName);

    if (typeName.empty())
        return "";

    rso << typeName;
    rso.flush();

    return refmode;
}

void
DebugInfoProcessor::postProcessTypedef(const DIDerivedType &dbgType, const string &name)
{
    const Metadata *baseType = dbgType.getRawBaseType();

    if (!baseType) return;

    if (const DICompositeType *compType = dyn_cast<DICompositeType>(baseType)) {
        postProcessType(*compType, name);
    }
    else if (const DIDerivedType *derType = dyn_cast<DIDerivedType>(baseType)) {
        postProcessTypedef(*derType, name);
    }
}

void
DebugInfoProcessor::postProcessType(const DICompositeType &type, const string &altName)
{
    // Construct refmode
    refmode_t refmode = refmodeOf(type, altName);

    if (refmode.empty()) return;

    // Record fields of composite type
    const DINodeArray &elements = type.getElements();

    for (unsigned i = 0; i < elements.size(); i++) {
        if (DIType *field = dyn_cast<DIType>(elements[i]))
        {
            string fieldName = field->getName();
            uint64_t bitOffset = field->getOffsetInBits();

            if (fieldName.empty())
                continue;

            if (field->isStaticMember()) { // TODO
                continue;
            }

            PredicateFactWriter::writeFact(
                pred::struct_type::field_name, refmode, bitOffset, fieldName);
        }
    }
}

void DebugInfoProcessor::CollectTypeIDs()
{
    // collect type ids
    for (const DIType *type : debugInfoFinder.types())
        if (const DICompositeType *tp = dyn_cast<DICompositeType>(type)) {
            refmode_t refmode = refmodeOf(*tp);

            if (!refmode.empty())
                typeNameByID[tp->getIdentifier()] = refmode;
        }
}
<commit_msg>Minor change<commit_after>#include <list>
#include <map>
#include <llvm/IR/Module.h>
#include <llvm/Support/raw_ostream.h>
#include "DebugInfoProcessor.hpp"
#include "RefmodePolicy.hpp"
#include "predicate_groups.hpp"

using namespace llvm;
using std::list;
using std::map;
using std::string;
namespace pred = predicates;

template
void DebugInfoProcessor::printScope<raw_string_ostream>(
    raw_string_ostream &, const DIScopeRef &);

template<typename Stream> void
DebugInfoProcessor::printScope(Stream &stream, const DIScopeRef &outerScope)
{
    DIScopeRef iScope = outerScope;
    list<string> nsComponents;

    // Find all enclosing namespaces
    while (iScope) {
        const Metadata &scope = *iScope;

        if (const MDString *mds = dyn_cast<MDString>(&scope)) {
            nsComponents.push_front(mds->getString());
            iScope = nullptr;
        }
        else {
            const DIScope *dis = dyn_cast<DIScope>(&scope);
            nsComponents.push_front(dis->getName());
            iScope = dis->getScope();
        }
    }

    // Append namespaces in reverse visiting order
    for (list<string>::iterator it = nsComponents.begin(),
             end = nsComponents.end(); it != end; ++it)
        stream << demangle(*it) << "::";
}

void
DebugInfoProcessor::postProcess(const Module &m)
{
    typedef DebugInfoFinder::type_iterator di_type_iterator;

    // Get iterator over all module types
    llvm::iterator_range<di_type_iterator> allTypes = debugInfoFinder.types();

    // Construct a mapping from Type ID to Type name
    CollectTypeIDs();

    // iterate over types
    for (di_type_iterator iType = allTypes.begin(), E = allTypes.end();
         iType != E; ++iType)
    {
        const DIType &dbgType = **iType;

        // dbgType.dump();

        switch (dbgType.getTag()) {
          case dwarf::Tag::DW_TAG_typedef: {
              postProcessTypedef(cast<DIDerivedType>(dbgType), dbgType.getName().str());
              break;
          }
          case dwarf::Tag::DW_TAG_inheritance: {
              const DIDerivedType &did = cast<DIDerivedType>(dbgType);
              const Metadata *baseType = did.getRawBaseType();
              const DIScopeRef scope = did.getScope();

              if (const MDString *mds = dyn_cast<MDString>(baseType)) {
                  if (const MDString *mdscope = dyn_cast<MDString>(scope)) {
                      string subtypeID = mdscope->getString();
                      string supertypeID = mds->getString();

                      if (!typeNameByID.count(subtypeID))
                          continue;

                      if (!typeNameByID.count(supertypeID))
                          continue;

                      refmode_t refmode = typeNameByID[subtypeID];
                      refmode_t baseRefmode = typeNameByID[supertypeID];
                      uint64_t bitOffset = did.getOffsetInBits();

                      // Inheritance relation accounts for a subobject
                      // field at some given offset

                      size_t pos = baseRefmode.find_first_of(".");
                      refmode_t basename = baseRefmode.substr(pos + 1);
                      refmode_t subobjField = "_subobj$" + basename;

                      // UPDATE - In fact, it can account for more
                      // than one fields at the same offset, since all
                      // super-classes of zero size would be mapped to
                      // offset 0.

                      writeFact(pred::struct_type::field_name,
                                refmode, bitOffset, subobjField);
                  }
              }
              break;
          }
          case dwarf::Tag::DW_TAG_structure_type: {
              postProcessType(cast<DICompositeType>(dbgType));
              break;
          }
          case dwarf::Tag::DW_TAG_class_type: {
              postProcessType(cast<DICompositeType>(dbgType));
              break;
          }
          default: {
              break;
          }
        }
    }
}

refmode_t DebugInfoProcessor::refmodeOf(const DICompositeType &type, const string &altName)
{
    // Construct refmode
    refmode_t refmode;
    raw_string_ostream rso(refmode);

    // Append prefix based on dwarf tag
    switch (type.getTag()) {
      case dwarf::Tag::DW_TAG_class_type: {
          rso << "%class.";
          break;
      }
      case dwarf::Tag::DW_TAG_structure_type: {
          rso << "%struct.";
          break;
      }
      default: {
          return "";
      }
    }

    // Append scope to name
    printScope(rso, type.getScope());

    // Append class name to refmode
    string typeName = type.getName();
    typeName = (typeName.empty() ? altName : typeName);

    if (typeName.empty())
        return "";

    rso << typeName;
    rso.flush();

    return refmode;
}

void
DebugInfoProcessor::postProcessTypedef(const DIDerivedType &dbgType, const string &name)
{
    const Metadata *baseType = dbgType.getRawBaseType();

    if (!baseType) return;

    if (const DICompositeType *compType = dyn_cast<DICompositeType>(baseType)) {
        postProcessType(*compType, name);
    }
    else if (const DIDerivedType *derType = dyn_cast<DIDerivedType>(baseType)) {
        postProcessTypedef(*derType, name);
    }
}

void
DebugInfoProcessor::postProcessType(const DICompositeType &type, const string &altName)
{
    // Construct refmode
    refmode_t refmode = refmodeOf(type, altName);

    if (refmode.empty()) return;

    // Record fields of composite type
    const DINodeArray &elements = type.getElements();

    for (unsigned i = 0; i < elements.size(); i++) {
        if (DIType *field = dyn_cast<DIType>(elements[i]))
        {
            string fieldName = field->getName();
            uint64_t bitOffset = field->getOffsetInBits();

            if (fieldName.empty())
                continue;

            if (field->isStaticMember()) { // TODO
                continue;
            }

            PredicateFactWriter::writeFact(
                pred::struct_type::field_name, refmode, bitOffset, fieldName);
        }
    }
}

void DebugInfoProcessor::CollectTypeIDs()
{
    // collect type ids
    for (const DIType *type : debugInfoFinder.types())
        if (const DICompositeType *tp = dyn_cast<DICompositeType>(type)) {
            refmode_t refmode = refmodeOf(*tp);

            if (!refmode.empty())
                typeNameByID[tp->getIdentifier()] = refmode;
        }
}
<|endoftext|>


<commit_before>#include <libgen.h>
#include <stdlib.h>
#include <unistd.h>

#include <algorithm>
#include <iostream>

#include "configurator.hpp"
#include "foreach.hpp"
#include "params.hpp"
#include "string_utils.hpp"

using namespace nexus::internal;

const char* Configurator::DEFAULT_CONFIG_DIR = "conf";
const char* Configurator::CONFIG_FILE_NAME = "mesos.conf";
const char* Configurator::ENV_VAR_PREFIX = "MESOS_";

// Define a function for accessing the list of environment variables
// in a platform-independent way.
// On Mac OS X, the environ symbol isn't visible to shared libraries,
// so we must use the _NSGetEnviron() function (see man environ on OS X).
// On other platforms, it's fine to access environ from shared libraries.
#ifdef __APPLE__
#include "crt_externs.h"
namespace {
char** getEnviron() { return *_NSGetEnviron(); }
}
#else
extern char** environ;
namespace {
char** getEnviron() { return environ; }
}
#endif /* __APPLE__ */

void Configurator::validate()
{
  foreachpair (const string& key, const Option& opt, options) {
    if (params.contains(key) && opt.validator &&
        !opt.validator->isValid(params[key])) {
      string msg = "Invalid value for '" + key + "' option: " + params[key];
      throw ConfigurationException(msg.c_str());
    }
  }
}

Params& Configurator::load(int argc, char** argv, bool inferMesosHomeFromArg0)
{
  loadEnv();
  loadCommandLine(argc, argv, inferMesosHomeFromArg0);
  loadConfigFileIfGiven();
  validate();
  return params;
}

Params& Configurator::load()
{
  loadEnv();
  loadConfigFileIfGiven();
  validate();
  return params;
}

Params& Configurator::load(const map<string, string>& _params)
{
  loadEnv();
  params.loadMap(_params);
  loadConfigFileIfGiven();
  validate();
  return params;
}

void Configurator::loadConfigFileIfGiven(bool overwrite) {
  string confDir = "";
  if (params.contains("conf"))
    confDir = params["conf"];
  else if (params.contains("home")) // find conf dir relative to MESOS_HOME
    confDir = params["home"] + "/" + DEFAULT_CONFIG_DIR;
  if (confDir != "")
    loadConfigFile(confDir + "/" + CONFIG_FILE_NAME, overwrite);
}

void Configurator::loadEnv(bool overwrite)
{
  char** environ = getEnviron();
  int i = 0;
  while (environ[i] != NULL) {
    string line = environ[i];
    if (line.find(ENV_VAR_PREFIX) == 0) {
      string key, val;
      size_t eq = line.find_first_of("=");
      if (eq == string::npos)
        continue; // ignore malformed lines (they shouldn't occur in environ!)
      key = line.substr(strlen(ENV_VAR_PREFIX), eq - strlen(ENV_VAR_PREFIX));
      std::transform(key.begin(), key.end(), key.begin(), ::tolower);
      val = line.substr(eq + 1);
      if (overwrite || !params.contains(key)) {
        params[key] = val;
      }
    }
    i++;
  }
}

void Configurator::loadCommandLine(int argc,
                                   char** argv,
                                   bool inferMesosHomeFromArg0,
                                   bool overwrite)
{
  // Set home based on argument 0 if asked to do so
  if (inferMesosHomeFromArg0) {
    char buf[4096];
    if (realpath(dirname(argv[0]), buf) == 0) {
      throw ConfigurationException(
          "Could not get directory containing argv[0] -- realpath failed");
    }
    params["home"] = buf;
  }

  // Convert args 1 and above to STL strings
  vector<string> args;
  for (int i=1; i < argc; i++) {
    args.push_back(string(argv[i]));
  }

  for (int i = 0; i < args.size(); i++) {
    string key, val;
    bool set = false;
    if (args[i].find("--", 0) == 0) {
      // handle "--" case

      size_t eq = args[i].find_first_of("=");
      if (eq == string::npos &&
          args[i].find("--no-", 0) == 0) { // handle --no-blah
        key = args[i].substr(5);
        val = "0";
        set = true;
      } else if (eq == string::npos) {     // handle --blah
        key = args[i].substr(2);
        val = "1";
        set = true;
      } else {                             // handle --blah=25
        key = args[i].substr(2, eq-2);
        val = args[i].substr(eq+1);
        set = true;
      }

    } else if (args[i].find_first_of("-", 0) == 0 &&
               args[i].size() > 1) {
      // handle "-" case

      if (args[i].find("-no-",0) == 0 && args[i].size() > 4) { // handle -no-b
        key = getLongName(args[i][4]);
        val = "0";
      } else if (args[i].size() > 1) {  // shared code for -b and -b 25
        key = getLongName(args[i][1]);
        val = "1";
      }
      if (key == "")
        continue;  // couldn't extract long name from short name

      if (options[key].validator->isBool())
        set = true;
      else if (i+1 < args.size()) {  // handle -b 25
        val = args[i+1];
        set = true;
        i++;  // we've consumed next parameter as a "value"-parameter
      }

    }
    if (set && (overwrite || !params.contains(key))) {
      std::transform(key.begin(), key.end(), key.begin(), ::tolower);
      params[key] = val;
    }
  }
}

void Configurator::loadConfigFile(const string& fname, bool overwrite)
{
  ifstream cfg(fname.c_str(), std::ios::in);
  if (!cfg.is_open()) {
    string message = "Couldn't read Mesos config file: " + fname;
    throw ConfigurationException(message.c_str());
  }

  string line, originalLine;

  while (!cfg.eof()) {
    getline(cfg, line);
    originalLine = line;
    // Strip any comment at end of line
    size_t hash = line.find_first_of("#"); // strip comments
    if (hash != string::npos) {
      line = line.substr(0, hash);
    }
    // Check for empty line
    line = StringUtils::trim(line);
    if (line == "") {
      continue;
    }
    // Split line by = and trim to get key and value
    vector<string> tokens;
    StringUtils::split(line, "=", &tokens);
    if (tokens.size() != 2) {
      string message = "Malformed line in config file: '" +
                       StringUtils::trim(originalLine) + "'";
      throw ConfigurationException(message.c_str());
    }
    string key = StringUtils::trim(tokens[0]);
    string value = StringUtils::trim(tokens[1]);
    if (overwrite || !params.contains(key)) {
      params[key] = value;
    }
  }
  cfg.close();
}

string Configurator::getUsage() const
{
  const int PAD = 5;
  string usage;

  map<string,string> col1; // key -> col 1 string
  int maxLen = 0;

  // construct string for the first column and get size of column
  foreachpair (const string& key, const Option& opt, options) {
    string val = "--" + key;
    if (!opt.validator->isBool())
      val += "=VAL";

    if (opt.hasShortName) {
      if (opt.validator->isBool())
        val += string(" (or -") + opt.shortName + " or -no-" +
          opt.shortName + ")";
      else
        val += string(" (or -") + opt.shortName + " VAL)";
    } else if (opt.validator->isBool())
      val += " (--no-" + key + ")";

    col1[key] = val;
    maxLen = val.size() > maxLen ? val.size() : maxLen;
  }

  foreachpair (const string& key, const Option& opt, options) {
    string helpStr = opt.helpString;
    string line = col1[key];

    if (opt.defaultValue != "") {  // add default value
      // Place a space between help string and (default: VAL) if the
      // help string does not end with a newline itself
      size_t lastNewLine = helpStr.find_last_of("\n\r");
      if (helpStr.size() > 0 && lastNewLine != helpStr.size() - 1) {
        helpStr += " ";
      }
      string defval = opt.defaultValue;
      if (opt.validator->isBool())
        defval = opt.defaultValue == "0" ? "false" : "true";

      helpStr += "(default: " + defval + ")";
    }

    string pad(PAD + maxLen - line.size(), ' ');
    line += pad;
    size_t pos1 = 0, pos2 = 0;
    pos2 = helpStr.find_first_of("\n\r", pos1);
    line += helpStr.substr(pos1, pos2 - pos1) + "\n";
    usage += line;

    while(pos2 != string::npos) {  // handle multi line help strings
      line = "";
      pos1 = pos2 + 1;
      string pad2(PAD + maxLen, ' ');
      line += pad2;
      pos2 = helpStr.find_first_of("\n\r", pos1);
      line += helpStr.substr(pos1, pos2 - pos1) + "\n";
      usage += line;
    }

  }
  return usage;
}

vector<string> Configurator::getOptions() const
{
  vector<string> ret;
  foreachpair (const string& key, _, options) {
    ret.push_back(key);
  }
  return ret;
}

Params& Configurator::getParams()
{
  return params;
}

string Configurator::getLongName(char shortName) const
{
  foreachpair (const string& key, const Option& opt, options) {
    if (opt.hasShortName && opt.shortName == shortName)
      return key;
  }
  return "";
}

void Configurator::clearMesosEnvironmentVars()
{
  char** environ = getEnviron();
  int i = 0;
  vector<string> toRemove;
  while (environ[i] != NULL) {
    string line = environ[i];
    if (line.find(ENV_VAR_PREFIX) == 0) {
      string key;
      size_t eq = line.find_first_of("=");
      if (eq == string::npos)
        continue; // ignore malformed lines (they shouldn't occur in environ!)
      key = line.substr(strlen(ENV_VAR_PREFIX), eq - strlen(ENV_VAR_PREFIX));
      toRemove.push_back(key);
    }
    i++;
  }
  foreach (string& str, toRemove) {
    unsetenv(str.c_str());
  }
}
<commit_msg>pretty printing...<commit_after>#include <libgen.h>
#include <stdlib.h>
#include <unistd.h>

#include <algorithm>
#include <iostream>

#include "configurator.hpp"
#include "foreach.hpp"
#include "params.hpp"
#include "string_utils.hpp"

using namespace nexus::internal;

const char* Configurator::DEFAULT_CONFIG_DIR = "conf";
const char* Configurator::CONFIG_FILE_NAME = "mesos.conf";
const char* Configurator::ENV_VAR_PREFIX = "MESOS_";

// Define a function for accessing the list of environment variables
// in a platform-independent way.
// On Mac OS X, the environ symbol isn't visible to shared libraries,
// so we must use the _NSGetEnviron() function (see man environ on OS X).
// On other platforms, it's fine to access environ from shared libraries.
#ifdef __APPLE__
#include "crt_externs.h"
namespace {
char** getEnviron() { return *_NSGetEnviron(); }
}
#else
extern char** environ;
namespace {
char** getEnviron() { return environ; }
}
#endif /* __APPLE__ */

void Configurator::validate()
{
  foreachpair (const string& key, const Option& opt, options) {
    if (params.contains(key) && opt.validator &&
        !opt.validator->isValid(params[key])) {
      string msg = "Invalid value for '" + key + "' option: " + params[key];
      throw ConfigurationException(msg.c_str());
    }
  }
}

Params& Configurator::load(int argc, char** argv, bool inferMesosHomeFromArg0)
{
  loadEnv();
  loadCommandLine(argc, argv, inferMesosHomeFromArg0);
  loadConfigFileIfGiven();
  validate();
  return params;
}

Params& Configurator::load()
{
  loadEnv();
  loadConfigFileIfGiven();
  validate();
  return params;
}

Params& Configurator::load(const map<string, string>& _params)
{
  loadEnv();
  params.loadMap(_params);
  loadConfigFileIfGiven();
  validate();
  return params;
}

void Configurator::loadConfigFileIfGiven(bool overwrite) {
  string confDir = "";
  if (params.contains("conf"))
    confDir = params["conf"];
  else if (params.contains("home")) // find conf dir relative to MESOS_HOME
    confDir = params["home"] + "/" + DEFAULT_CONFIG_DIR;
  if (confDir != "")
    loadConfigFile(confDir + "/" + CONFIG_FILE_NAME, overwrite);
}

void Configurator::loadEnv(bool overwrite)
{
  char** environ = getEnviron();
  int i = 0;
  while (environ[i] != NULL) {
    string line = environ[i];
    if (line.find(ENV_VAR_PREFIX) == 0) {
      string key, val;
      size_t eq = line.find_first_of("=");
      if (eq == string::npos)
        continue; // ignore malformed lines (they shouldn't occur in environ!)
      key = line.substr(strlen(ENV_VAR_PREFIX), eq - strlen(ENV_VAR_PREFIX));
      std::transform(key.begin(), key.end(), key.begin(), ::tolower);
      val = line.substr(eq + 1);
      if (overwrite || !params.contains(key)) {
        params[key] = val;
      }
    }
    i++;
  }
}

void Configurator::loadCommandLine(int argc,
                                   char** argv,
                                   bool inferMesosHomeFromArg0,
                                   bool overwrite)
{
  // Set home based on argument 0 if asked to do so
  if (inferMesosHomeFromArg0) {
    char buf[4096];
    if (realpath(dirname(argv[0]), buf) == 0) {
      throw ConfigurationException(
          "Could not get directory containing argv[0] -- realpath failed");
    }
    params["home"] = buf;
  }

  // Convert args 1 and above to STL strings
  vector<string> args;
  for (int i=1; i < argc; i++) {
    args.push_back(string(argv[i]));
  }

  for (int i = 0; i < args.size(); i++) {
    string key, val;
    bool set = false;
    if (args[i].find("--", 0) == 0) {
      // handle "--" case

      size_t eq = args[i].find_first_of("=");
      if (eq == string::npos &&
          args[i].find("--no-", 0) == 0) { // handle --no-blah
        key = args[i].substr(5);
        val = "0";
        set = true;
      } else if (eq == string::npos) {     // handle --blah
        key = args[i].substr(2);
        val = "1";
        set = true;
      } else {                             // handle --blah=25
        key = args[i].substr(2, eq-2);
        val = args[i].substr(eq+1);
        set = true;
      }

    } else if (args[i].find_first_of("-", 0) == 0 &&
               args[i].size() > 1) {
      // handle "-" case

      if (args[i].find("-no-",0) == 0 && args[i].size() > 4) { // handle -no-b
        key = getLongName(args[i][4]);
        val = "0";
      } else if (args[i].size() > 1) {  // shared code for -b and -b 25
        key = getLongName(args[i][1]);
        val = "1";
      }
      if (key == "")
        continue;  // couldn't extract long name from short name

      if (options[key].validator->isBool())
        set = true;
      else if (i+1 < args.size()) {  // handle -b 25
        val = args[i+1];
        set = true;
        i++;  // we've consumed next parameter as a "value"-parameter
      }

    }
    if (set && (overwrite || !params.contains(key))) {
      std::transform(key.begin(), key.end(), key.begin(), ::tolower);
      params[key] = val;
    }
  }
}

void Configurator::loadConfigFile(const string& fname, bool overwrite)
{
  ifstream cfg(fname.c_str(), std::ios::in);
  if (!cfg.is_open()) {
    string message = "Couldn't read Mesos config file: " + fname;
    throw ConfigurationException(message.c_str());
  }

  string line, originalLine;

  while (!cfg.eof()) {
    getline(cfg, line);
    originalLine = line;
    // Strip any comment at end of line
    size_t hash = line.find_first_of("#"); // strip comments
    if (hash != string::npos) {
      line = line.substr(0, hash);
    }
    // Check for empty line
    line = StringUtils::trim(line);
    if (line == "") {
      continue;
    }
    // Split line by = and trim to get key and value
    vector<string> tokens;
    StringUtils::split(line, "=", &tokens);
    if (tokens.size() != 2) {
      string message = "Malformed line in config file: '" +
                       StringUtils::trim(originalLine) + "'";
      throw ConfigurationException(message.c_str());
    }
    string key = StringUtils::trim(tokens[0]);
    string value = StringUtils::trim(tokens[1]);
    if (overwrite || !params.contains(key)) {
      params[key] = value;
    }
  }
  cfg.close();
}

string Configurator::getUsage() const
{
  const int PAD = 5;
  string usage;

  map<string,string> col1; // key -> col 1 string
  int maxLen = 0;

  // construct string for the first column and get size of column
  foreachpair (const string& key, const Option& opt, options) {
    string val = "  --" + key;
    if (!opt.validator->isBool())
      val += "=VAL";

    if (opt.hasShortName) {
      if (opt.validator->isBool())
        val += string(" (or -") + opt.shortName + " or -no-" +
          opt.shortName + ")";
      else
        val += string(" (or -") + opt.shortName + " VAL)";
    } else if (opt.validator->isBool())
      val += " (--no-" + key + ")";

    col1[key] = val;
    maxLen = val.size() > maxLen ? val.size() : maxLen;
  }

  foreachpair (const string& key, const Option& opt, options) {
    string helpStr = opt.helpString;
    string line = col1[key];

    if (opt.defaultValue != "") {  // add default value
      // Place a space between help string and (default: VAL) if the
      // help string does not end with a newline itself
      size_t lastNewLine = helpStr.find_last_of("\n\r");
      if (helpStr.size() > 0 && lastNewLine != helpStr.size() - 1) {
        helpStr += " ";
      }
      string defval = opt.defaultValue;
      if (opt.validator->isBool())
        defval = opt.defaultValue == "0" ? "false" : "true";

      helpStr += "(default: " + defval + ")";
    }

    string pad(PAD + maxLen - line.size(), ' ');
    line += pad;
    size_t pos1 = 0, pos2 = 0;
    pos2 = helpStr.find_first_of("\n\r", pos1);
    line += helpStr.substr(pos1, pos2 - pos1) + "\n";
    usage += line;

    while(pos2 != string::npos) {  // handle multi line help strings
      line = "";
      pos1 = pos2 + 1;
      string pad2(PAD + maxLen, ' ');
      line += pad2;
      pos2 = helpStr.find_first_of("\n\r", pos1);
      line += helpStr.substr(pos1, pos2 - pos1) + "\n";
      usage += line;
    }

  }
  return usage;
}

vector<string> Configurator::getOptions() const
{
  vector<string> ret;
  foreachpair (const string& key, _, options) {
    ret.push_back(key);
  }
  return ret;
}

Params& Configurator::getParams()
{
  return params;
}

string Configurator::getLongName(char shortName) const
{
  foreachpair (const string& key, const Option& opt, options) {
    if (opt.hasShortName && opt.shortName == shortName)
      return key;
  }
  return "";
}

void Configurator::clearMesosEnvironmentVars()
{
  char** environ = getEnviron();
  int i = 0;
  vector<string> toRemove;
  while (environ[i] != NULL) {
    string line = environ[i];
    if (line.find(ENV_VAR_PREFIX) == 0) {
      string key;
      size_t eq = line.find_first_of("=");
      if (eq == string::npos)
        continue; // ignore malformed lines (they shouldn't occur in environ!)
      key = line.substr(strlen(ENV_VAR_PREFIX), eq - strlen(ENV_VAR_PREFIX));
      toRemove.push_back(key);
    }
    i++;
  }
  foreach (string& str, toRemove) {
    unsetenv(str.c_str());
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Milian Wolff <mail@milianw.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "model.h"

#include <QDebug>

#include <sstream>

using namespace std;

namespace {
QString generateSummary(const AccumulatedTraceData& data)
{
    stringstream stream;
    const double totalTimeS = 0.001 * data.totalTime;
    stream << "<qt>"
           << "total runtime: " << fixed << totalTimeS << "s.
"
           << "bytes allocated in total (ignoring deallocations): " << formatBytes(data.totalAllocated)
             << " (" << formatBytes(data.totalAllocated / totalTimeS) << "/s)
"
           << "calls to allocation functions: " << data.totalAllocations
             << " (" << size_t(data.totalAllocations / totalTimeS) << "/s)
"
           << "peak heap memory consumption: " << formatBytes(data.peak) << "
"
           << "total memory leaked: " << formatBytes(data.leaked) << "
";
    stream << "</qt>";
    return QString::fromStdString(stream.str());
}

int parentRow(const QModelIndex& child)
{
    return child.isValid() ? static_cast<int>(child.internalId()) : -1;
}

}

Model::Model(QObject* parent)
{

}

Model::~Model()
{
}

QVariant Model::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 || section >= NUM_COLUMNS) {
        return QVariant();
    }
    switch (static_cast<Columns>(section)) {
        case FileColumn:
            return tr("File");
        case FunctionColumn:
            return tr("Function");
        case ModuleColumn:
            return tr("Module");
        case AllocationsColumn:
            return tr("Allocations");
        case PeakColumn:
            return tr("Peak");
        case LeakedColumn:
            return tr("Leaked");
        case AllocatedColumn:
            return tr("Allocated");
        case NUM_COLUMNS:
            break;
    }
    return QVariant();
}

QVariant Model::data(const QModelIndex& index, int role) const
{
    if (index.row() < 0 || index.row() > m_data.mergedAllocations.size()
        || index.column() < 0 || index.column() > NUM_COLUMNS)
    {
        return QVariant();
    }
    const auto parent = index.parent();
    if (parent.isValid()) {
        // child level
        if (parent.parent().isValid()) {
            return QVariant();
        }
        const auto& allocation = m_data.mergedAllocations[parent.row()];
        const auto& trace = allocation.traces[index.row()];

        if (role == Qt::DisplayRole) {
            return allocationData(trace, m_data.findTrace(trace.traceIndex).ipIndex, static_cast<Columns>(index.column()));
        }
        return QVariant();
    }

    // top-level
    const auto& allocation = m_data.mergedAllocations[index.row()];
    if (role == Qt::DisplayRole) {
        return allocationData(allocation, allocation.ipIndex, static_cast<Columns>(index.column()));
    }
    return QVariant();
}

QModelIndex Model::index(int row, int column, const QModelIndex& parent) const
{
    if (row < 0 || column  < 0 || column >= NUM_COLUMNS || row >= rowCount(parent)) {
        return QModelIndex();
    }
    return createIndex(row, column, static_cast<quintptr>(parent.row()));
}

QModelIndex Model::parent(const QModelIndex& child) const
{
    const auto parent = parentRow(child);
    if (parent == -1) {
        return QModelIndex();
    } else {
        return createIndex(parent, 0, -1);
    }
}

int Model::rowCount(const QModelIndex& parent) const
{
    if (parent.isValid()) {
        if (parent.column() != 0 || parent.row() < 0 || parent.row() >= m_data.mergedAllocations.size()
            || parentRow(parent) != -1)
        {
            return 0;
        } else {
            return m_data.mergedAllocations[parent.row()].traces.size();
        }
    }
    return m_data.mergedAllocations.size();
}

int Model::columnCount(const QModelIndex& /*parent*/) const
{
    return NUM_COLUMNS;
}

void Model::loadFile(const QString& file)
{
    beginResetModel();
    m_data.read(file.toStdString());
    endResetModel();
    emit dataReady(generateSummary(m_data));
}

QVariant Model::allocationData(const AllocationData& allocation, const IpIndex& ipIndex, Columns column) const
{
    switch (column) {
    case AllocationsColumn:
        return static_cast<quint64>(allocation.allocations);
    case PeakColumn:
        return static_cast<quint64>(allocation.peak);
    case LeakedColumn:
        return static_cast<quint64>(allocation.leaked);
    case AllocatedColumn:
        return static_cast<quint64>(allocation.allocated);
    case FileColumn:
    case ModuleColumn:
    case FunctionColumn: {
        const auto& ip = m_data.findIp(ipIndex);
        if (column == FunctionColumn) {
            if (ip.functionIndex) {
                return QString::fromStdString(m_data.prettyFunction(m_data.stringify(ip.functionIndex)));
            } else {
                return QLatin1String("0x") + QString::number(ip.instructionPointer, 16);
            }
        } else if (column == ModuleColumn) {
            return QString::fromStdString(m_data.stringify(ip.moduleIndex));
        } else if (ip.fileIndex) {
            auto file = QString::fromStdString(m_data.stringify(ip.fileIndex));
            return file + QLatin1Char(':') + QString::number(ip.line);
        }
        break;
    }
    case NUM_COLUMNS:
        break;
    }
    return QVariant();
}<commit_msg>Show backtrace in tooltip.<commit_after>/*
 * Copyright 2015 Milian Wolff <mail@milianw.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "model.h"

#include <QDebug>

#include <sstream>

using namespace std;

namespace {
QString generateSummary(const AccumulatedTraceData& data)
{
    stringstream stream;
    const double totalTimeS = 0.001 * data.totalTime;
    stream << "<qt>"
           << "total runtime: " << fixed << totalTimeS << "s.
"
           << "bytes allocated in total (ignoring deallocations): " << formatBytes(data.totalAllocated)
             << " (" << formatBytes(data.totalAllocated / totalTimeS) << "/s)
"
           << "calls to allocation functions: " << data.totalAllocations
             << " (" << size_t(data.totalAllocations / totalTimeS) << "/s)
"
           << "peak heap memory consumption: " << formatBytes(data.peak) << "
"
           << "total memory leaked: " << formatBytes(data.leaked) << "
";
    stream << "</qt>";
    return QString::fromStdString(stream.str());
}

int parentRow(const QModelIndex& child)
{
    return child.isValid() ? static_cast<int>(child.internalId()) : -1;
}

}

Model::Model(QObject* parent)
{

}

Model::~Model()
{
}

QVariant Model::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 || section >= NUM_COLUMNS) {
        return QVariant();
    }
    switch (static_cast<Columns>(section)) {
        case FileColumn:
            return tr("File");
        case FunctionColumn:
            return tr("Function");
        case ModuleColumn:
            return tr("Module");
        case AllocationsColumn:
            return tr("Allocations");
        case PeakColumn:
            return tr("Peak");
        case LeakedColumn:
            return tr("Leaked");
        case AllocatedColumn:
            return tr("Allocated");
        case NUM_COLUMNS:
            break;
    }
    return QVariant();
}

QVariant Model::data(const QModelIndex& index, int role) const
{
    if (index.row() < 0 || index.row() > m_data.mergedAllocations.size()
        || index.column() < 0 || index.column() > NUM_COLUMNS)
    {
        return QVariant();
    }
    const auto parent = index.parent();
    if (parent.isValid()) {
        // child level
        if (parent.parent().isValid()) {
            return QVariant();
        }
        const auto& allocation = m_data.mergedAllocations[parent.row()];
        const auto& trace = allocation.traces[index.row()];

        if (role == Qt::DisplayRole) {
            return allocationData(trace, m_data.findTrace(trace.traceIndex).ipIndex, static_cast<Columns>(index.column()));
        } else if (role == Qt::ToolTipRole) {
            stringstream stream;
            m_data.printBacktrace(trace.traceIndex, stream);
            return QString::fromStdString(stream.str());
        }
        return QVariant();
    }

    // top-level
    const auto& allocation = m_data.mergedAllocations[index.row()];
    if (role == Qt::DisplayRole) {
        return allocationData(allocation, allocation.ipIndex, static_cast<Columns>(index.column()));
    }
    return QVariant();
}

QModelIndex Model::index(int row, int column, const QModelIndex& parent) const
{
    if (row < 0 || column  < 0 || column >= NUM_COLUMNS || row >= rowCount(parent)) {
        return QModelIndex();
    }
    return createIndex(row, column, static_cast<quintptr>(parent.row()));
}

QModelIndex Model::parent(const QModelIndex& child) const
{
    const auto parent = parentRow(child);
    if (parent == -1) {
        return QModelIndex();
    } else {
        return createIndex(parent, 0, -1);
    }
}

int Model::rowCount(const QModelIndex& parent) const
{
    if (parent.isValid()) {
        if (parent.column() != 0 || parent.row() < 0 || parent.row() >= m_data.mergedAllocations.size()
            || parentRow(parent) != -1)
        {
            return 0;
        } else {
            return m_data.mergedAllocations[parent.row()].traces.size();
        }
    }
    return m_data.mergedAllocations.size();
}

int Model::columnCount(const QModelIndex& /*parent*/) const
{
    return NUM_COLUMNS;
}

void Model::loadFile(const QString& file)
{
    beginResetModel();
    m_data.read(file.toStdString());
    endResetModel();
    emit dataReady(generateSummary(m_data));
}

QVariant Model::allocationData(const AllocationData& allocation, const IpIndex& ipIndex, Columns column) const
{
    switch (column) {
    case AllocationsColumn:
        return static_cast<quint64>(allocation.allocations);
    case PeakColumn:
        return static_cast<quint64>(allocation.peak);
    case LeakedColumn:
        return static_cast<quint64>(allocation.leaked);
    case AllocatedColumn:
        return static_cast<quint64>(allocation.allocated);
    case FileColumn:
    case ModuleColumn:
    case FunctionColumn: {
        const auto& ip = m_data.findIp(ipIndex);
        if (column == FunctionColumn) {
            if (ip.functionIndex) {
                return QString::fromStdString(m_data.prettyFunction(m_data.stringify(ip.functionIndex)));
            } else {
                return QLatin1String("0x") + QString::number(ip.instructionPointer, 16);
            }
        } else if (column == ModuleColumn) {
            return QString::fromStdString(m_data.stringify(ip.moduleIndex));
        } else if (ip.fileIndex) {
            auto file = QString::fromStdString(m_data.stringify(ip.fileIndex));
            return file + QLatin1Char(':') + QString::number(ip.line);
        }
        break;
    }
    case NUM_COLUMNS:
        break;
    }
    return QVariant();
}<|endoftext|>


<commit_before>#include <db_cxx.h>
#include <errno.h>
#include <assert.h>

#include <iostream>
using namespace std;

int cmp(DB *db, const DBT *dbt1, const DBT *dbt2) {
    return 0;
}

void test_db(void) {
    DbEnv env(0);
    env.open(NULL, DB_PRIVATE, 0666);
    Db db(&env, 0);

    int r;

    r = db.set_bt_compare(cmp);                 assert(r == 0);
    try {
	r = db.remove("DoesNotExist.db", NULL, 0);
	abort(); // must not make it here.
    } catch (DbException e) {
	assert(e.get_errno() == ENOENT);
    }
    // The db is closed.
    env.close(0);
}

void test_db_env(void) {
    DbEnv dbenv(0);
    int r;

    r = dbenv.set_data_dir(".");    assert(r == 0);
    r = dbenv.set_data_dir("..");   assert(r == 0);
    try {
	r = dbenv.set_data_dir(NULL);
	abort();
    } catch (DbException e) {
	assert(e.get_errno() == EINVAL);
    }
    dbenv.set_errpfx("Prefix");
    dbenv.set_errfile(stdout);
    dbenv.err(0, "Hello %s!\n", "Name");
    dbenv.close(0);
}

int main()
{
    test_db();
    test_db_env();
    return 0;
}
<commit_msg>fix build on fedora 9<commit_after>#include <db_cxx.h>
#include <errno.h>
#include <assert.h>
#include <stdlib.h>

#include <iostream>
using namespace std;

int cmp(DB *db, const DBT *dbt1, const DBT *dbt2) {
    return 0;
}

void test_db(void) {
    DbEnv env(0);
    env.open(NULL, DB_PRIVATE, 0666);
    Db db(&env, 0);

    int r;

    r = db.set_bt_compare(cmp);                 assert(r == 0);
    try {
	r = db.remove("DoesNotExist.db", NULL, 0);
	abort(); // must not make it here.
    } catch (DbException e) {
	assert(e.get_errno() == ENOENT);
    }
    // The db is closed.
    env.close(0);
}

void test_db_env(void) {
    DbEnv dbenv(0);
    int r;

    r = dbenv.set_data_dir(".");    assert(r == 0);
    r = dbenv.set_data_dir("..");   assert(r == 0);
    try {
	r = dbenv.set_data_dir(NULL);
	abort();
    } catch (DbException e) {
	assert(e.get_errno() == EINVAL);
    }
    dbenv.set_errpfx("Prefix");
    dbenv.set_errfile(stdout);
    dbenv.err(0, "Hello %s!\n", "Name");
    dbenv.close(0);
}

int main()
{
    test_db();
    test_db_env();
    return 0;
}
<|endoftext|>