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

#include "VhpiImpl.h"
#include <vector>

extern "C" {
static VhpiCbHdl *sim_init_cb;
static VhpiCbHdl *sim_finish_cb;
static VhpiImpl  *vhpi_table;
}

const char * VhpiImpl::format_to_string(int format)
{
    switch (format) {
    case vhpiBinStrVal:
        return "vhpiBinStrVal";
    case vhpiOctStrVal:
        return "vhpiOctStrVal";
    case vhpiDecStrVal:
        return "vhpiDecStrVal";
    case vhpiHexStrVal:
        return "vhpiHexStrVal";
    case vhpiEnumVal:
        return "vhpiEnumVal";
    case vhpiIntVal:
        return "vhpiIntVal";
    case vhpiLogicVal:
        return "vhpiLogicVal";
    case vhpiRealVal:
        return "vhpiRealVal";
    case vhpiStrVal:
        return "vhpiStrVal";
    case vhpiCharVal:
        return "vhpiCharVal";
    case vhpiTimeVal:
        return "vhpiTimeVal";
    case vhpiPhysVal:
        return "vhpiPhysVal";
    case vhpiObjTypeVal:
        return "vhpiObjTypeVal";
    case vhpiPtrVal:
        return "vhpiPtrVal";
    case vhpiEnumVecVal:
        return "vhpiEnumVecVal";

    default:
        return "unknown";
    }
}

const char *VhpiImpl::reason_to_string(int reason)
{
    switch (reason) {
    case vhpiCbValueChange:
        return "vhpiCbValueChange";
    case vhpiCbStartOfNextCycle:
        return "vhpiCbStartOfNextCycle";
    case vhpiCbStartOfPostponed:
        return "vhpiCbStartOfPostponed";
    case vhpiCbEndOfTimeStep:
        return "vhpiCbEndOfTimeStep";
    case vhpiCbNextTimeStep:
        return "vhpiCbNextTimeStep";
    case vhpiCbAfterDelay:
        return "vhpiCbAfterDelay";
    case vhpiCbStartOfSimulation:
        return "vhpiCbStartOfSimulation";
    case vhpiCbEndOfSimulation:
        return "vhpiCbEndOfSimulation";
    case vhpiCbEndOfProcesses:
        return "vhpiCbEndOfProcesses";
    case vhpiCbLastKnownDeltaCycle:
        return "vhpiCbLastKnownDeltaCycle";
    default:
        return "unknown";
    }
}

void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low)
{
    vhpiTimeT vhpi_time_s;
    vhpi_get_time(&vhpi_time_s, NULL);
    check_vhpi_error();
    *high = vhpi_time_s.high;
    *low = vhpi_time_s.low;
}

GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name)
{
    vhpiIntT type;
    GpiObjHdl *new_obj = NULL;

    if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) {
        LOG_DEBUG("vhpiVerilog returned from vhpi_get(vhpiType, ...)")
        return NULL;
    }
    /* What sort of isntance is this ?*/
    switch (type) {
        case vhpiPortDeclK:
        case vhpiSigDeclK:
        case vhpiIndexedNameK:
            new_obj = new VhpiSignalObjHdl(this, new_hdl);
            break;
        case vhpiCompInstStmtK:
            new_obj = new GpiObjHdl(this, new_hdl);
            break;
        default:
            LOG_WARN("Not able to map type %d to object.");
            return NULL;
    }

    new_obj->initialise(name);

    return new_obj;
}

GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent)
{
    vhpiHandleT new_hdl;
    std::vector<char> writable(name.begin(), name.end());
    writable.push_back('\0');

    new_hdl = vhpi_handle_by_name(&writable[0], NULL);

    if (new_hdl == NULL) {
        LOG_DEBUG("Unable to query vhpi_handle_by_name %s", name.c_str());
        return NULL;
    }

    GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name);
    if (new_obj == NULL) {
        vhpi_release_handle(new_hdl);
        LOG_DEBUG("Unable to fetch object %s", name.c_str());
        return NULL;
    }

    return new_obj;
}

GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent)
{
    GpiObjHdl *parent_hdl = sim_to_hdl<GpiObjHdl*>(parent);
    vhpiHandleT vpi_hdl = parent_hdl->get_handle<vhpiHandleT>();
    vhpiHandleT new_hdl;

    new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vpi_hdl, index);

    if (new_hdl == NULL) {
        LOG_DEBUG("Unable to query vhpi_handle_by_index %s", index);
        return NULL;
    }

    std::string name = vhpi_get_str(vhpiNameP, new_hdl);
    GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name);
    if (new_obj == NULL) {
        vhpi_release_handle(new_hdl);
        LOG_DEBUG("Could not fetch object below entity (%s) at index (%d)",
                                 parent->get_name_str(), index);
        return NULL;
    }

    return new_obj;
}

GpiObjHdl *VhpiImpl::get_root_handle(const char* name)
{
    FENTER
    vhpiHandleT root;
    vhpiHandleT dut;
    GpiObjHdl *rv;
    std::string root_name = name;

    root = vhpi_handle(vhpiRootInst, NULL);
    check_vhpi_error();

    if (!root) {
        LOG_ERROR("VHPI: Attempting to get the root handle failed");
        FEXIT
        return NULL;
    }

    if (name)
        dut = vhpi_handle_by_name(name, NULL);
    else
        dut = vhpi_handle(vhpiDesignUnit, root);
    check_vhpi_error();

    if (!dut) {
        LOG_ERROR("VHPI: Attempting to get the DUT handle failed");
        FEXIT
        return NULL;
    }

    const char *found = vhpi_get_str(vhpiNameP, dut);
    check_vhpi_error();

    if (name != NULL && strcmp(name, found)) {
        LOG_WARN("VHPI: Root '%s' doesn't match requested toplevel %s", found, name);
        FEXIT
        return NULL;
    }

    rv = new GpiObjHdl(this, root);
    rv->initialise(root_name);

    FEXIT
    return rv;
}

GpiCbHdl *VhpiImpl::register_timed_callback(uint64_t time_ps)
{
    VhpiTimedCbHdl *hdl = new VhpiTimedCbHdl(this, time_ps);

    if (hdl->arm_callback()) {
        delete(hdl);
        hdl = NULL;
    }

    return hdl;
}

GpiCbHdl *VhpiImpl::register_readwrite_callback(void)
{
    if (m_read_write.arm_callback())
        return NULL;

    return &m_read_write;
}

GpiCbHdl *VhpiImpl::register_readonly_callback(void)
{
    if (m_read_only.arm_callback())
        return NULL;

    return &m_read_only;
}

GpiCbHdl *VhpiImpl::register_nexttime_callback(void)
{
    if (m_next_phase.arm_callback())
        return NULL;

    return &m_next_phase;
}

int VhpiImpl::deregister_callback(GpiCbHdl *gpi_hdl)
{
    gpi_hdl->cleanup_callback();
    return 0;
}

void VhpiImpl::sim_end(void)
{
    sim_finish_cb->set_call_state(GPI_DELETE);
    vhpi_control(vhpiFinish);
    check_vhpi_error();
}

extern "C" {

// Main entry point for callbacks from simulator
void handle_vhpi_callback(const vhpiCbDataT *cb_data)
{
    VhpiCbHdl *cb_hdl = (VhpiCbHdl*)cb_data->user_data;

    if (!cb_hdl)
        LOG_CRITICAL("VHPI: Callback data corrupted");

    gpi_cb_state_e old_state = cb_hdl->get_call_state();

    if (old_state == GPI_PRIMED) {

        cb_hdl->set_call_state(GPI_CALL);
        cb_hdl->run_callback();

        gpi_cb_state_e new_state = cb_hdl->get_call_state();

        /* We have re-primed in the handler */
        if (new_state != GPI_PRIMED)
            if (cb_hdl->cleanup_callback()) {
                delete cb_hdl;
            }

    }

    return;
};

static void register_initial_callback(void)
{
    FENTER
    sim_init_cb = new VhpiStartupCbHdl(vhpi_table);
    sim_init_cb->arm_callback();
    FEXIT
}

static void register_final_callback(void)
{
    FENTER
    sim_finish_cb = new VhpiShutdownCbHdl(vhpi_table);
    sim_finish_cb->arm_callback();
    FEXIT
}

static void register_embed(void)
{
    vhpi_table = new VhpiImpl("VHPI");
    gpi_register_impl(vhpi_table);
    gpi_load_extra_libs();
}

// pre-defined VHPI registration table
void (*vhpi_startup_routines[])(void) = {
    register_embed,
    register_initial_callback,
    register_final_callback,
    0
};

// For non-VPI compliant applications that cannot find vlog_startup_routines
void vhpi_startup_routines_bootstrap(void) {
    void (*routine)(void);
    int i;
    routine = vhpi_startup_routines[0];
    for (i = 0, routine = vhpi_startup_routines[i];
         routine;
         routine = vhpi_startup_routines[++i]) {
        routine();
    }
}

}

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

#include "VhpiImpl.h"
#include <vector>

extern "C" {
static VhpiCbHdl *sim_init_cb;
static VhpiCbHdl *sim_finish_cb;
static VhpiImpl  *vhpi_table;
}

const char * VhpiImpl::format_to_string(int format)
{
    switch (format) {
    case vhpiBinStrVal:
        return "vhpiBinStrVal";
    case vhpiOctStrVal:
        return "vhpiOctStrVal";
    case vhpiDecStrVal:
        return "vhpiDecStrVal";
    case vhpiHexStrVal:
        return "vhpiHexStrVal";
    case vhpiEnumVal:
        return "vhpiEnumVal";
    case vhpiIntVal:
        return "vhpiIntVal";
    case vhpiLogicVal:
        return "vhpiLogicVal";
    case vhpiRealVal:
        return "vhpiRealVal";
    case vhpiStrVal:
        return "vhpiStrVal";
    case vhpiCharVal:
        return "vhpiCharVal";
    case vhpiTimeVal:
        return "vhpiTimeVal";
    case vhpiPhysVal:
        return "vhpiPhysVal";
    case vhpiObjTypeVal:
        return "vhpiObjTypeVal";
    case vhpiPtrVal:
        return "vhpiPtrVal";
    case vhpiEnumVecVal:
        return "vhpiEnumVecVal";

    default:
        return "unknown";
    }
}

const char *VhpiImpl::reason_to_string(int reason)
{
    switch (reason) {
    case vhpiCbValueChange:
        return "vhpiCbValueChange";
    case vhpiCbStartOfNextCycle:
        return "vhpiCbStartOfNextCycle";
    case vhpiCbStartOfPostponed:
        return "vhpiCbStartOfPostponed";
    case vhpiCbEndOfTimeStep:
        return "vhpiCbEndOfTimeStep";
    case vhpiCbNextTimeStep:
        return "vhpiCbNextTimeStep";
    case vhpiCbAfterDelay:
        return "vhpiCbAfterDelay";
    case vhpiCbStartOfSimulation:
        return "vhpiCbStartOfSimulation";
    case vhpiCbEndOfSimulation:
        return "vhpiCbEndOfSimulation";
    case vhpiCbEndOfProcesses:
        return "vhpiCbEndOfProcesses";
    case vhpiCbLastKnownDeltaCycle:
        return "vhpiCbLastKnownDeltaCycle";
    default:
        return "unknown";
    }
}

void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low)
{
    vhpiTimeT vhpi_time_s;
    vhpi_get_time(&vhpi_time_s, NULL);
    check_vhpi_error();
    *high = vhpi_time_s.high;
    *low = vhpi_time_s.low;
}

GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name)
{
    vhpiIntT type;
    GpiObjHdl *new_obj = NULL;

    if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) {
        LOG_DEBUG("vhpiVerilog returned from vhpi_get(vhpiType, ...)")
        return NULL;
    }
    /* What sort of isntance is this ?*/
    switch (type) {
        case vhpiPortDeclK:
        case vhpiSigDeclK:
        case vhpiIndexedNameK:
            new_obj = new VhpiSignalObjHdl(this, new_hdl);
            break;
        case vhpiForGenerateK:
        case vhpiCompInstStmtK:
            new_obj = new GpiObjHdl(this, new_hdl);
            break;
        default:
            LOG_WARN("Not able to map type %d to object.");
            return NULL;
    }

    new_obj->initialise(name);

    return new_obj;
}

GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent)
{
    vhpiHandleT new_hdl;
    std::vector<char> writable(name.begin(), name.end());
    writable.push_back('\0');

    new_hdl = vhpi_handle_by_name(&writable[0], NULL);

    if (new_hdl == NULL) {
        LOG_DEBUG("Unable to query vhpi_handle_by_name %s", name.c_str());
        return NULL;
    }

    GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name);
    if (new_obj == NULL) {
        vhpi_release_handle(new_hdl);
        LOG_DEBUG("Unable to fetch object %s", name.c_str());
        return NULL;
    }

    return new_obj;
}

GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent)
{
    GpiObjHdl *parent_hdl = sim_to_hdl<GpiObjHdl*>(parent);
    vhpiHandleT vpi_hdl = parent_hdl->get_handle<vhpiHandleT>();
    vhpiHandleT new_hdl;

    new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vpi_hdl, index);

    if (new_hdl == NULL) {
        LOG_DEBUG("Unable to query vhpi_handle_by_index %s", index);
        return NULL;
    }

    std::string name = vhpi_get_str(vhpiNameP, new_hdl);
    GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name);
    if (new_obj == NULL) {
        vhpi_release_handle(new_hdl);
        LOG_DEBUG("Could not fetch object below entity (%s) at index (%d)",
                                 parent->get_name_str(), index);
        return NULL;
    }

    return new_obj;
}

GpiObjHdl *VhpiImpl::get_root_handle(const char* name)
{
    FENTER
    vhpiHandleT root;
    vhpiHandleT dut;
    GpiObjHdl *rv;
    std::string root_name = name;

    root = vhpi_handle(vhpiRootInst, NULL);
    check_vhpi_error();

    if (!root) {
        LOG_ERROR("VHPI: Attempting to get the root handle failed");
        FEXIT
        return NULL;
    }

    if (name)
        dut = vhpi_handle_by_name(name, NULL);
    else
        dut = vhpi_handle(vhpiDesignUnit, root);
    check_vhpi_error();

    if (!dut) {
        LOG_ERROR("VHPI: Attempting to get the DUT handle failed");
        FEXIT
        return NULL;
    }

    const char *found = vhpi_get_str(vhpiNameP, dut);
    check_vhpi_error();

    if (name != NULL && strcmp(name, found)) {
        LOG_WARN("VHPI: Root '%s' doesn't match requested toplevel %s", found, name);
        FEXIT
        return NULL;
    }

    rv = new GpiObjHdl(this, root);
    rv->initialise(root_name);

    FEXIT
    return rv;
}

GpiCbHdl *VhpiImpl::register_timed_callback(uint64_t time_ps)
{
    VhpiTimedCbHdl *hdl = new VhpiTimedCbHdl(this, time_ps);

    if (hdl->arm_callback()) {
        delete(hdl);
        hdl = NULL;
    }

    return hdl;
}

GpiCbHdl *VhpiImpl::register_readwrite_callback(void)
{
    if (m_read_write.arm_callback())
        return NULL;

    return &m_read_write;
}

GpiCbHdl *VhpiImpl::register_readonly_callback(void)
{
    if (m_read_only.arm_callback())
        return NULL;

    return &m_read_only;
}

GpiCbHdl *VhpiImpl::register_nexttime_callback(void)
{
    if (m_next_phase.arm_callback())
        return NULL;

    return &m_next_phase;
}

int VhpiImpl::deregister_callback(GpiCbHdl *gpi_hdl)
{
    gpi_hdl->cleanup_callback();
    return 0;
}

void VhpiImpl::sim_end(void)
{
    sim_finish_cb->set_call_state(GPI_DELETE);
    vhpi_control(vhpiFinish);
    check_vhpi_error();
}

extern "C" {

// Main entry point for callbacks from simulator
void handle_vhpi_callback(const vhpiCbDataT *cb_data)
{
    VhpiCbHdl *cb_hdl = (VhpiCbHdl*)cb_data->user_data;

    if (!cb_hdl)
        LOG_CRITICAL("VHPI: Callback data corrupted");

    gpi_cb_state_e old_state = cb_hdl->get_call_state();

    if (old_state == GPI_PRIMED) {

        cb_hdl->set_call_state(GPI_CALL);
        cb_hdl->run_callback();

        gpi_cb_state_e new_state = cb_hdl->get_call_state();

        /* We have re-primed in the handler */
        if (new_state != GPI_PRIMED)
            if (cb_hdl->cleanup_callback()) {
                delete cb_hdl;
            }

    }

    return;
};

static void register_initial_callback(void)
{
    FENTER
    sim_init_cb = new VhpiStartupCbHdl(vhpi_table);
    sim_init_cb->arm_callback();
    FEXIT
}

static void register_final_callback(void)
{
    FENTER
    sim_finish_cb = new VhpiShutdownCbHdl(vhpi_table);
    sim_finish_cb->arm_callback();
    FEXIT
}

static void register_embed(void)
{
    vhpi_table = new VhpiImpl("VHPI");
    gpi_register_impl(vhpi_table);
    gpi_load_extra_libs();
}

// pre-defined VHPI registration table
void (*vhpi_startup_routines[])(void) = {
    register_embed,
    register_initial_callback,
    register_final_callback,
    0
};

// For non-VPI compliant applications that cannot find vlog_startup_routines
void vhpi_startup_routines_bootstrap(void) {
    void (*routine)(void);
    int i;
    routine = vhpi_startup_routines[0];
    for (i = 0, routine = vhpi_startup_routines[i];
         routine;
         routine = vhpi_startup_routines[++i]) {
        routine();
    }
}

}

GPI_ENTRY_POINT(vhpi, register_embed)
<|endoftext|>


<commit_before>#ifdef JOEDB_HAS_SSH

#include "joedb/server/SSH_Connection.h"
#include "joedb/Exception.h"

#include <fcntl.h>
#include <thread>
#include <chrono>
#include <iostream>

//
// Windows does not define those
//
#ifndef S_IRUSR
#define	S_IRUSR	0000400
#endif

#ifndef S_IWUSR
#define	S_IWUSR	0000200
#endif

namespace joedb
{
 ////////////////////////////////////////////////////////////////////////////
 void SSH_Connection::lock()
 ////////////////////////////////////////////////////////////////////////////
 {
  if (trace)
   std::cerr << full_remote_name << ": lock()... ";

  while (true)
  {
   sftp_file file = sftp_open
   (
    sftp.get(),
    mutex_file_name.c_str(),
    O_CREAT | O_EXCL,
    S_IRUSR
   );

   if (file)
   {
    sftp_close(file);
    break;
   }
   else
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }

  if (trace)
   std::cerr << "done.\n";
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Connection::unlock()
 ////////////////////////////////////////////////////////////////////////////
 {
  if (trace)
   std::cerr << full_remote_name << ": unlock()\n";

  if (sftp_unlink(sftp.get(), mutex_file_name.c_str()) < 0)
   throw Exception("Error removing remote mutex");
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Connection::raw_pull(Journal_File &client_journal)
 ////////////////////////////////////////////////////////////////////////////
 {
  if (trace)
   std::cerr << full_remote_name << ": pull()... ";

  server_position = 0;

  try
  {
   ssh::SFTP_Attributes attributes
   (
    sftp_stat(sftp.get(), remote_file_name.c_str())
   );

   server_position = int64_t(attributes.get()->size);
  }
  catch (const joedb::Exception &)
  {
  }

  const int64_t client_position = client_journal.get_checkpoint_position();

  if (client_position < server_position)
  {
   std::vector<char> v(size_t(server_position - client_position));

   if (trace)
    std::cerr << v.size() << " bytes... ";

   sftp_file file = sftp_open
   (
    sftp.get(),
    remote_file_name.c_str(),
    O_RDONLY,
    S_IRUSR | S_IWUSR
   );

   if (file)
   {
    int seek_result = sftp_seek64(file, uint64_t(client_position));
    ssize_t read_result = sftp_read(file, v.data(), v.size());
    sftp_close(file);

    if (seek_result < 0 || read_result != ssize_t(v.size()))
     throw Exception("Error during sftp_read");

    client_journal.append_raw_tail
    (
     v
    );
   }
   else
    throw Exception("Could not open remote file for reading");
  }
  else if (server_position > 0 && client_position > server_position)
   throw Exception("Trying to pull when ahead of server");

  if (trace)
   std::cerr << "done\n";
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Connection::raw_push(Readonly_Journal &client_journal)
 ////////////////////////////////////////////////////////////////////////////
 {
  if (trace)
   std::cerr << full_remote_name << ": push()... ";

  const int64_t client_position = client_journal.get_checkpoint_position();
  if (client_position > server_position)
  {
   std::vector<char> v(client_journal.get_raw_tail(server_position));

   if (trace)
    std::cerr << v.size() << " bytes... ";

   sftp_file file = sftp_open
   (
    sftp.get(),
    remote_file_name.c_str(),
    O_WRONLY | O_APPEND | O_CREAT,
    S_IRUSR | S_IWUSR
   );

   if (file)
   {
    ssize_t written = sftp_write(file, v.data(), v.size());
    sftp_close(file);
    if (written < ssize_t(v.size()))
     throw Exception("Incomplete write during push");
   }
   else
    throw Exception("Could not open remote file for writing");
  }

  if (trace)
   std::cerr << "done\n";
 }

 ////////////////////////////////////////////////////////////////////////////
 SSH_Connection::SSH_Connection
 ////////////////////////////////////////////////////////////////////////////
 (
  std::string user,
  std::string host,
  int port,
  std::string remote_file_name,
  bool trace,
  int ssh_log_level
 ):
  remote_file_name(remote_file_name),
  trace(trace),
  mutex_file_name(remote_file_name + ".mutex"),
  full_remote_name(user + "@" + host + ":" + remote_file_name),
  session
  (
   user,
   host,
   port,
   ssh_log_level
  ),
  sftp(session),
  server_position(0)
 {
 }

#define RETRY(x, n)\
 for (int i = n; --i >= 0;)\
  try\
  {\
   x;\
   return;\
  }\
  catch (const Exception &e)\
  {\
   if (trace)\
    std::cerr << "Error: " << e.what() << '\n';\
   reset();\
  }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Robust_Connection::pull(Journal_File &client_journal)
 ////////////////////////////////////////////////////////////////////////////
 {
  RETRY(connection->pull(client_journal), 2);
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Robust_Connection::lock_pull(Journal_File &client_journal)
 ////////////////////////////////////////////////////////////////////////////
 {
  RETRY(connection->lock_pull(client_journal), 2);
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Robust_Connection::push_unlock(Readonly_Journal &client_journal)
 ////////////////////////////////////////////////////////////////////////////
 {
  RETRY(connection->push_unlock(client_journal), 2);
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Robust_Connection::reset()
 ////////////////////////////////////////////////////////////////////////////
 {
  if (trace && connection)
  {
   std::cerr << "An error occurred, trying to reset the connection...\n";
   std::this_thread::sleep_for(std::chrono::seconds(10));
  }

  for (int i = 100; --i >= 0;)
   try
   {
    connection.reset
    (
     new SSH_Connection
     (
      user,
      host,
      port,
      remote_file_name,
      trace,
      ssh_log_level
     )
    );
    return;
   }
   catch (const Exception &e)
   {
    if (trace)
    {
     std::cerr << "Connection failed: " << e.what() << '\n';
     std::cerr << "Will retry after a pause...\n";
    }
    std::this_thread::sleep_for(std::chrono::seconds(10));
   }

  throw Exception("Impossible to connect. Giving up");
 }

 ////////////////////////////////////////////////////////////////////////////
 SSH_Robust_Connection::SSH_Robust_Connection
 ////////////////////////////////////////////////////////////////////////////
 (
  std::string user,
  std::string host,
  int port,
  std::string remote_file_name,
  bool trace,
  int ssh_log_level
 ):
  user(user),
  host(host),
  port(port),
  remote_file_name(remote_file_name),
  trace(trace),
  ssh_log_level(ssh_log_level)
 {
  reset();
 }
}

#endif
<commit_msg>code formatting<commit_after>#ifdef JOEDB_HAS_SSH

#include "joedb/server/SSH_Connection.h"
#include "joedb/Exception.h"

#include <fcntl.h>
#include <thread>
#include <chrono>
#include <iostream>

//
// Windows does not define those
//
#ifndef S_IRUSR
#define	S_IRUSR	0000400
#endif

#ifndef S_IWUSR
#define	S_IWUSR	0000200
#endif

namespace joedb
{
 ////////////////////////////////////////////////////////////////////////////
 void SSH_Connection::lock()
 ////////////////////////////////////////////////////////////////////////////
 {
  if (trace)
   std::cerr << full_remote_name << ": lock()... ";

  while (true)
  {
   sftp_file file = sftp_open
   (
    sftp.get(),
    mutex_file_name.c_str(),
    O_CREAT | O_EXCL,
    S_IRUSR
   );

   if (file)
   {
    sftp_close(file);
    break;
   }
   else
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }

  if (trace)
   std::cerr << "done.\n";
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Connection::unlock()
 ////////////////////////////////////////////////////////////////////////////
 {
  if (trace)
   std::cerr << full_remote_name << ": unlock()\n";

  if (sftp_unlink(sftp.get(), mutex_file_name.c_str()) < 0)
   throw Exception("Error removing remote mutex");
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Connection::raw_pull(Journal_File &client_journal)
 ////////////////////////////////////////////////////////////////////////////
 {
  if (trace)
   std::cerr << full_remote_name << ": pull()... ";

  server_position = 0;

  try
  {
   ssh::SFTP_Attributes attributes
   (
    sftp_stat(sftp.get(), remote_file_name.c_str())
   );

   server_position = int64_t(attributes.get()->size);
  }
  catch (const joedb::Exception &)
  {
  }

  const int64_t client_position = client_journal.get_checkpoint_position();

  if (client_position < server_position)
  {
   std::vector<char> v(size_t(server_position - client_position));

   if (trace)
    std::cerr << v.size() << " bytes... ";

   sftp_file file = sftp_open
   (
    sftp.get(),
    remote_file_name.c_str(),
    O_RDONLY,
    S_IRUSR | S_IWUSR
   );

   if (file)
   {
    int seek_result = sftp_seek64(file, uint64_t(client_position));
    ssize_t read_result = sftp_read(file, v.data(), v.size());
    sftp_close(file);

    if (seek_result < 0 || read_result != ssize_t(v.size()))
     throw Exception("Error during sftp_read");

    client_journal.append_raw_tail(v);
   }
   else
    throw Exception("Could not open remote file for reading");
  }
  else if (server_position > 0 && client_position > server_position)
   throw Exception("Trying to pull when ahead of server");

  if (trace)
   std::cerr << "done\n";
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Connection::raw_push(Readonly_Journal &client_journal)
 ////////////////////////////////////////////////////////////////////////////
 {
  if (trace)
   std::cerr << full_remote_name << ": push()... ";

  const int64_t client_position = client_journal.get_checkpoint_position();
  if (client_position > server_position)
  {
   std::vector<char> v(client_journal.get_raw_tail(server_position));

   if (trace)
    std::cerr << v.size() << " bytes... ";

   sftp_file file = sftp_open
   (
    sftp.get(),
    remote_file_name.c_str(),
    O_WRONLY | O_APPEND | O_CREAT,
    S_IRUSR | S_IWUSR
   );

   if (file)
   {
    ssize_t written = sftp_write(file, v.data(), v.size());
    sftp_close(file);
    if (written < ssize_t(v.size()))
     throw Exception("Incomplete write during push");
   }
   else
    throw Exception("Could not open remote file for writing");
  }

  if (trace)
   std::cerr << "done\n";
 }

 ////////////////////////////////////////////////////////////////////////////
 SSH_Connection::SSH_Connection
 ////////////////////////////////////////////////////////////////////////////
 (
  std::string user,
  std::string host,
  int port,
  std::string remote_file_name,
  bool trace,
  int ssh_log_level
 ):
  remote_file_name(remote_file_name),
  trace(trace),
  mutex_file_name(remote_file_name + ".mutex"),
  full_remote_name(user + "@" + host + ":" + remote_file_name),
  session
  (
   user,
   host,
   port,
   ssh_log_level
  ),
  sftp(session),
  server_position(0)
 {
 }

#define RETRY(x, n)\
 for (int i = n; --i >= 0;)\
  try\
  {\
   x;\
   return;\
  }\
  catch (const Exception &e)\
  {\
   if (trace)\
    std::cerr << "Error: " << e.what() << '\n';\
   reset();\
  }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Robust_Connection::pull(Journal_File &client_journal)
 ////////////////////////////////////////////////////////////////////////////
 {
  RETRY(connection->pull(client_journal), 2);
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Robust_Connection::lock_pull(Journal_File &client_journal)
 ////////////////////////////////////////////////////////////////////////////
 {
  RETRY(connection->lock_pull(client_journal), 2);
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Robust_Connection::push_unlock(Readonly_Journal &client_journal)
 ////////////////////////////////////////////////////////////////////////////
 {
  RETRY(connection->push_unlock(client_journal), 2);
 }

 ////////////////////////////////////////////////////////////////////////////
 void SSH_Robust_Connection::reset()
 ////////////////////////////////////////////////////////////////////////////
 {
  if (trace && connection)
  {
   std::cerr << "An error occurred, trying to reset the connection...\n";
   std::this_thread::sleep_for(std::chrono::seconds(10));
  }

  for (int i = 100; --i >= 0;)
   try
   {
    connection.reset
    (
     new SSH_Connection
     (
      user,
      host,
      port,
      remote_file_name,
      trace,
      ssh_log_level
     )
    );
    return;
   }
   catch (const Exception &e)
   {
    if (trace)
    {
     std::cerr << "Connection failed: " << e.what() << '\n';
     std::cerr << "Will retry after a pause...\n";
    }
    std::this_thread::sleep_for(std::chrono::seconds(10));
   }

  throw Exception("Impossible to connect. Giving up");
 }

 ////////////////////////////////////////////////////////////////////////////
 SSH_Robust_Connection::SSH_Robust_Connection
 ////////////////////////////////////////////////////////////////////////////
 (
  std::string user,
  std::string host,
  int port,
  std::string remote_file_name,
  bool trace,
  int ssh_log_level
 ):
  user(user),
  host(host),
  port(port),
  remote_file_name(remote_file_name),
  trace(trace),
  ssh_log_level(ssh_log_level)
 {
  reset();
 }
}

#endif
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------
/// \file   async_file_logger.cpp
/// \author Serge Aleynikov
//----------------------------------------------------------------------------
/// \brief Asynchronous file logger
//----------------------------------------------------------------------------
// Copyright (c) 2012 Omnibius, LLC
// Created: 2012-03-20
//----------------------------------------------------------------------------
/*
***** BEGIN LICENSE BLOCK *****

This file is part of utxx open-source project.

Copyright (C) 2012 Serge Aleynikov <saleyn@gmail.com>

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

This library 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
Lesser General Public License 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

***** END LICENSE BLOCK *****
*/
#pragma once

#include <utxx/atomic.hpp>
#include <utxx/synch.hpp>
#include <iostream>
#include <functional>
#include <thread>
#include <memory>
#include <string>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

namespace utxx {

#ifdef DEBUG_ASYNC_LOGGER
#   define ASYNC_TRACE(...) printf(__VA_ARGS__);
#else
#   define ASYNC_TRACE(...)
#endif

//-----------------------------------------------------------------------------
/// Traits of asynchronous logger using file stream writing
//-----------------------------------------------------------------------------
struct async_file_logger_traits {
    using allocator  = std::allocator<char>;
    using event_type = futex;
    using file_type  = FILE*;
    static constexpr const file_type null_file_value = nullptr;

    //-------------------------------------------------------------------------
    /// Represents a text message to be logged by the logger
    //-------------------------------------------------------------------------
    class msg_type {
        size_t m_size;
        char*  m_data;
    public:
        explicit msg_type(const char* a_data, size_t a_size)
            : m_size(a_size)
            , m_data(const_cast<char*>(a_data))
        {}

        bool        empty() const { return m_size; }
        size_t      size()  const { return m_size; }
        const char* data()  const { return m_data; }
        char*       data()        { return m_data; }
    };

    static file_type file_open(const std::string& a_filename) {
        return fopen(a_filename.c_str(), "a+");
    };

    static int file_write(file_type a_fd, const char* a_data, size_t a_sz) {
        return fwrite(a_data, a_sz, 1, a_fd);
    };

    static int file_close(file_type a_fd) { return fclose(a_fd); }
    static int file_flush(file_type a_fd) { return fflush(a_fd); }

    static const int commit_timeout = 1000;  // commit interval in usecs
    static const int write_buf_sz   = 256;
};

//-----------------------------------------------------------------------------
/// Traits of asynchronous logger using direct file I/O writing
//-----------------------------------------------------------------------------
struct async_fd_logger_traits : public async_file_logger_traits {
    using file_type  = int;
    static constexpr const file_type null_file_value = -1;

    static file_type file_open(const std::string& a_filename) {
        return ::open(a_filename.c_str(), O_CREAT | O_APPEND | O_RDWR);
    };

    static int file_write(file_type a_fd, const char* a_data, size_t a_sz) {
        return ::write(a_fd, a_data, a_sz);
    };

    static int file_close(file_type a_fd) { return ::close(a_fd); }
    static int file_flush(file_type a_fd) { return 0; }
};

//-----------------------------------------------------------------------------
/// Asynchronous logger of text messages.
//-----------------------------------------------------------------------------
template<typename traits = async_file_logger_traits>
class basic_async_logger {
protected:
    class cons: public traits::msg_type {
        using base      = typename traits::msg_type;
        using allocator = typename traits::allocator;
    public:
        cons(const char* a_data, size_t a_sz) : base(a_data, a_sz) {}

        cons* next()        { return m_next; }
        void  next(cons* p) { m_next = p;    }
    private:
        cons* m_next = nullptr;
    };

    using allocator    = typename traits::allocator;
    using event_type   = typename traits::event_type;
    using log_msg_type = cons;
    using file_type    = typename traits::file_type;

    std::unique_ptr<std::thread> m_thread;
    allocator                    m_allocator;
    file_type                    m_file;
    std::atomic<log_msg_type*>   m_head;
    bool                         m_cancel;
    int                          m_max_queue_size;
    std::string                  m_filename;
    event_type                   m_event;
    bool                         m_notify_immediate;

    // Invoked by the async thread to flush messages from queue to file
    int  commit(const struct timespec* tsp = NULL);
    // Invoked by the async thread
    void run(std::condition_variable* a_cv);
    // Print error message
    void print_error(int, const char* what, int line);

public:
    explicit basic_async_logger(const allocator& alloc = allocator())
        : m_allocator(alloc)
        , m_file(traits::null_file_value)
        , m_head(nullptr)
        , m_cancel(false)
        , m_max_queue_size(0)
        , m_notify_immediate(true)
    {}

    ~basic_async_logger() {
        if (m_file != traits::null_file_value)
            stop();
    }

    /// Initialize and start asynchronous file writer
    /// @param a_filename           - name of the file
    /// @param a_notify_immediate   - whether to notify the I/O thread on write
    int  start(const std::string& filename, bool a_notify_immediate = true);

    /// Stop asynchronous file writering thread
    void stop();

    /// @return name of the log file
    const std::string&  filename()         const { return m_filename;         }
    /// @return max size of the commit queue
    const int           max_queue_size()   const { return m_max_queue_size;   }
    /// @return indicates if async thread must be notified immediately
    /// when message is written to queue
    int                 notify_immediate() const { return m_notify_immediate; }

    /// Allocate a message of size \a a_sz.
    /// The content of the message is accessible via its data() property
    log_msg_type* allocate(size_t a_sz);
    /// Deallocate an object previously allocated by call to allocate().
    void  deallocate(log_msg_type* a_msg);

    /// Write a message to the logger by making of copy of
    int   write_copy(const void* a_data, size_t a_sz);
    // Enqueues msg to internal queue
    int   write(log_msg_type* msg);

    /// Callback to be called on file I/O error
    std::function<void (int, const char*)> on_error;
};

//-----------------------------------------------------------------------------
/// Asynchronous text logger
//-----------------------------------------------------------------------------
template <class Traits = async_file_logger_traits>
struct text_file_logger: public basic_async_logger<Traits> {
    using base          = basic_async_logger<Traits>;
    using log_msg_type  = typename base::log_msg_type;
    using allocator     = typename base::allocator;

    explicit text_file_logger(const allocator& alloc = allocator())
        : base(alloc)
    {}

    /// Formatted write with arguments
    int fwrite(const char* fmt, ...) {
        va_list ap;
        va_start(ap, fmt);
        int result = vwrite(fmt, ap);
        va_end(ap);

        return result;
    }

    /// Write string to file
    int write(const char* a) {
        return (!this->m_file || this->m_cancel)
            ? -1 : this->write_copy((void*)a, strlen(a));
    }

    /// Write \a s to file asynchronously
    int write(const std::string& a) {
        return (!this->m_file || this->m_cancel)
            ? -1 : this->write_copy((void*)a.c_str(), a.size());
    }

    /// Formatted write with argumets
    int vwrite(const char* fmt, va_list ap) {
        if (!this->m_file || this->m_cancel)
            return -1;

        char buf[Traits::write_buf_sz];
        int n = vsnprintf(buf, sizeof(buf)-1, fmt, ap);
        return this->write_copy(buf, n);
    }
};

//-----------------------------------------------------------------------------
// Implementation
//-----------------------------------------------------------------------------

template<typename traits>
int basic_async_logger<traits>::
start(const std::string& a_filename, bool a_notify_immediate)
{
    if (m_file)
        return -1;

    m_event.reset();

    m_filename          = a_filename;
    m_head              = nullptr;
    m_cancel            = false;
    m_notify_immediate  = a_notify_immediate;

    if (!(m_file = traits::file_open(m_filename))) {
        print_error(-1, strerror(errno), __LINE__);
        return -2;
    }

    std::condition_variable      cv;
    std::mutex                   mtx;
    std::unique_lock<std::mutex> guard(mtx);

    m_thread.reset(new std::thread([this, &cv]() { run(&cv); }));

    cv.wait(guard);
    return 0;
}

//-----------------------------------------------------------------------------
template<typename traits>
void basic_async_logger<traits>::stop()
{
    if (m_file == traits::null_file_value)
        return;

    m_cancel = true;
    ASYNC_TRACE("Stopping async logger (head %p)\n", m_head);
    m_event.signal();
    if (m_thread) {
        m_thread->join();
        m_thread.reset();
    }
}

//-----------------------------------------------------------------------------
template<typename traits>
void basic_async_logger<traits>::run(std::condition_variable* a_cv)
{
    // Notify the caller this thread is ready
    a_cv->notify_one();

    ASYNC_TRACE("Started async logging thread (cancel=%s)\n",
        m_cancel ? "true" : "false");

    static const timespec ts{
        traits::commit_timeout / 1000,
       (traits::commit_timeout % 1000) * 1000000
    };

    while (true) {
        int n = commit(&ts);

        ASYNC_TRACE("Async thread result: %d (head=%p, cancel=%s)\n",
            n, m_head, m_cancel ? "true" : "false" );

        if (n || (!m_head && m_cancel))
            break;
    }

    traits::file_close(m_file);
    m_file = traits::null_file_value;
}

//-----------------------------------------------------------------------------
template<typename traits>
int basic_async_logger<traits>::commit(const struct timespec* tsp)
{
    ASYNC_TRACE("Committing head: %p\n", m_head);

    int old_val = m_event.value();

    while (!m_head.load(std::memory_order_relaxed)) {
        m_event.wait(tsp, &old_val);

        if (m_cancel && !m_head.load(std::memory_order_relaxed))
            return 0;
    }

    log_msg_type* cur_head;

    do {
        cur_head = m_head.load(std::memory_order_relaxed);
    } while (!m_head.compare_exchange_weak(cur_head, nullptr,
                                           std::memory_order_release,
                                           std::memory_order_relaxed));

    ASYNC_TRACE(" --> cur head: %p, new head: %p\n", cur_head, m_head);

    assert(cur_head);

    int   count = 0;
    log_msg_type* last = nullptr;
    log_msg_type* next = cur_head;

    // Reverse the section of this list
    for (auto p = cur_head; next; count++, p = next) {
        ASYNC_TRACE("Set last[%p] -> next[%p]\n", next, last);
        next = p->next();
        p->next(last);
        last = p;
    }

    assert(last);
    ASYNC_TRACE("Total (%d). Sublist's head: %p (%s)\n",
                count, last, last->c_str());

    if (m_max_queue_size < count)
        m_max_queue_size = count;

    next = last;

    for (auto p = last; next; p = next) {
        if (traits::file_write(m_file, p->data(), p->size()) < 0)
            return -1;
        next = p->next();
        p->next(nullptr);
        ASYNC_TRACE("Wrote (%ld bytes): %p (next: %p): %s\n",
                    p->size(), p, next, p->c_str());
        deallocate(p);
    }

    if (traits::file_flush(m_file) < 0)
        return -2;

    return 0;
}

//-----------------------------------------------------------------------------
template<typename traits>
inline typename basic_async_logger<traits>::log_msg_type*
basic_async_logger<traits>::
allocate(size_t  a_sz)
{
    auto   size  = sizeof(log_msg_type) + a_sz;
    char*  p     = m_allocator.allocate(size);
    char*  data  = p + sizeof(log_msg_type);
    auto   q     = reinterpret_cast<log_msg_type*>(p);
    new   (q) log_msg_type(data, a_sz);
    return q;
}

//-----------------------------------------------------------------------------
template<typename traits>
inline void basic_async_logger<traits>::
deallocate(log_msg_type* a_msg)
{
    auto   size  = sizeof(log_msg_type) + a_msg->size();
    a_msg->~log_msg_type();
    m_allocator.deallocate(reinterpret_cast<char*>(a_msg), size);
}

//-----------------------------------------------------------------------------
template<typename traits>
inline int basic_async_logger<traits>::
write_copy(const void* a_data, size_t a_sz)
{
    auto msg = allocate(a_sz);
    memcpy(msg->data(), a_data, a_sz);
    return write(msg);
}

//-----------------------------------------------------------------------------
template<typename traits>
inline int basic_async_logger<traits>::write(log_msg_type* msg)
{
    if (!msg)
        return -3;

    int n = msg->size();

    log_msg_type* last_head;

    do {
        last_head = m_head.load(std::memory_order_relaxed);
        msg->next(last_head);
    } while (!m_head.compare_exchange_weak(last_head, msg,
                                           std::memory_order_release,
                                           std::memory_order_relaxed));
    if (!last_head && m_notify_immediate)
        m_event.signal();

    ASYNC_TRACE("write - cur head: %p, prev head: %p\n",
                m_head, last_head);
    return n;
}

//-----------------------------------------------------------------------------
template<typename traits>
void basic_async_logger<traits>::
print_error(int a_errno, const char* a_what, int a_line) {
    if (on_error)
        on_error(a_errno, a_what);
    else
        std::cerr << "Error " << a_errno << " writing to file \""  << m_filename
                  << "\": "   << a_what << " [" << __FILE__ << ':' << a_line
                  << ']'      << std::endl;
}

} // namespace utxx
<commit_msg>Removed reference to utxx::atomic<commit_after>//----------------------------------------------------------------------------
/// \file   async_file_logger.cpp
/// \author Serge Aleynikov
//----------------------------------------------------------------------------
/// \brief Asynchronous file logger
//----------------------------------------------------------------------------
// Copyright (c) 2012 Omnibius, LLC
// Created: 2012-03-20
//----------------------------------------------------------------------------
/*
***** BEGIN LICENSE BLOCK *****

This file is part of utxx open-source project.

Copyright (C) 2012 Serge Aleynikov <saleyn@gmail.com>

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

This library 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
Lesser General Public License 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

***** END LICENSE BLOCK *****
*/
#pragma once

#include <utxx/synch.hpp>
#include <iostream>
#include <functional>
#include <atomic>
#include <thread>
#include <memory>
#include <string>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

namespace utxx {

#ifdef DEBUG_ASYNC_LOGGER
#   define ASYNC_TRACE(...) printf(__VA_ARGS__);
#else
#   define ASYNC_TRACE(...)
#endif

//-----------------------------------------------------------------------------
/// Traits of asynchronous logger using file stream writing
//-----------------------------------------------------------------------------
struct async_file_logger_traits {
    using allocator  = std::allocator<char>;
    using event_type = futex;
    using file_type  = FILE*;
    static constexpr const file_type null_file_value = nullptr;

    //-------------------------------------------------------------------------
    /// Represents a text message to be logged by the logger
    //-------------------------------------------------------------------------
    class msg_type {
        size_t m_size;
        char*  m_data;
    public:
        explicit msg_type(const char* a_data, size_t a_size)
            : m_size(a_size)
            , m_data(const_cast<char*>(a_data))
        {}

        bool        empty() const { return m_size; }
        size_t      size()  const { return m_size; }
        const char* data()  const { return m_data; }
        char*       data()        { return m_data; }
    };

    static file_type file_open(const std::string& a_filename) {
        return fopen(a_filename.c_str(), "a+");
    };

    static int file_write(file_type a_fd, const char* a_data, size_t a_sz) {
        return fwrite(a_data, a_sz, 1, a_fd);
    };

    static int file_close(file_type a_fd) { return fclose(a_fd); }
    static int file_flush(file_type a_fd) { return fflush(a_fd); }

    static const int commit_timeout = 1000;  // commit interval in usecs
    static const int write_buf_sz   = 256;
};

//-----------------------------------------------------------------------------
/// Traits of asynchronous logger using direct file I/O writing
//-----------------------------------------------------------------------------
struct async_fd_logger_traits : public async_file_logger_traits {
    using file_type  = int;
    static constexpr const file_type null_file_value = -1;

    static file_type file_open(const std::string& a_filename) {
        return ::open(a_filename.c_str(), O_CREAT | O_APPEND | O_RDWR);
    };

    static int file_write(file_type a_fd, const char* a_data, size_t a_sz) {
        return ::write(a_fd, a_data, a_sz);
    };

    static int file_close(file_type a_fd) { return ::close(a_fd); }
    static int file_flush(file_type a_fd) { return 0; }
};

//-----------------------------------------------------------------------------
/// Asynchronous logger of text messages.
//-----------------------------------------------------------------------------
template<typename traits = async_file_logger_traits>
class basic_async_logger {
protected:
    class cons: public traits::msg_type {
        using base      = typename traits::msg_type;
        using allocator = typename traits::allocator;
    public:
        cons(const char* a_data, size_t a_sz) : base(a_data, a_sz) {}

        cons* next()        { return m_next; }
        void  next(cons* p) { m_next = p;    }
    private:
        cons* m_next = nullptr;
    };

    using allocator    = typename traits::allocator;
    using event_type   = typename traits::event_type;
    using log_msg_type = cons;
    using file_type    = typename traits::file_type;

    std::unique_ptr<std::thread> m_thread;
    allocator                    m_allocator;
    file_type                    m_file;
    std::atomic<log_msg_type*>   m_head;
    bool                         m_cancel;
    int                          m_max_queue_size;
    std::string                  m_filename;
    event_type                   m_event;
    bool                         m_notify_immediate;

    // Invoked by the async thread to flush messages from queue to file
    int  commit(const struct timespec* tsp = NULL);
    // Invoked by the async thread
    void run(std::condition_variable* a_cv);
    // Print error message
    void print_error(int, const char* what, int line);

public:
    explicit basic_async_logger(const allocator& alloc = allocator())
        : m_allocator(alloc)
        , m_file(traits::null_file_value)
        , m_head(nullptr)
        , m_cancel(false)
        , m_max_queue_size(0)
        , m_notify_immediate(true)
    {}

    ~basic_async_logger() {
        if (m_file != traits::null_file_value)
            stop();
    }

    /// Initialize and start asynchronous file writer
    /// @param a_filename           - name of the file
    /// @param a_notify_immediate   - whether to notify the I/O thread on write
    int  start(const std::string& filename, bool a_notify_immediate = true);

    /// Stop asynchronous file writering thread
    void stop();

    /// @return name of the log file
    const std::string&  filename()         const { return m_filename;         }
    /// @return max size of the commit queue
    const int           max_queue_size()   const { return m_max_queue_size;   }
    /// @return indicates if async thread must be notified immediately
    /// when message is written to queue
    int                 notify_immediate() const { return m_notify_immediate; }

    /// Allocate a message of size \a a_sz.
    /// The content of the message is accessible via its data() property
    log_msg_type* allocate(size_t a_sz);
    /// Deallocate an object previously allocated by call to allocate().
    void  deallocate(log_msg_type* a_msg);

    /// Write a message to the logger by making of copy of
    int   write_copy(const void* a_data, size_t a_sz);
    // Enqueues msg to internal queue
    int   write(log_msg_type* msg);

    /// Callback to be called on file I/O error
    std::function<void (int, const char*)> on_error;
};

//-----------------------------------------------------------------------------
/// Asynchronous text logger
//-----------------------------------------------------------------------------
template <class Traits = async_file_logger_traits>
struct text_file_logger: public basic_async_logger<Traits> {
    using base          = basic_async_logger<Traits>;
    using log_msg_type  = typename base::log_msg_type;
    using allocator     = typename base::allocator;

    explicit text_file_logger(const allocator& alloc = allocator())
        : base(alloc)
    {}

    /// Formatted write with arguments
    int fwrite(const char* fmt, ...) {
        va_list ap;
        va_start(ap, fmt);
        int result = vwrite(fmt, ap);
        va_end(ap);

        return result;
    }

    /// Write string to file
    int write(const char* a) {
        return (!this->m_file || this->m_cancel)
            ? -1 : this->write_copy((void*)a, strlen(a));
    }

    /// Write \a s to file asynchronously
    int write(const std::string& a) {
        return (!this->m_file || this->m_cancel)
            ? -1 : this->write_copy((void*)a.c_str(), a.size());
    }

    /// Formatted write with argumets
    int vwrite(const char* fmt, va_list ap) {
        if (!this->m_file || this->m_cancel)
            return -1;

        char buf[Traits::write_buf_sz];
        int n = vsnprintf(buf, sizeof(buf)-1, fmt, ap);
        return this->write_copy(buf, n);
    }
};

//-----------------------------------------------------------------------------
// Implementation
//-----------------------------------------------------------------------------

template<typename traits>
int basic_async_logger<traits>::
start(const std::string& a_filename, bool a_notify_immediate)
{
    if (m_file)
        return -1;

    m_event.reset();

    m_filename          = a_filename;
    m_head              = nullptr;
    m_cancel            = false;
    m_notify_immediate  = a_notify_immediate;

    if (!(m_file = traits::file_open(m_filename))) {
        print_error(-1, strerror(errno), __LINE__);
        return -2;
    }

    std::condition_variable      cv;
    std::mutex                   mtx;
    std::unique_lock<std::mutex> guard(mtx);

    m_thread.reset(new std::thread([this, &cv]() { run(&cv); }));

    cv.wait(guard);
    return 0;
}

//-----------------------------------------------------------------------------
template<typename traits>
void basic_async_logger<traits>::stop()
{
    if (m_file == traits::null_file_value)
        return;

    m_cancel = true;
    ASYNC_TRACE("Stopping async logger (head %p)\n", m_head);
    m_event.signal();
    if (m_thread) {
        m_thread->join();
        m_thread.reset();
    }
}

//-----------------------------------------------------------------------------
template<typename traits>
void basic_async_logger<traits>::run(std::condition_variable* a_cv)
{
    // Notify the caller this thread is ready
    a_cv->notify_one();

    ASYNC_TRACE("Started async logging thread (cancel=%s)\n",
        m_cancel ? "true" : "false");

    static const timespec ts{
        traits::commit_timeout / 1000,
       (traits::commit_timeout % 1000) * 1000000
    };

    while (true) {
        int n = commit(&ts);

        ASYNC_TRACE("Async thread result: %d (head=%p, cancel=%s)\n",
            n, m_head, m_cancel ? "true" : "false" );

        if (n || (!m_head && m_cancel))
            break;
    }

    traits::file_close(m_file);
    m_file = traits::null_file_value;
}

//-----------------------------------------------------------------------------
template<typename traits>
int basic_async_logger<traits>::commit(const struct timespec* tsp)
{
    ASYNC_TRACE("Committing head: %p\n", m_head);

    int old_val = m_event.value();

    while (!m_head.load(std::memory_order_relaxed)) {
        m_event.wait(tsp, &old_val);

        if (m_cancel && !m_head.load(std::memory_order_relaxed))
            return 0;
    }

    log_msg_type* cur_head;

    do {
        cur_head = m_head.load(std::memory_order_relaxed);
    } while (!m_head.compare_exchange_weak(cur_head, nullptr,
                                           std::memory_order_release,
                                           std::memory_order_relaxed));

    ASYNC_TRACE(" --> cur head: %p, new head: %p\n", cur_head, m_head);

    assert(cur_head);

    int   count = 0;
    log_msg_type* last = nullptr;
    log_msg_type* next = cur_head;

    // Reverse the section of this list
    for (auto p = cur_head; next; count++, p = next) {
        ASYNC_TRACE("Set last[%p] -> next[%p]\n", next, last);
        next = p->next();
        p->next(last);
        last = p;
    }

    assert(last);
    ASYNC_TRACE("Total (%d). Sublist's head: %p (%s)\n",
                count, last, last->c_str());

    if (m_max_queue_size < count)
        m_max_queue_size = count;

    next = last;

    for (auto p = last; next; p = next) {
        if (traits::file_write(m_file, p->data(), p->size()) < 0)
            return -1;
        next = p->next();
        p->next(nullptr);
        ASYNC_TRACE("Wrote (%ld bytes): %p (next: %p): %s\n",
                    p->size(), p, next, p->c_str());
        deallocate(p);
    }

    if (traits::file_flush(m_file) < 0)
        return -2;

    return 0;
}

//-----------------------------------------------------------------------------
template<typename traits>
inline typename basic_async_logger<traits>::log_msg_type*
basic_async_logger<traits>::
allocate(size_t  a_sz)
{
    auto   size  = sizeof(log_msg_type) + a_sz;
    char*  p     = m_allocator.allocate(size);
    char*  data  = p + sizeof(log_msg_type);
    auto   q     = reinterpret_cast<log_msg_type*>(p);
    new   (q) log_msg_type(data, a_sz);
    return q;
}

//-----------------------------------------------------------------------------
template<typename traits>
inline void basic_async_logger<traits>::
deallocate(log_msg_type* a_msg)
{
    auto   size  = sizeof(log_msg_type) + a_msg->size();
    a_msg->~log_msg_type();
    m_allocator.deallocate(reinterpret_cast<char*>(a_msg), size);
}

//-----------------------------------------------------------------------------
template<typename traits>
inline int basic_async_logger<traits>::
write_copy(const void* a_data, size_t a_sz)
{
    auto msg = allocate(a_sz);
    memcpy(msg->data(), a_data, a_sz);
    return write(msg);
}

//-----------------------------------------------------------------------------
template<typename traits>
inline int basic_async_logger<traits>::write(log_msg_type* msg)
{
    if (!msg)
        return -3;

    int n = msg->size();

    log_msg_type* last_head;

    do {
        last_head = m_head.load(std::memory_order_relaxed);
        msg->next(last_head);
    } while (!m_head.compare_exchange_weak(last_head, msg,
                                           std::memory_order_release,
                                           std::memory_order_relaxed));
    if (!last_head && m_notify_immediate)
        m_event.signal();

    ASYNC_TRACE("write - cur head: %p, prev head: %p\n",
                m_head, last_head);
    return n;
}

//-----------------------------------------------------------------------------
template<typename traits>
void basic_async_logger<traits>::
print_error(int a_errno, const char* a_what, int a_line) {
    if (on_error)
        on_error(a_errno, a_what);
    else
        std::cerr << "Error " << a_errno << " writing to file \""  << m_filename
                  << "\": "   << a_what << " [" << __FILE__ << ':' << a_line
                  << ']'      << std::endl;
}

} // namespace utxx
<|endoftext|>


<commit_before>/* Copyright 2016-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */

#include "watchman_system.h"
#include <condition_variable>
#include <thread>
#include "ChildProcess.h"
#include "watchman.h"
#include "watchman_perf.h"
#include "watchman_synchronized.h"

using watchman::ChildProcess;
using Options = ChildProcess::Options;
using Environment = ChildProcess::Environment;
using CaseSensitivity = watchman::CaseSensitivity;

namespace {
class PerfLogThread {
  watchman::Synchronized<json_ref, std::mutex> samples_;
  std::thread thread_;
  std::condition_variable cond_;

  void loop();

 public:
  PerfLogThread() : thread_([this]() { loop(); }) {}

  ~PerfLogThread() {
    cond_.notify_all();
    thread_.join();
  }

  void addSample(json_ref&& sample) {
    auto wlock = samples_.wlock();
    if (!*wlock) {
      *wlock = json_array();
    }
    json_array_append_new(*wlock, std::move(sample));
    cond_.notify_one();
  }
};

PerfLogThread& getPerfThread() {
  // Get the perf logging thread, starting it on the first call.
  // Meyer's singleton!
  static PerfLogThread perfThread;
  return perfThread;
}
}

watchman_perf_sample::watchman_perf_sample(const char* description)
    : description(description) {
  gettimeofday(&time_begin, nullptr);
#ifdef HAVE_SYS_RESOURCE_H
  getrusage(RUSAGE_SELF, &usage_begin);
#endif
}

bool watchman_perf_sample::finish() {
  gettimeofday(&time_end, nullptr);
  w_timeval_sub(time_end, time_begin, &duration);
#ifdef HAVE_SYS_RESOURCE_H
  getrusage(RUSAGE_SELF, &usage_end);

  // Compute the delta for the usage
  w_timeval_sub(usage_end.ru_utime, usage_begin.ru_utime, &usage.ru_utime);
  w_timeval_sub(usage_end.ru_stime, usage_begin.ru_stime, &usage.ru_stime);

#define DIFFU(n) usage.n = usage_end.n - usage_begin.n
  DIFFU(ru_maxrss);
  DIFFU(ru_ixrss);
  DIFFU(ru_idrss);
  DIFFU(ru_minflt);
  DIFFU(ru_majflt);
  DIFFU(ru_nswap);
  DIFFU(ru_inblock);
  DIFFU(ru_oublock);
  DIFFU(ru_msgsnd);
  DIFFU(ru_msgrcv);
  DIFFU(ru_nsignals);
  DIFFU(ru_nvcsw);
  DIFFU(ru_nivcsw);
#undef DIFFU
#endif

  if (!will_log) {
    if (wall_time_elapsed_thresh == 0) {
      auto thresh = cfg_get_json("perf_sampling_thresh");
      if (thresh) {
        if (json_is_number(thresh)) {
          wall_time_elapsed_thresh = json_number_value(thresh);
        } else {
          json_unpack(thresh, "{s:f}", description, &wall_time_elapsed_thresh);
        }
      }
    }

    if (wall_time_elapsed_thresh > 0 &&
        w_timeval_diff(time_begin, time_end) > wall_time_elapsed_thresh) {
      will_log = true;
    }
  }

  return will_log;
}

void watchman_perf_sample::add_meta(const char* key, json_ref&& val) {
  if (!meta_data) {
    meta_data = json_object();
  }
  meta_data.set(key, std::move(val));
}

void watchman_perf_sample::add_root_meta(
    const std::shared_ptr<w_root_t>& root) {
  // Note: if the root lock isn't held, we may read inaccurate numbers for
  // some of these properties.  We're ok with that, and don't want to force
  // the root lock to be re-acquired just for this.
  auto meta = json_object(
      {{"path", w_string_to_json(root->root_path)},
       {"recrawl_count", json_integer(root->recrawlInfo.rlock()->recrawlCount)},
       {"case_sensitive", json_boolean(root->case_sensitive == CaseSensitivity::CaseSensitive)}});

  // During recrawl, the view may be re-assigned.  Protect against
  // reading a nullptr.
  auto view = root->view();
  if (view) {
    auto position = view->getMostRecentRootNumberAndTickValue();
    meta.set({{"number", json_integer(position.rootNumber)},
              {"ticks", json_integer(position.ticks)},
              {"watcher", w_string_to_json(view->getName())}});
  }

  add_meta("root", std::move(meta));
}

void watchman_perf_sample::set_wall_time_thresh(double thresh) {
  wall_time_elapsed_thresh = thresh;
}

void watchman_perf_sample::force_log() {
  will_log = true;
}

void PerfLogThread::loop() {
  json_ref samples;
  json_ref perf_cmd;
  int64_t sample_batch;

  w_set_thread_name("perflog");

  auto stateDir = w_string_piece(watchman_state_file).dirName().asWString();

  perf_cmd = cfg_get_json("perf_logger_command");
  if (json_is_string(perf_cmd)) {
    perf_cmd = json_array({perf_cmd});
  }
  if (!json_is_array(perf_cmd)) {
    w_log(
        W_LOG_FATAL,
        "perf_logger_command must be either a string or an array of strings\n");
  }

  sample_batch = cfg_get_int("perf_logger_command_max_samples_per_call", 4);

  while (!w_is_stopping()) {
    {
      auto wlock = samples_.wlock();
      if (!*wlock) {
        cond_.wait(wlock.getUniqueLock());
      }

      samples = nullptr;
      std::swap(samples, *wlock);
    }

    if (samples) {
      while (json_array_size(samples) > 0) {
        int i = 0;
        auto cmd = json_array();

        json_array_extend(cmd, perf_cmd);

        while (i < sample_batch && json_array_size(samples) > 0) {
          char *stringy = json_dumps(json_array_get(samples, 0), 0);
          if (stringy) {
            json_array_append_new(
                cmd, typed_string_to_json(stringy, W_STRING_MIXED));
            free(stringy);
          }
          json_array_remove(samples, 0);
          i++;
        }

        ChildProcess::Options opts;
        opts.environment().set({{"WATCHMAN_STATE_DIR", stateDir},
                                {"WATCHMAN_SOCK", get_sock_name()}});
        opts.open(STDIN_FILENO, "/dev/null", O_RDONLY, 0666);
        opts.open(STDOUT_FILENO, "/dev/null", O_WRONLY, 0666);
        opts.open(STDERR_FILENO, "/dev/null", O_WRONLY, 0666);

        try {
          ChildProcess proc(cmd, std::move(opts));
          proc.wait();
        } catch (const std::exception& exc) {
          watchman::log(
              watchman::ERR, "failed to spawn perf logger: ", exc.what(), "\n");
        }
      }
    }
  }
}

void watchman_perf_sample::log() {
  char *dumped = NULL;

  if (!will_log) {
    return;
  }

  // Assemble a perf blob
  auto info = json_object(
      {{"description", typed_string_to_json(description)},
       {"meta", meta_data},
       {"pid", json_integer(getpid())},
       {"version", typed_string_to_json(PACKAGE_VERSION, W_STRING_UNICODE)}});

#ifdef WATCHMAN_BUILD_INFO
  info.set(
      "buildinfo", typed_string_to_json(WATCHMAN_BUILD_INFO, W_STRING_UNICODE));
#endif

#define ADDTV(name, tv) info.set(name, json_real(w_timeval_abs_seconds(tv)))
  ADDTV("elapsed_time", duration);
  ADDTV("start_time", time_begin);
#ifdef HAVE_SYS_RESOURCE_H
  ADDTV("user_time", usage.ru_utime);
  ADDTV("system_time", usage.ru_stime);
#define ADDU(n) info.set(#n, json_integer(usage.n))
  ADDU(ru_maxrss);
  ADDU(ru_ixrss);
  ADDU(ru_idrss);
  ADDU(ru_minflt);
  ADDU(ru_majflt);
  ADDU(ru_nswap);
  ADDU(ru_inblock);
  ADDU(ru_oublock);
  ADDU(ru_msgsnd);
  ADDU(ru_msgrcv);
  ADDU(ru_nsignals);
  ADDU(ru_nvcsw);
  ADDU(ru_nivcsw);
#endif // HAVE_SYS_RESOURCE_H
#undef ADDU
#undef ADDTV

  // Log to the log file
  dumped = json_dumps(info, 0);
  w_log(W_LOG_ERR, "PERF: %s\n", dumped);
  free(dumped);

  if (!cfg_get_json("perf_logger_command")) {
    return;
  }

  // Send this to our logging thread for async processing
  auto& perfThread = getPerfThread();
  perfThread.addSample(std::move(info));
}

/* vim:ts=2:sw=2:et:
 */
<commit_msg>suppress rusage deltas from watchman perf logs<commit_after>/* Copyright 2016-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */

#include "watchman_system.h"
#include <condition_variable>
#include <thread>
#include "ChildProcess.h"
#include "watchman.h"
#include "watchman_perf.h"
#include "watchman_synchronized.h"

using watchman::ChildProcess;
using Options = ChildProcess::Options;
using Environment = ChildProcess::Environment;
using CaseSensitivity = watchman::CaseSensitivity;

namespace {
class PerfLogThread {
  watchman::Synchronized<json_ref, std::mutex> samples_;
  std::thread thread_;
  std::condition_variable cond_;

  void loop();

 public:
  PerfLogThread() : thread_([this]() { loop(); }) {}

  ~PerfLogThread() {
    cond_.notify_all();
    thread_.join();
  }

  void addSample(json_ref&& sample) {
    auto wlock = samples_.wlock();
    if (!*wlock) {
      *wlock = json_array();
    }
    json_array_append_new(*wlock, std::move(sample));
    cond_.notify_one();
  }
};

PerfLogThread& getPerfThread() {
  // Get the perf logging thread, starting it on the first call.
  // Meyer's singleton!
  static PerfLogThread perfThread;
  return perfThread;
}
}

watchman_perf_sample::watchman_perf_sample(const char* description)
    : description(description) {
  gettimeofday(&time_begin, nullptr);
#ifdef HAVE_SYS_RESOURCE_H
  getrusage(RUSAGE_SELF, &usage_begin);
#endif
}

bool watchman_perf_sample::finish() {
  gettimeofday(&time_end, nullptr);
  w_timeval_sub(time_end, time_begin, &duration);
#ifdef HAVE_SYS_RESOURCE_H
  getrusage(RUSAGE_SELF, &usage_end);

  // Compute the delta for the usage
  w_timeval_sub(usage_end.ru_utime, usage_begin.ru_utime, &usage.ru_utime);
  w_timeval_sub(usage_end.ru_stime, usage_begin.ru_stime, &usage.ru_stime);

#define DIFFU(n) usage.n = usage_end.n - usage_begin.n
  DIFFU(ru_maxrss);
  DIFFU(ru_ixrss);
  DIFFU(ru_idrss);
  DIFFU(ru_minflt);
  DIFFU(ru_majflt);
  DIFFU(ru_nswap);
  DIFFU(ru_inblock);
  DIFFU(ru_oublock);
  DIFFU(ru_msgsnd);
  DIFFU(ru_msgrcv);
  DIFFU(ru_nsignals);
  DIFFU(ru_nvcsw);
  DIFFU(ru_nivcsw);
#undef DIFFU
#endif

  if (!will_log) {
    if (wall_time_elapsed_thresh == 0) {
      auto thresh = cfg_get_json("perf_sampling_thresh");
      if (thresh) {
        if (json_is_number(thresh)) {
          wall_time_elapsed_thresh = json_number_value(thresh);
        } else {
          json_unpack(thresh, "{s:f}", description, &wall_time_elapsed_thresh);
        }
      }
    }

    if (wall_time_elapsed_thresh > 0 &&
        w_timeval_diff(time_begin, time_end) > wall_time_elapsed_thresh) {
      will_log = true;
    }
  }

  return will_log;
}

void watchman_perf_sample::add_meta(const char* key, json_ref&& val) {
  if (!meta_data) {
    meta_data = json_object();
  }
  meta_data.set(key, std::move(val));
}

void watchman_perf_sample::add_root_meta(
    const std::shared_ptr<w_root_t>& root) {
  // Note: if the root lock isn't held, we may read inaccurate numbers for
  // some of these properties.  We're ok with that, and don't want to force
  // the root lock to be re-acquired just for this.
  auto meta = json_object(
      {{"path", w_string_to_json(root->root_path)},
       {"recrawl_count", json_integer(root->recrawlInfo.rlock()->recrawlCount)},
       {"case_sensitive", json_boolean(root->case_sensitive == CaseSensitivity::CaseSensitive)}});

  // During recrawl, the view may be re-assigned.  Protect against
  // reading a nullptr.
  auto view = root->view();
  if (view) {
    auto position = view->getMostRecentRootNumberAndTickValue();
    meta.set({{"number", json_integer(position.rootNumber)},
              {"ticks", json_integer(position.ticks)},
              {"watcher", w_string_to_json(view->getName())}});
  }

  add_meta("root", std::move(meta));
}

void watchman_perf_sample::set_wall_time_thresh(double thresh) {
  wall_time_elapsed_thresh = thresh;
}

void watchman_perf_sample::force_log() {
  will_log = true;
}

void PerfLogThread::loop() {
  json_ref samples;
  json_ref perf_cmd;
  int64_t sample_batch;

  w_set_thread_name("perflog");

  auto stateDir = w_string_piece(watchman_state_file).dirName().asWString();

  perf_cmd = cfg_get_json("perf_logger_command");
  if (json_is_string(perf_cmd)) {
    perf_cmd = json_array({perf_cmd});
  }
  if (!json_is_array(perf_cmd)) {
    w_log(
        W_LOG_FATAL,
        "perf_logger_command must be either a string or an array of strings\n");
  }

  sample_batch = cfg_get_int("perf_logger_command_max_samples_per_call", 4);

  while (!w_is_stopping()) {
    {
      auto wlock = samples_.wlock();
      if (!*wlock) {
        cond_.wait(wlock.getUniqueLock());
      }

      samples = nullptr;
      std::swap(samples, *wlock);
    }

    if (samples) {
      while (json_array_size(samples) > 0) {
        int i = 0;
        auto cmd = json_array();

        json_array_extend(cmd, perf_cmd);

        while (i < sample_batch && json_array_size(samples) > 0) {
          char *stringy = json_dumps(json_array_get(samples, 0), 0);
          if (stringy) {
            json_array_append_new(
                cmd, typed_string_to_json(stringy, W_STRING_MIXED));
            free(stringy);
          }
          json_array_remove(samples, 0);
          i++;
        }

        ChildProcess::Options opts;
        opts.environment().set({{"WATCHMAN_STATE_DIR", stateDir},
                                {"WATCHMAN_SOCK", get_sock_name()}});
        opts.open(STDIN_FILENO, "/dev/null", O_RDONLY, 0666);
        opts.open(STDOUT_FILENO, "/dev/null", O_WRONLY, 0666);
        opts.open(STDERR_FILENO, "/dev/null", O_WRONLY, 0666);

        try {
          ChildProcess proc(cmd, std::move(opts));
          proc.wait();
        } catch (const std::exception& exc) {
          watchman::log(
              watchman::ERR, "failed to spawn perf logger: ", exc.what(), "\n");
        }
      }
    }
  }
}

void watchman_perf_sample::log() {
  char *dumped = NULL;

  if (!will_log) {
    return;
  }

  // Assemble a perf blob
  auto info = json_object(
      {{"description", typed_string_to_json(description)},
       {"meta", meta_data},
       {"pid", json_integer(getpid())},
       {"version", typed_string_to_json(PACKAGE_VERSION, W_STRING_UNICODE)}});

#ifdef WATCHMAN_BUILD_INFO
  info.set(
      "buildinfo", typed_string_to_json(WATCHMAN_BUILD_INFO, W_STRING_UNICODE));
#endif

#define ADDTV(name, tv) info.set(name, json_real(w_timeval_abs_seconds(tv)))
  ADDTV("elapsed_time", duration);
  ADDTV("start_time", time_begin);
#ifdef HAVE_SYS_RESOURCE_H
  ADDTV("user_time", usage.ru_utime);
  ADDTV("system_time", usage.ru_stime);
#endif // HAVE_SYS_RESOURCE_H
#undef ADDTV

  // Log to the log file
  dumped = json_dumps(info, 0);
  w_log(W_LOG_ERR, "PERF: %s\n", dumped);
  free(dumped);

  if (!cfg_get_json("perf_logger_command")) {
    return;
  }

  // Send this to our logging thread for async processing
  auto& perfThread = getPerfThread();
  perfThread.addSample(std::move(info));
}

/* vim:ts=2:sw=2:et:
 */
<|endoftext|>


<commit_before>#include <libdariadb/status.h>
#include <libdariadb/utils/exception.h>

namespace dariadb {
// TODO make normal error messages.
std::string to_string(const APPEND_ERROR st) {
  switch (st) {
  case APPEND_ERROR::OK:
    return "OK";
    break;
  case APPEND_ERROR::bad_alloc:
    return "bad_alloc";
    break;
  case APPEND_ERROR::bad_shard:
    return "bad_shard";
    break;
  case APPEND_ERROR::wal_file_limit:
    return "wal_file_limit";
    break;
  }
  THROW_EXCEPTION("std::string to_string(const APPEND_ERROR st) - ", (int)st);
}
}<commit_msg>status error messages refactorin.<commit_after>#include <libdariadb/status.h>
#include <libdariadb/utils/exception.h>

namespace dariadb {
std::string to_string(const APPEND_ERROR st) {
  switch (st) {
  case APPEND_ERROR::OK:
    return "OK";
    break;
  case APPEND_ERROR::bad_alloc:
    return "memory allocation error";
    break;
  case APPEND_ERROR::bad_shard:
    return "shard not found";
    break;
  case APPEND_ERROR::wal_file_limit:
    return "wall file is full";
    break;
  }
  THROW_EXCEPTION("std::string to_string(const APPEND_ERROR st) - ", (int)st);
}
}<|endoftext|>


<commit_before>//=====================================================================//
/*! @file
    @brief  RX24T ファースト・サンプル @n
			・P00(4) ピンに赤色LED（VF:1.9V）を吸い込みで接続する
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "RX24T/system.hpp"
#include "RX600/port.hpp"

namespace {

	void wait_delay_(uint32_t n)
	{
		// とりあえず無駄ループ
		for(uint32_t i = 0; i < n; ++i) {
			asm("nop");
		}
	}

}

int main(int argc, char** argv);

int main(int argc, char** argv)
{
	device::SYSTEM::PRCR = 0xA50B;	// クロック、低消費電力、関係書き込み許可

	device::SYSTEM::MEMWAIT = 0b10; // 80MHz 動作 wait 設定

	while(device::SYSTEM::OPCCR.OPCMTSF() != 0) asm("nop");
	device::SYSTEM::OPCCR = 0;  // 高速モード選択
	while(device::SYSTEM::OPCCR.OPCMTSF() != 0) asm("nop");

	// clock osc 10MHz
	device::SYSTEM::MOSCWTCR = 9;	// 4ms wait
	// メインクロック・ドライブ能力設定、内部発信
	device::SYSTEM::MOFCR = device::SYSTEM::MOFCR.MODRV21.b(1);
	device::SYSTEM::MOSCCR.MOSTP = 0;  // メインクロック発振器動作
	while(device::SYSTEM::OSCOVFSR.MOOVF() == 0) asm("nop");

	device::SYSTEM::PLLCR.STC = 0b001111;		// PLL input: 1, PLL 8 倍(80MHz)
	device::SYSTEM::PLLCR2.PLLEN = 0;			// PLL 動作
	while(device::SYSTEM::OSCOVFSR.PLOVF() == 0) asm("nop");

	device::SYSTEM::SCKCR = device::SYSTEM::SCKCR.FCK.b(2)		// 1/4 (80/4=20)
						  | device::SYSTEM::SCKCR.ICK.b(0)		// 1/1 (80/1=80)
						  | device::SYSTEM::SCKCR.PCKA.b(0)		// 1/1 (80/1=80)
						  | device::SYSTEM::SCKCR.PCKB.b(1)		// 1/2 (80/2=40)
						  | device::SYSTEM::SCKCR.PCKD.b(1);	// 1/2 (120/2=60)
	device::SYSTEM::SCKCR3.CKSEL = 0b100;	///< PLL 選択

	uint32_t wait = 1000000;
	device::PORT0::PDR.B0 = 1; // output
	while(1) {
		wait_delay_(wait);
		device::PORT0::PODR.B0 = 0;
		wait_delay_(wait);
		device::PORT0::PODR.B0 = 1;
	}
}
<commit_msg>update comment<commit_after>//=====================================================================//
/*! @file
    @brief  RX24T ファースト・サンプル @n
			・P00(4) ピンに赤色LED（VF:1.9V）を吸い込みで接続する @n
			Copyright 2016,2017 Kunihito Hiramatsu
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "RX24T/system.hpp"
#include "RX600/port.hpp"

namespace {

	void wait_delay_(uint32_t n)
	{
		// とりあえず無駄ループ
		for(uint32_t i = 0; i < n; ++i) {
			asm("nop");
		}
	}

}

int main(int argc, char** argv);

int main(int argc, char** argv)
{
	device::SYSTEM::PRCR = 0xA50B;	// クロック、低消費電力、関係書き込み許可

	device::SYSTEM::MEMWAIT = 0b10; // 80MHz 動作 wait 設定

	while(device::SYSTEM::OPCCR.OPCMTSF() != 0) asm("nop");
	device::SYSTEM::OPCCR = 0;  // 高速モード選択
	while(device::SYSTEM::OPCCR.OPCMTSF() != 0) asm("nop");

	// clock osc 10MHz
	device::SYSTEM::MOSCWTCR = 9;	// 4ms wait
	// メインクロック・ドライブ能力設定、内部発信
	device::SYSTEM::MOFCR = device::SYSTEM::MOFCR.MODRV21.b(1);
	device::SYSTEM::MOSCCR.MOSTP = 0;  // メインクロック発振器動作
	while(device::SYSTEM::OSCOVFSR.MOOVF() == 0) asm("nop");

	device::SYSTEM::PLLCR.STC = 0b001111;		// PLL input: 1, PLL 8 倍(80MHz)
	device::SYSTEM::PLLCR2.PLLEN = 0;			// PLL 動作
	while(device::SYSTEM::OSCOVFSR.PLOVF() == 0) asm("nop");

	device::SYSTEM::SCKCR = device::SYSTEM::SCKCR.FCK.b(2)		// 1/4 (80/4=20)
						  | device::SYSTEM::SCKCR.ICK.b(0)		// 1/1 (80/1=80)
						  | device::SYSTEM::SCKCR.PCKA.b(0)		// 1/1 (80/1=80)
						  | device::SYSTEM::SCKCR.PCKB.b(1)		// 1/2 (80/2=40)
						  | device::SYSTEM::SCKCR.PCKD.b(1);	// 1/2 (120/2=60)
	device::SYSTEM::SCKCR3.CKSEL = 0b100;	///< PLL 選択

	uint32_t wait = 1000000;
	device::PORT0::PDR.B0 = 1; // output
	while(1) {
		wait_delay_(wait);
		device::PORT0::PODR.B0 = 0;
		wait_delay_(wait);
		device::PORT0::PODR.B0 = 1;
	}
}
<|endoftext|>