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

#include <istream>
#include <memory>

#include "compiler/api/compiler_api.h"
#include "diagnostics/xquery_diagnostics.h"
#include "diagnostics/dict.h"

#include "system/globalenv.h"
#include "system/properties.h"

#include "context/static_context.h"

#include "functions/function.h"
#include "functions/udf.h"

#include "types/typeops.h"
#include "types/root_typemanager.h"
#include "types/typemanagerimpl.h"

#include "context/uri_resolver.h"
#include "compiler/api/compiler_api_impl.h"
#include "compiler/api/compilercb.h"

#include "compiler/parser/xquery_driver.h"
#include "compiler/parser/jsoniq_driver.h"
#include "compiler/parsetree/parsenodes.h"
#include "compiler/parsetree/parsenodes.h"
#include "compiler/parsetree/parsenode_print_xml_visitor.h"
#include "compiler/parsetree/parsenode_print_xqdoc_visitor.h"

#include "compiler/expression/expr_base.h"

#include "compiler/translator/translator.h"

#include "compiler/rewriter/framework/rewriter_context.h"
#include "compiler/rewriter/framework/rewriter.h"
#include "compiler/rewriter/tools/udf_graph.h"

#include "compiler/codegen/plan_visitor.h"
#ifdef ZORBA_XQUERYX
#include "compiler/xqueryx/xqueryx_to_xquery.h"
#endif

#include "runtime/base/plan_iterator.h"
#include "util/xml_util.h"
#include "zorbatypes/URI.h"

#include "api/auditimpl.h"
#include <zorba/util/timer.h>

namespace zorba
{

static void print_ast_tree(const parsenode *n, const std::string& name)
{
  std::cout << "AST for " << name << "\n";
  print_parsetree_xml(std::cout, n);
  std::cout << std::endl;
}

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

********************************************************************************/
XQueryCompiler::XQueryCompiler(CompilerCB* aCompilerCB)
  :
  theCompilerCB(aCompilerCB)
{
}

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

********************************************************************************/
XQueryCompiler::~XQueryCompiler()
{
}

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

********************************************************************************/
void XQueryCompiler::parseOnly(
    std::istream& aXQuery,
    const zstring& aFileName)
{
  parsenode_t pn = parse(aXQuery, aFileName);
}

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

********************************************************************************/
void XQueryCompiler::xqdoc(
    std::istream&         aXQuery,
    const zstring&        aFileName,
    store::Item_t&        aResult,
    const store::Item_t&  aDateTime,
    uint32_t              aOptions)
{
  parsenode_t lAST = parse(aXQuery, aFileName);

  print_parsetree_xqdoc(aResult, lAST.getp(),
                        aFileName.c_str(), aDateTime, aOptions);
}

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

********************************************************************************/
bool XQueryCompiler::getLanguageMode(std::stringstream& s) const
{
  const size_t lPeekSize = 7;
  char lPeek[lPeekSize];
  s.get(lPeek, lPeekSize, ' ');
  s.clear();
  s.seekg(0, s.beg);

  bool lXQueryMode;

  if (strncmp(lPeek, "jsoniq", lPeekSize - 1) == 0)
  {
    lXQueryMode = false;
  }
  else if (strncmp(lPeek, "xquery", lPeekSize - 1) == 0)
  {
    lXQueryMode = true;
  }
  else
  {
    if (theCompilerCB->theRootSctx) // could be null in eval
    {
      StaticContextConsts::language_kind_t lKind
        = theCompilerCB->theRootSctx->language_kind();
      lXQueryMode = (lKind != StaticContextConsts::language_kind_jsoniq);
    }
    else
    {
      lXQueryMode = true;
    }
  }
  return lXQueryMode;
}

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

********************************************************************************/
parsenode_t XQueryCompiler::parse(std::istream& aXQuery, const zstring& aFileName)
{
  // TODO: move these out
  if (Properties::instance()->printAst())
  {
    theCompilerCB->theConfig.parse_cb = print_ast_tree;
  }

  std::stringstream xquery_stream;

#ifdef ZORBA_XQUERYX
  char* converted_xquery_str = NULL;
  std::string   xquery_str;
  bool is_xqueryx = false;

  {
    char strtemp[1000];
    do
    {
      strtemp[0] = 0;
      aXQuery.read(strtemp, sizeof(strtemp)-1);
      strtemp[aXQuery.gcount()] = 0;
      xquery_str += strtemp;
    }
    while(aXQuery.gcount() == (sizeof(strtemp)-1));
  }

  XQueryXConvertor* xqxconvertor = GENV.getXQueryXConvertor();

  if(xqxconvertor->isXQueryX((char*)xquery_str.c_str()))
  {
    // identify XQueryX by content:
    // root tag =
    // "<prefix:module ... xmlns:prefix="http://www.w3.org/2005/XQueryX" ... > "

    is_xqueryx = true;
    //translate from xqueryx to xquery using XSLT
    //read all input stream into std::string

    converted_xquery_str = xqxconvertor->XQueryX2XQuery(xquery_str.c_str());

#ifndef NDEBUG
    printf ("\n\n%s", converted_xquery_str);  // debug
#endif
    xquery_stream << converted_xquery_str;
  }
  else
  {
    xquery_stream << xquery_str;
  }
#else // ZORBA_XQUERYX

  xquery_stream << aXQuery.rdbuf();
#endif

  theCompilerCB->setPhase(CompilerCB::PARSING);

  parsenode_t node;
  theCompilerCB->setPhase(CompilerCB::NONE);

  bool lXQueryMode = getLanguageMode(xquery_stream);

  if (!lXQueryMode)
  {
    jsoniq_driver lDriver(&*theCompilerCB);
    lDriver.parse_stream(xquery_stream, aFileName);
    node =  lDriver.get_expr();
  }
  else
  {
    xquery_driver lDriver(&*theCompilerCB);
    lDriver.parse_stream(xquery_stream, aFileName);
    node =  lDriver.get_expr();
  }

#ifdef ZORBA_XQUERYX
  if (is_xqueryx)
  {
    xqxconvertor->freeResult(converted_xquery_str);
  }
#endif

  if (typeid (*node) == typeid (ParseErrorNode))
  {
    ParseErrorNode* pen = static_cast<ParseErrorNode *>(&*node);
    throw XQUERY_EXCEPTION_VAR(pen->err,
    ERROR_PARAMS(pen->msg), ERROR_LOC(pen->get_location()));
  }

  return node;
}

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

********************************************************************************/
PlanIter_t XQueryCompiler::compile(
    std::istream& aXQuery,
    const zstring& aFileName,
    ulong& nextDynamicVarId)
{
  audit::Event* ae = theCompilerCB->theRootSctx->get_audit_event();
  zorba::audit::ScopedRecord sar(ae);

  const char* lFileName = aFileName.c_str();

  audit::ScopedAuditor<const char*>
  filenameAudit(sar, zorba::audit::XQUERY_COMPILATION_FILENAME, lFileName);

  parsenode_t lAST;

  {
    time::Timer lTimer;

    audit::DurationAuditor
    durationAudit(sar, audit::XQUERY_COMPILATION_PARSE_DURATION, lTimer);

    lAST = parse(aXQuery, aFileName);

    if (theCompilerCB->theConfig.lib_module &&
        dynamic_cast<LibraryModule*>(lAST.getp()) != NULL)
    {
      lAST = createMainModule(lAST, aXQuery, aFileName);
    }
  }

  return compile(lAST, true, nextDynamicVarId, sar);
}

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

********************************************************************************/
PlanIter_t XQueryCompiler::compile(
    const parsenode_t& ast,
    bool applyPUL,
    ulong& nextDynamicVarId,
    audit::ScopedRecord& auditRecord)
{
  expr* rootExpr;

  rootExpr = translate(ast, auditRecord);

  rootExpr = optimize(rootExpr, auditRecord);

  return codegen(rootExpr, nextDynamicVarId, auditRecord);
}

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

********************************************************************************/
expr* XQueryCompiler::translate(
    parsenode_t parsenode,
    audit::ScopedRecord& auditRecord)
{
  time::Timer timer;

  audit::DurationAuditor
  durationAudit(auditRecord,
                audit::XQUERY_COMPILATION_TRANSLATION_DURATION,
                timer);

  theCompilerCB->setPhase(CompilerCB::TRANSLATION);

  expr* rootExpr = zorba::translate(*parsenode, theCompilerCB);

  theCompilerCB->setPhase(CompilerCB::NONE);

  if (rootExpr == NULL)
  {
    // TODO: can this happen?
    throw ZORBA_EXCEPTION(zerr::ZAPI0002_XQUERY_COMPILATION_FAILED);
  }

  return rootExpr;
}

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

********************************************************************************/
expr* XQueryCompiler::optimize(expr* rootExpr, audit::ScopedRecord& auditRecord)
{
  time::Timer timer;

  audit::DurationAuditor
  durationAudit(auditRecord,
                audit::XQUERY_COMPILATION_OPTIMIZATION_DURATION,
                timer);

  theCompilerCB->setPhase(CompilerCB::OPTIMIZATION);

  // Build the call-graph among the udfs that are actually used in the query
  // program.
  UDFGraph udfGraph(rootExpr);

  // By default all UDFs are marked as deterministic. Now, we find which udfs
  // are actually non-deterministic and mark them as such. This has to be done
  // before optimization.
  udfGraph.inferDeterminism();

  if (theCompilerCB->theConfig.opt_level <= CompilerCB::config::O0)
  {
    theCompilerCB->setPhase(CompilerCB::NONE);
    return rootExpr;
  }

  // Optimize the udfs.
  udfGraph.optimizeUDFs(theCompilerCB);

  // Optimize the main expr (i.e., the top expr of the main module).
  RewriterContext rCtx(theCompilerCB,
                       rootExpr,
                       NULL,
                       "Optimizing main program",
                       rootExpr->get_sctx()->is_in_ordered_mode());

  GENV_COMPILERSUBSYS.getDefaultOptimizingRewriter()->rewrite(rCtx);

  rootExpr = rCtx.getRoot();

  if (theCompilerCB->theConfig.optimize_cb != NULL)
    theCompilerCB->theConfig.optimize_cb(rootExpr, "main query");

  theCompilerCB->setPhase(CompilerCB::NONE);

  return rootExpr;
}

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

********************************************************************************/
PlanIter_t XQueryCompiler::codegen(
    expr* rootExpr,
    ulong& nextDynamicVarId,
    audit::ScopedRecord& auditRecord)
{
  time::Timer timer;

  audit::DurationAuditor
  durationAudit(auditRecord,
                audit::XQUERY_COMPILATION_CODEGENERATION_DURATION,
                timer);

  theCompilerCB->setPhase(CompilerCB::CODEGEN);

  PlanIter_t plan = zorba::codegen("main query",
                                   rootExpr,
                                   theCompilerCB,
                                   nextDynamicVarId);

  theCompilerCB->setPhase(CompilerCB::NONE);

  return plan;
}

/******************************************************************************
  This is a small helper class used when the user wants to compile a library
  module. The ONLY place it is used (and should be used) is in the
  XQueryCompiler::createMainModule method below.
  QQQ When we have the ability to compile a library module indepedently, this
  rather hacky class can go away. At that time, we can also eliminate the
  "stream URL" hack in StreamResource - this is the only place in the code where
  we use the two-arg StreamResource constructor.
*******************************************************************************/
class FakeLibraryModuleURLResolver : public internal::URLResolver
{
public:
  FakeLibraryModuleURLResolver(
      zstring const& aLibraryModuleFilename,
      std::istream& aStream)
    :
    theLibraryModuleFilename(aLibraryModuleFilename),
    theStream(aStream)
  {}

  virtual ~FakeLibraryModuleURLResolver()
  {}

  virtual internal::Resource* resolveURL(
      const zstring& aUrl,
      const internal::EntityData* aEntityData)
  {
    // Since we know this URL resolver will only be used when compiling the
    // stub query, and the only URI that query will contain is the one we're
    // interested in, then we don't actually need to check the URL at all -
    // just return the stream. This is a good thing, because due to URI
    // mapping, aUrl will probably be different than the one we're expecting.
    // If we returned NULL here, Zorba's built-in URL Resolvers would attempt
    // to load the resource from somewhere else, and that may result in the
    // wrong information being compiled. See bug 992304.
    assert (theStream.good());
    // Pass a nullptr StreamReleaser; memory ownership of the istream remains
    // with the caller of this method.
    // QQQ We can remove this third argument when we can compile modules individually
    return new internal::StreamResource(&theStream, nullptr, theLibraryModuleFilename);
  }

private:
  zstring theLibraryModuleFilename;
  std::istream& theStream;
};

/******************************************************************************
  Create a dummy main module to wrap a library module.
******************************************************************************/
parsenode_t XQueryCompiler::createMainModule(
    parsenode_t aLibraryModule,
    std::istream& aXQuery,
    const zstring& aFileName)
{
  //get the namespace from the LibraryModule
  LibraryModule* mod_ast = dynamic_cast<LibraryModule *>(&*aLibraryModule);
  if (!mod_ast)
    throw ZORBA_EXCEPTION(zerr::ZAPI0002_XQUERY_COMPILATION_FAILED,
    ERROR_PARAMS(ZED(BadLibraryModule)));

  const zstring& lib_namespace = mod_ast->get_decl()->get_target_namespace();

  URI lURI(lib_namespace);
  if(!lURI.is_absolute())
  {
    throw XQUERY_EXCEPTION(err::XQST0046,
    ERROR_PARAMS(lURI.toString(), ZED(MustBeAbsoluteURI)),
      ERROR_LOC(mod_ast->get_decl()->get_location()));
  }

  // Set up the original query stream as the result of resolving the
  // library module's URI
  aXQuery.clear();
  aXQuery.seekg(0);

  FakeLibraryModuleURLResolver* aFakeResolver =
    new FakeLibraryModuleURLResolver(aFileName, aXQuery);

  theCompilerCB->theRootSctx->add_url_resolver(aFakeResolver);

  // create a dummy main module and parse it
  std::stringstream lDocStream;
  zstring tmp;
  zorba::xml::escape(lib_namespace, &tmp);
  lDocStream << "import module namespace m = '" << tmp << "'; 1";
  return parse(lDocStream, aFileName);
}

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

********************************************************************************/
XQueryCompilerSubsystem::XQueryCompilerSubsystem()
{
}

XQueryCompilerSubsystem::~XQueryCompilerSubsystem()
{
}

std::auto_ptr<XQueryCompilerSubsystem> XQueryCompilerSubsystem::create()
{
  return std::auto_ptr<XQueryCompilerSubsystem>(new XQueryCompilerSubsystemImpl());
}

}
/* vim:set et sw=2 ts=2: */
<commit_msg>removed confusing optimization<commit_after>/*
 * Copyright 2006-2012 The FLWOR Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "stdafx.h"

#include <istream>
#include <memory>

#include "compiler/api/compiler_api.h"
#include "diagnostics/xquery_diagnostics.h"
#include "diagnostics/dict.h"

#include "system/globalenv.h"
#include "system/properties.h"

#include "context/static_context.h"

#include "functions/function.h"
#include "functions/udf.h"

#include "types/typeops.h"
#include "types/root_typemanager.h"
#include "types/typemanagerimpl.h"

#include "context/uri_resolver.h"
#include "compiler/api/compiler_api_impl.h"
#include "compiler/api/compilercb.h"

#include "compiler/parser/xquery_driver.h"
#include "compiler/parser/jsoniq_driver.h"
#include "compiler/parsetree/parsenodes.h"
#include "compiler/parsetree/parsenodes.h"
#include "compiler/parsetree/parsenode_print_xml_visitor.h"
#include "compiler/parsetree/parsenode_print_xqdoc_visitor.h"

#include "compiler/expression/expr_base.h"

#include "compiler/translator/translator.h"

#include "compiler/rewriter/framework/rewriter_context.h"
#include "compiler/rewriter/framework/rewriter.h"
#include "compiler/rewriter/tools/udf_graph.h"

#include "compiler/codegen/plan_visitor.h"
#ifdef ZORBA_XQUERYX
#include "compiler/xqueryx/xqueryx_to_xquery.h"
#endif

#include "runtime/base/plan_iterator.h"
#include "util/xml_util.h"
#include "zorbatypes/URI.h"

#include "api/auditimpl.h"
#include <zorba/util/timer.h>

namespace zorba
{

static void print_ast_tree(const parsenode *n, const std::string& name)
{
  std::cout << "AST for " << name << "\n";
  print_parsetree_xml(std::cout, n);
  std::cout << std::endl;
}

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

********************************************************************************/
XQueryCompiler::XQueryCompiler(CompilerCB* aCompilerCB)
  :
  theCompilerCB(aCompilerCB)
{
}

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

********************************************************************************/
XQueryCompiler::~XQueryCompiler()
{
}

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

********************************************************************************/
void XQueryCompiler::parseOnly(
    std::istream& aXQuery,
    const zstring& aFileName)
{
  parsenode_t pn = parse(aXQuery, aFileName);
}

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

********************************************************************************/
void XQueryCompiler::xqdoc(
    std::istream&         aXQuery,
    const zstring&        aFileName,
    store::Item_t&        aResult,
    const store::Item_t&  aDateTime,
    uint32_t              aOptions)
{
  parsenode_t lAST = parse(aXQuery, aFileName);

  print_parsetree_xqdoc(aResult, lAST.getp(),
                        aFileName.c_str(), aDateTime, aOptions);
}

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

********************************************************************************/
bool XQueryCompiler::getLanguageMode(std::stringstream& s) const
{
  const size_t lPeekSize = 7;
  char lPeek[lPeekSize];
  s.get(lPeek, lPeekSize);
  s.clear();
  s.seekg(0, s.beg);

  bool lXQueryMode;

  if (strncmp(lPeek, "jsoniq", lPeekSize - 1) == 0)
  {
    lXQueryMode = false;
  }
  else if (strncmp(lPeek, "xquery", lPeekSize - 1) == 0)
  {
    lXQueryMode = true;
  }
  else
  {
    if (theCompilerCB->theRootSctx) // could be null in eval
    {
      StaticContextConsts::language_kind_t lKind
        = theCompilerCB->theRootSctx->language_kind();
      lXQueryMode = (lKind != StaticContextConsts::language_kind_jsoniq);
    }
    else
    {
      lXQueryMode = true;
    }
  }
  return lXQueryMode;
}

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

********************************************************************************/
parsenode_t XQueryCompiler::parse(std::istream& aXQuery, const zstring& aFileName)
{
  // TODO: move these out
  if (Properties::instance()->printAst())
  {
    theCompilerCB->theConfig.parse_cb = print_ast_tree;
  }

  std::stringstream xquery_stream;

#ifdef ZORBA_XQUERYX
  char* converted_xquery_str = NULL;
  std::string   xquery_str;
  bool is_xqueryx = false;

  {
    char strtemp[1000];
    do
    {
      strtemp[0] = 0;
      aXQuery.read(strtemp, sizeof(strtemp)-1);
      strtemp[aXQuery.gcount()] = 0;
      xquery_str += strtemp;
    }
    while(aXQuery.gcount() == (sizeof(strtemp)-1));
  }

  XQueryXConvertor* xqxconvertor = GENV.getXQueryXConvertor();

  if(xqxconvertor->isXQueryX((char*)xquery_str.c_str()))
  {
    // identify XQueryX by content:
    // root tag =
    // "<prefix:module ... xmlns:prefix="http://www.w3.org/2005/XQueryX" ... > "

    is_xqueryx = true;
    //translate from xqueryx to xquery using XSLT
    //read all input stream into std::string

    converted_xquery_str = xqxconvertor->XQueryX2XQuery(xquery_str.c_str());

#ifndef NDEBUG
    printf ("\n\n%s", converted_xquery_str);  // debug
#endif
    xquery_stream << converted_xquery_str;
  }
  else
  {
    xquery_stream << xquery_str;
  }
#else // ZORBA_XQUERYX

  xquery_stream << aXQuery.rdbuf();
#endif

  theCompilerCB->setPhase(CompilerCB::PARSING);

  parsenode_t node;
  theCompilerCB->setPhase(CompilerCB::NONE);

  bool lXQueryMode = getLanguageMode(xquery_stream);

  if (!lXQueryMode)
  {
    jsoniq_driver lDriver(&*theCompilerCB);
    lDriver.parse_stream(xquery_stream, aFileName);
    node =  lDriver.get_expr();
  }
  else
  {
    xquery_driver lDriver(&*theCompilerCB);
    lDriver.parse_stream(xquery_stream, aFileName);
    node =  lDriver.get_expr();
  }

#ifdef ZORBA_XQUERYX
  if (is_xqueryx)
  {
    xqxconvertor->freeResult(converted_xquery_str);
  }
#endif

  if (typeid (*node) == typeid (ParseErrorNode))
  {
    ParseErrorNode* pen = static_cast<ParseErrorNode *>(&*node);
    throw XQUERY_EXCEPTION_VAR(pen->err,
    ERROR_PARAMS(pen->msg), ERROR_LOC(pen->get_location()));
  }

  return node;
}

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

********************************************************************************/
PlanIter_t XQueryCompiler::compile(
    std::istream& aXQuery,
    const zstring& aFileName,
    ulong& nextDynamicVarId)
{
  audit::Event* ae = theCompilerCB->theRootSctx->get_audit_event();
  zorba::audit::ScopedRecord sar(ae);

  const char* lFileName = aFileName.c_str();

  audit::ScopedAuditor<const char*>
  filenameAudit(sar, zorba::audit::XQUERY_COMPILATION_FILENAME, lFileName);

  parsenode_t lAST;

  {
    time::Timer lTimer;

    audit::DurationAuditor
    durationAudit(sar, audit::XQUERY_COMPILATION_PARSE_DURATION, lTimer);

    lAST = parse(aXQuery, aFileName);

    if (theCompilerCB->theConfig.lib_module &&
        dynamic_cast<LibraryModule*>(lAST.getp()) != NULL)
    {
      lAST = createMainModule(lAST, aXQuery, aFileName);
    }
  }

  return compile(lAST, true, nextDynamicVarId, sar);
}

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

********************************************************************************/
PlanIter_t XQueryCompiler::compile(
    const parsenode_t& ast,
    bool applyPUL,
    ulong& nextDynamicVarId,
    audit::ScopedRecord& auditRecord)
{
  expr* rootExpr;

  rootExpr = translate(ast, auditRecord);

  rootExpr = optimize(rootExpr, auditRecord);

  return codegen(rootExpr, nextDynamicVarId, auditRecord);
}

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

********************************************************************************/
expr* XQueryCompiler::translate(
    parsenode_t parsenode,
    audit::ScopedRecord& auditRecord)
{
  time::Timer timer;

  audit::DurationAuditor
  durationAudit(auditRecord,
                audit::XQUERY_COMPILATION_TRANSLATION_DURATION,
                timer);

  theCompilerCB->setPhase(CompilerCB::TRANSLATION);

  expr* rootExpr = zorba::translate(*parsenode, theCompilerCB);

  theCompilerCB->setPhase(CompilerCB::NONE);

  if (rootExpr == NULL)
  {
    // TODO: can this happen?
    throw ZORBA_EXCEPTION(zerr::ZAPI0002_XQUERY_COMPILATION_FAILED);
  }

  return rootExpr;
}

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

********************************************************************************/
expr* XQueryCompiler::optimize(expr* rootExpr, audit::ScopedRecord& auditRecord)
{
  time::Timer timer;

  audit::DurationAuditor
  durationAudit(auditRecord,
                audit::XQUERY_COMPILATION_OPTIMIZATION_DURATION,
                timer);

  theCompilerCB->setPhase(CompilerCB::OPTIMIZATION);

  // Build the call-graph among the udfs that are actually used in the query
  // program.
  UDFGraph udfGraph(rootExpr);

  // By default all UDFs are marked as deterministic. Now, we find which udfs
  // are actually non-deterministic and mark them as such. This has to be done
  // before optimization.
  udfGraph.inferDeterminism();

  if (theCompilerCB->theConfig.opt_level <= CompilerCB::config::O0)
  {
    theCompilerCB->setPhase(CompilerCB::NONE);
    return rootExpr;
  }

  // Optimize the udfs.
  udfGraph.optimizeUDFs(theCompilerCB);

  // Optimize the main expr (i.e., the top expr of the main module).
  RewriterContext rCtx(theCompilerCB,
                       rootExpr,
                       NULL,
                       "Optimizing main program",
                       rootExpr->get_sctx()->is_in_ordered_mode());

  GENV_COMPILERSUBSYS.getDefaultOptimizingRewriter()->rewrite(rCtx);

  rootExpr = rCtx.getRoot();

  if (theCompilerCB->theConfig.optimize_cb != NULL)
    theCompilerCB->theConfig.optimize_cb(rootExpr, "main query");

  theCompilerCB->setPhase(CompilerCB::NONE);

  return rootExpr;
}

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

********************************************************************************/
PlanIter_t XQueryCompiler::codegen(
    expr* rootExpr,
    ulong& nextDynamicVarId,
    audit::ScopedRecord& auditRecord)
{
  time::Timer timer;

  audit::DurationAuditor
  durationAudit(auditRecord,
                audit::XQUERY_COMPILATION_CODEGENERATION_DURATION,
                timer);

  theCompilerCB->setPhase(CompilerCB::CODEGEN);

  PlanIter_t plan = zorba::codegen("main query",
                                   rootExpr,
                                   theCompilerCB,
                                   nextDynamicVarId);

  theCompilerCB->setPhase(CompilerCB::NONE);

  return plan;
}

/******************************************************************************
  This is a small helper class used when the user wants to compile a library
  module. The ONLY place it is used (and should be used) is in the
  XQueryCompiler::createMainModule method below.
  QQQ When we have the ability to compile a library module indepedently, this
  rather hacky class can go away. At that time, we can also eliminate the
  "stream URL" hack in StreamResource - this is the only place in the code where
  we use the two-arg StreamResource constructor.
*******************************************************************************/
class FakeLibraryModuleURLResolver : public internal::URLResolver
{
public:
  FakeLibraryModuleURLResolver(
      zstring const& aLibraryModuleFilename,
      std::istream& aStream)
    :
    theLibraryModuleFilename(aLibraryModuleFilename),
    theStream(aStream)
  {}

  virtual ~FakeLibraryModuleURLResolver()
  {}

  virtual internal::Resource* resolveURL(
      const zstring& aUrl,
      const internal::EntityData* aEntityData)
  {
    // Since we know this URL resolver will only be used when compiling the
    // stub query, and the only URI that query will contain is the one we're
    // interested in, then we don't actually need to check the URL at all -
    // just return the stream. This is a good thing, because due to URI
    // mapping, aUrl will probably be different than the one we're expecting.
    // If we returned NULL here, Zorba's built-in URL Resolvers would attempt
    // to load the resource from somewhere else, and that may result in the
    // wrong information being compiled. See bug 992304.
    assert (theStream.good());
    // Pass a nullptr StreamReleaser; memory ownership of the istream remains
    // with the caller of this method.
    // QQQ We can remove this third argument when we can compile modules individually
    return new internal::StreamResource(&theStream, nullptr, theLibraryModuleFilename);
  }

private:
  zstring theLibraryModuleFilename;
  std::istream& theStream;
};

/******************************************************************************
  Create a dummy main module to wrap a library module.
******************************************************************************/
parsenode_t XQueryCompiler::createMainModule(
    parsenode_t aLibraryModule,
    std::istream& aXQuery,
    const zstring& aFileName)
{
  //get the namespace from the LibraryModule
  LibraryModule* mod_ast = dynamic_cast<LibraryModule *>(&*aLibraryModule);
  if (!mod_ast)
    throw ZORBA_EXCEPTION(zerr::ZAPI0002_XQUERY_COMPILATION_FAILED,
    ERROR_PARAMS(ZED(BadLibraryModule)));

  const zstring& lib_namespace = mod_ast->get_decl()->get_target_namespace();

  URI lURI(lib_namespace);
  if(!lURI.is_absolute())
  {
    throw XQUERY_EXCEPTION(err::XQST0046,
    ERROR_PARAMS(lURI.toString(), ZED(MustBeAbsoluteURI)),
      ERROR_LOC(mod_ast->get_decl()->get_location()));
  }

  // Set up the original query stream as the result of resolving the
  // library module's URI
  aXQuery.clear();
  aXQuery.seekg(0);

  FakeLibraryModuleURLResolver* aFakeResolver =
    new FakeLibraryModuleURLResolver(aFileName, aXQuery);

  theCompilerCB->theRootSctx->add_url_resolver(aFakeResolver);

  // create a dummy main module and parse it
  std::stringstream lDocStream;
  zstring tmp;
  zorba::xml::escape(lib_namespace, &tmp);
  lDocStream << "import module namespace m = '" << tmp << "'; 1";
  return parse(lDocStream, aFileName);
}

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

********************************************************************************/
XQueryCompilerSubsystem::XQueryCompilerSubsystem()
{
}

XQueryCompilerSubsystem::~XQueryCompilerSubsystem()
{
}

std::auto_ptr<XQueryCompilerSubsystem> XQueryCompilerSubsystem::create()
{
  return std::auto_ptr<XQueryCompilerSubsystem>(new XQueryCompilerSubsystemImpl());
}

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


<commit_before>#include "http/parse_request.hpp"

#include <new>
#include <string>
#include <utility>

#include <cctype>

namespace http
{

namespace
{

struct wrapper
{
	std::istream& src;
	request& req;
};

int hex_to_int(int ch)
{
	if(ch >= '0' && ch <= '9')
		return ch - '0';
	else if(ch >= 'A' && ch <= 'F')
		return ch - 'A' + 10;
	else if(ch >= 'a' && ch <= 'f')
		return ch - 'a' + 10;
	else
		return -1;
}

int parse_pchar(std::istream& src)
{
	int ch = src.get();

	if(std::isalpha(ch) || std::isdigit(ch))
	{
	        return ch;
	}

	switch(ch)
	{
	case '-': case '.': case '_': case '~': case ':': case '@':
	case '!': case '$': case '&': case '\'': case '(': case ')':
	case '*': case '+': case ',': case ';': case '=':
		return ch;
	default:
		break;
	}

	if(ch != '%')
	{
		return -1;
	}

	ch = 0;

	int temp = hex_to_int(src.get());
	if(temp < 0)
	{
		return -1;
	}
	ch += temp * 16;
	temp = hex_to_int(src.get());
	if(temp < 0)
	{
		return -1;
	}
	return ch + temp;
}

bool is_tchar(int c)
{
	switch(c)
	{
	case '!': case '#': case '$': case '%': case '&': case '\'': case '*':
	case '+': case '-': case '.': case '^': case '_': case '`': case '|': case '~':
		return true;
	default:
		break;
	}
	return std::isalnum(c);
}

parse_status parse_token(std::istream& src, std::string& token)
{
	int ch;

	token.clear();

	while(is_tchar(ch = src.get()))
	{
		token.push_back(ch);
	}
	if(ch != EOF)
	{
		src.putback(ch);
	}

	return token.empty() ? PARSE_FAILURE : PARSE_SUCCESS;
}

parse_status parse_request_target_origin_form(std::istream& src, request_target_origin_form& of)
{
	int ch;

	ch = src.get();
	if(ch != '/')
	{
		return PARSE_FAILURE;
	}
	while(true)
	{
		of.absolute_path.push_back(ch);
		ch = src.get();
		if(ch == '?')
		{
			break;
		}
		else if(ch == ' ')
		{
			return PARSE_SUCCESS;
		}
		else if(ch == EOF)
		{
			return PARSE_FAILURE;
		}
		else if(ch != '/')
		{
			src.putback(ch);
			ch = parse_pchar(src);
			if(ch < 0)
			{
				return PARSE_FAILURE;
			}
		}
	}

	of.query = std::string{};

	while(true)
	{
		ch = src.get();
		if(ch == '/' || ch == '?')
		{
			of.query->push_back(ch);
		}
		else
		{
			ch = parse_pchar(src);
			if(ch < 0)
			{
				return PARSE_FAILURE;
			}
			of.query->push_back(ch);
		}
	}

	return PARSE_SUCCESS;
}

parse_status parse_request_target(wrapper& w)
{
	int ch;
	std::string tempstr;

	ch = w.src.get();
	if(ch == '*')
	{
		w.req.target = request_target_asterisk_form{};
		w.src.get(); // Consume space
	}
	else if(ch == '/')
	{
		request_target_origin_form of;
		w.src.putback(ch);
		parse_status result = parse_request_target_origin_form(w.src, of);
		if(result != PARSE_SUCCESS)
		{
			return result;
		}
		w.req.target = std::move(of);
	}
	else
	{
		request_target_absolute_form af;
		while(ch != ':')
		{
			if(ch == EOF || ch == ' ')
			{
				return PARSE_FAILURE;
			}
			af.scheme.push_back(ch);
			ch = w.src.get();
		}
		// Consume // as in http://www.example.com/
		while((ch = w.src.get()) == '/')
			;

		while(ch != '/')
		{
			if(ch == EOF || ch == ' ')
			{
				return PARSE_FAILURE;
			}

			af.host.push_back(ch);
			ch = w.src.get();
		}
		w.src.putback(ch);

		parse_status result = parse_request_target_origin_form(w.src, af.origin_part);
		if(result != PARSE_SUCCESS)
		{
			return result;
		}
		w.req.target = std::move(af);
	}

	return PARSE_SUCCESS;
}

parse_status parse_request_line(wrapper& w)
{
	int ch;
	std::string tempstr;

	// First parse the method
	parse_status result = parse_token(w.src, tempstr);
	if(result != PARSE_SUCCESS)
	{
		return result;
	}

	// Consume space
	if((ch = w.src.get()) != ' ')
	{
		return PARSE_FAILURE;
	}

	if(tempstr == "GET")
		w.req.method = request_method::GET;
	else if(tempstr == "HEAD")
		w.req.method = request_method::HEAD;
	else if(tempstr == "POST")
		w.req.method = request_method::POST;
	else if(tempstr == "PUT")
		w.req.method = request_method::PUT;
	else if(tempstr == "DELETE")
		w.req.method = request_method::DELETE;
	else if(tempstr == "CONNECT")
		w.req.method = request_method::CONNECT;
	else if(tempstr == "OPTIONS")
		w.req.method = request_method::OPTIONS;
	else if(tempstr == "TRACE")
		w.req.method = request_method::TRACE;
	else
		return PARSE_FAILURE;

	// Next parse the target
	result = parse_request_target(w);
	if(result != PARSE_SUCCESS)
	{
		return result;
	}

	// Last consume the HTTP version and CRLF
	// Out of laziness I will ignore the HTTP version string and the CR and just consume the LF
	while((ch = w.src.get()) != '\n')
	{
		if(ch == EOF)
		{
			return PARSE_FAILURE;
		}
	}

	return PARSE_SUCCESS;
}

bool is_ows(int c)
{
	switch(c)
	{
	case ' ': case '\t':
		return true;
	default:
		return false;
	}
}

void consume_ows(std::istream& src)
{
	int ch;
	do
	{
		ch = src.get();
	} while(is_ows(ch));

	if(ch != EOF)
	{
		src.putback(ch);
	}
}

parse_status parse_header_field_value(std::istream& src, std::string& value)
{
	int ch = src.get();

	while(std::isgraph(ch))
	{
		value.push_back(ch);

		while(is_ows(ch = src.get()))
		{
			value.push_back(ch);
		}
	}

	if(ch != EOF)
	{
		src.putback(ch);
	}

	return PARSE_SUCCESS;
}

parse_status parse_header_field(wrapper& w)
{
	int ch;
	std::string tempstr;

	parse_status result = parse_token(w.src, tempstr);
	if(result != PARSE_SUCCESS)
	{
		return PARSE_FAILURE;
	}

	auto h_it = w.req.header_fields.emplace(std::move(tempstr), "").first;

	ch = w.src.get();
	if(ch != ':')
	{
		return PARSE_FAILURE;
	}
	consume_ows(w.src);

	result = parse_header_field_value(w.src, h_it->second);
	if(result != PARSE_SUCCESS)
	{
		return result;
	}

	while((ch = w.src.get()) != '\n')
	{
		if(ch == EOF)
		{
			return PARSE_FAILURE;
		}
	}

	return PARSE_SUCCESS;
}

parse_status parse_header_fields(wrapper& w)
{
	int ch;

	w.req.header_fields.clear();

	while((ch = w.src.get()) != '\n')
	{
		if(ch == EOF)
		{
			return PARSE_FAILURE;
		}
		else if(ch == '\r')
		{
			ch = w.src.get();
			if(ch != '\n')
			{
				return PARSE_FAILURE;
			}
			break;
		}
		w.src.putback(ch);

		parse_status result = parse_header_field(w);
		if(result != PARSE_SUCCESS)
		{
			return result;
		}
	}

	return PARSE_SUCCESS;
}

} // anonymous namespace

parse_status parse_request(std::istream& src, request& req)
{
	wrapper w = {src, req};

	try
	{
		parse_status result = parse_request_line(w);
		if(result != PARSE_SUCCESS)
		{
			return result;
		}

		result = parse_header_fields(w);
		if(result != PARSE_SUCCESS)
		{
			return result;
		}
	}
	catch(std::bad_alloc& e)
	{
		return PARSE_FAILURE;
	}

	return PARSE_SUCCESS;
}

} // namespace http
<commit_msg>Fixed query string parsing bug<commit_after>#include "http/parse_request.hpp"

#include <new>
#include <string>
#include <utility>

#include <cctype>

namespace http
{

namespace
{

struct wrapper
{
	std::istream& src;
	request& req;
};

int hex_to_int(int ch)
{
	if(ch >= '0' && ch <= '9')
		return ch - '0';
	else if(ch >= 'A' && ch <= 'F')
		return ch - 'A' + 10;
	else if(ch >= 'a' && ch <= 'f')
		return ch - 'a' + 10;
	else
		return -1;
}

int parse_pchar(std::istream& src)
{
	int ch = src.get();

	if(std::isalpha(ch) || std::isdigit(ch))
	{
	        return ch;
	}

	switch(ch)
	{
	case '-': case '.': case '_': case '~': case ':': case '@':
	case '!': case '$': case '&': case '\'': case '(': case ')':
	case '*': case '+': case ',': case ';': case '=':
		return ch;
	default:
		break;
	}

	if(ch != '%')
	{
		return -1;
	}

	ch = 0;

	int temp = hex_to_int(src.get());
	if(temp < 0)
	{
		return -1;
	}
	ch += temp * 16;
	temp = hex_to_int(src.get());
	if(temp < 0)
	{
		return -1;
	}
	return ch + temp;
}

bool is_tchar(int c)
{
	switch(c)
	{
	case '!': case '#': case '$': case '%': case '&': case '\'': case '*':
	case '+': case '-': case '.': case '^': case '_': case '`': case '|': case '~':
		return true;
	default:
		break;
	}
	return std::isalnum(c);
}

parse_status parse_token(std::istream& src, std::string& token)
{
	int ch;

	token.clear();

	while(is_tchar(ch = src.get()))
	{
		token.push_back(ch);
	}
	if(ch != EOF)
	{
		src.putback(ch);
	}

	return token.empty() ? PARSE_FAILURE : PARSE_SUCCESS;
}

parse_status parse_request_target_origin_form(std::istream& src, request_target_origin_form& of)
{
	int ch;

	ch = src.get();
	if(ch != '/')
	{
		return PARSE_FAILURE;
	}
	while(true)
	{
		of.absolute_path.push_back(ch);
		ch = src.get();
		if(ch == '?')
		{
			break;
		}
		else if(ch == ' ')
		{
			return PARSE_SUCCESS;
		}
		else if(ch == EOF)
		{
			return PARSE_FAILURE;
		}
		else if(ch != '/')
		{
			src.putback(ch);
			ch = parse_pchar(src);
			if(ch < 0)
			{
				return PARSE_FAILURE;
			}
		}
	}

	of.query = std::string{};

	while(true)
	{
		ch = src.get();
		if(ch == '/' || ch == '?')
		{
			of.query->push_back(ch);
		}
		else if(ch == ' ')
		{
			break;
		}
		else
		{
			src.putback(ch);
			ch = parse_pchar(src);
			if(ch < 0)
			{
				return PARSE_FAILURE;
			}
			of.query->push_back(ch);
		}
	}

	return PARSE_SUCCESS;
}

parse_status parse_request_target(wrapper& w)
{
	int ch;
	std::string tempstr;

	ch = w.src.get();
	if(ch == '*')
	{
		w.req.target = request_target_asterisk_form{};
		w.src.get(); // Consume space
	}
	else if(ch == '/')
	{
		request_target_origin_form of;
		w.src.putback(ch);
		parse_status result = parse_request_target_origin_form(w.src, of);
		if(result != PARSE_SUCCESS)
		{
			return result;
		}
		w.req.target = std::move(of);
	}
	else
	{
		request_target_absolute_form af;
		while(ch != ':')
		{
			if(ch == EOF || ch == ' ')
			{
				return PARSE_FAILURE;
			}
			af.scheme.push_back(ch);
			ch = w.src.get();
		}
		// Consume // as in http://www.example.com/
		while((ch = w.src.get()) == '/')
			;

		while(ch != '/')
		{
			if(ch == EOF || ch == ' ')
			{
				return PARSE_FAILURE;
			}

			af.host.push_back(ch);
			ch = w.src.get();
		}
		w.src.putback(ch);

		parse_status result = parse_request_target_origin_form(w.src, af.origin_part);
		if(result != PARSE_SUCCESS)
		{
			return result;
		}
		w.req.target = std::move(af);
	}

	return PARSE_SUCCESS;
}

parse_status parse_request_line(wrapper& w)
{
	int ch;
	std::string tempstr;

	// First parse the method
	parse_status result = parse_token(w.src, tempstr);
	if(result != PARSE_SUCCESS)
	{
		return result;
	}

	// Consume space
	if((ch = w.src.get()) != ' ')
	{
		return PARSE_FAILURE;
	}

	if(tempstr == "GET")
		w.req.method = request_method::GET;
	else if(tempstr == "HEAD")
		w.req.method = request_method::HEAD;
	else if(tempstr == "POST")
		w.req.method = request_method::POST;
	else if(tempstr == "PUT")
		w.req.method = request_method::PUT;
	else if(tempstr == "DELETE")
		w.req.method = request_method::DELETE;
	else if(tempstr == "CONNECT")
		w.req.method = request_method::CONNECT;
	else if(tempstr == "OPTIONS")
		w.req.method = request_method::OPTIONS;
	else if(tempstr == "TRACE")
		w.req.method = request_method::TRACE;
	else
		return PARSE_FAILURE;

	// Next parse the target
	result = parse_request_target(w);
	if(result != PARSE_SUCCESS)
	{
		return result;
	}

	// Last consume the HTTP version and CRLF
	// Out of laziness I will ignore the HTTP version string and the CR and just consume the LF
	while((ch = w.src.get()) != '\n')
	{
		if(ch == EOF)
		{
			return PARSE_FAILURE;
		}
	}

	return PARSE_SUCCESS;
}

bool is_ows(int c)
{
	switch(c)
	{
	case ' ': case '\t':
		return true;
	default:
		return false;
	}
}

void consume_ows(std::istream& src)
{
	int ch;
	do
	{
		ch = src.get();
	} while(is_ows(ch));

	if(ch != EOF)
	{
		src.putback(ch);
	}
}

parse_status parse_header_field_value(std::istream& src, std::string& value)
{
	int ch = src.get();

	while(std::isgraph(ch))
	{
		value.push_back(ch);

		while(is_ows(ch = src.get()))
		{
			value.push_back(ch);
		}
	}

	if(ch != EOF)
	{
		src.putback(ch);
	}

	return PARSE_SUCCESS;
}

parse_status parse_header_field(wrapper& w)
{
	int ch;
	std::string tempstr;

	parse_status result = parse_token(w.src, tempstr);
	if(result != PARSE_SUCCESS)
	{
		return PARSE_FAILURE;
	}

	auto h_it = w.req.header_fields.emplace(std::move(tempstr), "").first;

	ch = w.src.get();
	if(ch != ':')
	{
		return PARSE_FAILURE;
	}
	consume_ows(w.src);

	result = parse_header_field_value(w.src, h_it->second);
	if(result != PARSE_SUCCESS)
	{
		return result;
	}

	while((ch = w.src.get()) != '\n')
	{
		if(ch == EOF)
		{
			return PARSE_FAILURE;
		}
	}

	return PARSE_SUCCESS;
}

parse_status parse_header_fields(wrapper& w)
{
	int ch;

	w.req.header_fields.clear();

	while((ch = w.src.get()) != '\n')
	{
		if(ch == EOF)
		{
			return PARSE_FAILURE;
		}
		else if(ch == '\r')
		{
			ch = w.src.get();
			if(ch != '\n')
			{
				return PARSE_FAILURE;
			}
			break;
		}
		w.src.putback(ch);

		parse_status result = parse_header_field(w);
		if(result != PARSE_SUCCESS)
		{
			return result;
		}
	}

	return PARSE_SUCCESS;
}

} // anonymous namespace

parse_status parse_request(std::istream& src, request& req)
{
	wrapper w = {src, req};

	try
	{
		parse_status result = parse_request_line(w);
		if(result != PARSE_SUCCESS)
		{
			return result;
		}

		result = parse_header_fields(w);
		if(result != PARSE_SUCCESS)
		{
			return result;
		}
	}
	catch(std::bad_alloc& e)
	{
		return PARSE_FAILURE;
	}

	return PARSE_SUCCESS;
}

} // namespace http
<|endoftext|>


<commit_before>#pragma once

#include <ifaddrs.h>

#include <list>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <set>
#include <chrono>
#include <systemd/sd-event.h>

namespace phosphor
{
namespace network
{

using namespace std::chrono_literals;

// wait for three seconds before restarting the networkd
constexpr auto restartTimeout = 3s;

// refresh the objets after five seconds as network
// configuration takes 3-4 sec after systemd-networkd restart.
constexpr auto refreshTimeout = restartTimeout + 7s;

namespace systemd
{
namespace config
{

constexpr auto networkFilePrefix = "00-bmc-";
constexpr auto networkFileSuffix = ".network";
constexpr auto deviceFileSuffix = ".netdev";

}// namespace config
}// namespace systemd

using IntfName = std::string;

struct AddrInfo {
    uint8_t addrType;
    std::string ipaddress;
    uint16_t prefix;
};

using Addr_t = ifaddrs*;

struct AddrDeleter
{
    void operator()(Addr_t ptr) const
    {
        freeifaddrs(ptr);
    }
};

using AddrPtr = std::unique_ptr<ifaddrs, AddrDeleter>;

/* Need a custom deleter for freeing up sd_event */
struct EventDeleter
{
    void operator()(sd_event* event) const
    {
        event = sd_event_unref(event);
    }
};
using EventPtr = std::unique_ptr<sd_event, EventDeleter>;

template<typename T>
using UniquePtr = std::unique_ptr<T, std::function<void(T*)>>;

using AddrList = std::list<AddrInfo>;
using IntfAddrMap = std::map<IntfName, AddrList>;
using InterfaceList = std::set<IntfName>;

}//namespace network
}//namespace phosphor
<commit_msg>types: Add missing functional include<commit_after>#pragma once

#include <ifaddrs.h>

#include <functional>
#include <list>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <set>
#include <chrono>
#include <systemd/sd-event.h>

namespace phosphor
{
namespace network
{

using namespace std::chrono_literals;

// wait for three seconds before restarting the networkd
constexpr auto restartTimeout = 3s;

// refresh the objets after five seconds as network
// configuration takes 3-4 sec after systemd-networkd restart.
constexpr auto refreshTimeout = restartTimeout + 7s;

namespace systemd
{
namespace config
{

constexpr auto networkFilePrefix = "00-bmc-";
constexpr auto networkFileSuffix = ".network";
constexpr auto deviceFileSuffix = ".netdev";

}// namespace config
}// namespace systemd

using IntfName = std::string;

struct AddrInfo {
    uint8_t addrType;
    std::string ipaddress;
    uint16_t prefix;
};

using Addr_t = ifaddrs*;

struct AddrDeleter
{
    void operator()(Addr_t ptr) const
    {
        freeifaddrs(ptr);
    }
};

using AddrPtr = std::unique_ptr<ifaddrs, AddrDeleter>;

/* Need a custom deleter for freeing up sd_event */
struct EventDeleter
{
    void operator()(sd_event* event) const
    {
        event = sd_event_unref(event);
    }
};
using EventPtr = std::unique_ptr<sd_event, EventDeleter>;

template<typename T>
using UniquePtr = std::unique_ptr<T, std::function<void(T*)>>;

using AddrList = std::list<AddrInfo>;
using IntfAddrMap = std::map<IntfName, AddrList>;
using InterfaceList = std::set<IntfName>;

}//namespace network
}//namespace phosphor
<|endoftext|>


<commit_before><commit_msg>am 4a7aa350: am 5ea18555: Merge "x86_64: Fix GenArrayBoundsCheck"<commit_after><|endoftext|>


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

#include <assert.h>

#include "eval_env.h"

string BindingEnv::LookupVariable(const string& var) {
  map<string, string>::iterator i = bindings_.find(var);
  if (i != bindings_.end())
    return i->second;
  if (parent_)
    return parent_->LookupVariable(var);
  return "";
}

void BindingEnv::AddBinding(const string& key, const string& val) {
  bindings_[key] = val;
}

void BindingEnv::AddRule(const Rule* rule) {
  assert(LookupRule(rule->name()) == NULL);
  rules_[rule->name()] = rule;
}

const Rule* BindingEnv::LookupRuleCurrentScope(const string& rule_name) {
  map<string, const Rule*>::iterator i = rules_.find(rule_name);
  if (i == rules_.end())
    return NULL;
  return i->second;
}

const Rule* BindingEnv::LookupRule(const string& rule_name) {
  map<string, const Rule*>::iterator i = rules_.find(rule_name);
  if (i != rules_.end())
    return i->second;
  if (parent_)
    return parent_->LookupRule(rule_name);
  return NULL;
}

void Rule::AddBinding(const string& key, const EvalString& val) {
  bindings_[key] = val;
}

const EvalString* Rule::GetBinding(const string& key) const {
  map<string, EvalString>::const_iterator i = bindings_.find(key);
  if (i == bindings_.end())
    return NULL;
  return &i->second;
}

// static
bool Rule::IsReservedBinding(const string& var) {
  return var == "command" ||
      var == "depfile" ||
      var == "description" ||
      var == "deps" ||
      var == "generator" ||
      var == "pool" ||
      var == "restat" ||
      var == "rspfile" ||
      var == "rspfile_content";
}

const map<string, const Rule*>& BindingEnv::GetRules() const {
  return rules_;
}

string BindingEnv::LookupWithFallback(const string& var,
                                      const EvalString* eval,
                                      Env* env) {
  map<string, string>::iterator i = bindings_.find(var);
  if (i != bindings_.end())
    return i->second;

  if (eval)
    return eval->Evaluate(env);

  if (parent_)
    return parent_->LookupVariable(var);

  return "";
}

string EvalString::Evaluate(Env* env) const {
  string result;
  for (TokenList::const_iterator i = parsed_.begin(); i != parsed_.end(); ++i) {
    if (i->second == RAW)
      result.append(i->first);
    else
      result.append(env->LookupVariable(i->first));
  }
  return result;
}

void EvalString::AddText(StringPiece text) {
  // Add it to the end of an existing RAW token if possible.
  if (!parsed_.empty() && parsed_.back().second == RAW) {
    parsed_.back().first.append(text.str_, text.len_);
  } else {
    parsed_.push_back(make_pair(text.AsString(), RAW));
  }
}
void EvalString::AddSpecial(StringPiece text) {
  parsed_.push_back(make_pair(text.AsString(), SPECIAL));
}

string EvalString::Serialize() const {
  string result;
  for (TokenList::const_iterator i = parsed_.begin();
       i != parsed_.end(); ++i) {
    result.append("[");
    if (i->second == SPECIAL)
      result.append("$");
    result.append(i->first);
    result.append("]");
  }
  return result;
}
<commit_msg>Fix an assert (and tests in --debug mode) after #921.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <assert.h>

#include "eval_env.h"

string BindingEnv::LookupVariable(const string& var) {
  map<string, string>::iterator i = bindings_.find(var);
  if (i != bindings_.end())
    return i->second;
  if (parent_)
    return parent_->LookupVariable(var);
  return "";
}

void BindingEnv::AddBinding(const string& key, const string& val) {
  bindings_[key] = val;
}

void BindingEnv::AddRule(const Rule* rule) {
  assert(LookupRuleCurrentScope(rule->name()) == NULL);
  rules_[rule->name()] = rule;
}

const Rule* BindingEnv::LookupRuleCurrentScope(const string& rule_name) {
  map<string, const Rule*>::iterator i = rules_.find(rule_name);
  if (i == rules_.end())
    return NULL;
  return i->second;
}

const Rule* BindingEnv::LookupRule(const string& rule_name) {
  map<string, const Rule*>::iterator i = rules_.find(rule_name);
  if (i != rules_.end())
    return i->second;
  if (parent_)
    return parent_->LookupRule(rule_name);
  return NULL;
}

void Rule::AddBinding(const string& key, const EvalString& val) {
  bindings_[key] = val;
}

const EvalString* Rule::GetBinding(const string& key) const {
  map<string, EvalString>::const_iterator i = bindings_.find(key);
  if (i == bindings_.end())
    return NULL;
  return &i->second;
}

// static
bool Rule::IsReservedBinding(const string& var) {
  return var == "command" ||
      var == "depfile" ||
      var == "description" ||
      var == "deps" ||
      var == "generator" ||
      var == "pool" ||
      var == "restat" ||
      var == "rspfile" ||
      var == "rspfile_content";
}

const map<string, const Rule*>& BindingEnv::GetRules() const {
  return rules_;
}

string BindingEnv::LookupWithFallback(const string& var,
                                      const EvalString* eval,
                                      Env* env) {
  map<string, string>::iterator i = bindings_.find(var);
  if (i != bindings_.end())
    return i->second;

  if (eval)
    return eval->Evaluate(env);

  if (parent_)
    return parent_->LookupVariable(var);

  return "";
}

string EvalString::Evaluate(Env* env) const {
  string result;
  for (TokenList::const_iterator i = parsed_.begin(); i != parsed_.end(); ++i) {
    if (i->second == RAW)
      result.append(i->first);
    else
      result.append(env->LookupVariable(i->first));
  }
  return result;
}

void EvalString::AddText(StringPiece text) {
  // Add it to the end of an existing RAW token if possible.
  if (!parsed_.empty() && parsed_.back().second == RAW) {
    parsed_.back().first.append(text.str_, text.len_);
  } else {
    parsed_.push_back(make_pair(text.AsString(), RAW));
  }
}
void EvalString::AddSpecial(StringPiece text) {
  parsed_.push_back(make_pair(text.AsString(), SPECIAL));
}

string EvalString::Serialize() const {
  string result;
  for (TokenList::const_iterator i = parsed_.begin();
       i != parsed_.end(); ++i) {
    result.append("[");
    if (i->second == SPECIAL)
      result.append("$");
    result.append(i->first);
    result.append("]");
  }
  return result;
}
<|endoftext|>


<commit_before>/**
 * \file file.hpp
 *
 * \brief Add a summary of functionalities provided by this file.
 * \brief Stabilizability analysis for linear systems.
 *
 * A system:
 * - continuous-time case: \f$\dot{x}=Ax,\quad y=Cxf$
 * - discrete-time case: \f$x(k+1)=Ax(k)\quad y(k)=Cx(k)\f$
 * is detectable if all its unobservable modes decay to zero asymptotically.
 *
 * The detectability test can be performed in different ways:
 * - Eigenvector test [1]:
 *   - The continuous-time LTI system is detectable if and only if every
 *     eigenvector of A^T corresponding to an eigenvalue with a positive or zero
 *     real part is not in the kernel of C.
 *   - The discrete-time LTI system is detectable if and only if every
 *     eigenvector of A^T corresponding to an eigenvalue with magnitude larger
 *     or equal to 1 is not in the kernel of C.
 *   .
 * - Popov-Belevitch-Hautus [PBH] test [1,2]:
 *   - The continuous-time LTI system is detectable if and only if
 *     \f[
 *       \operatorname{rank}(\begin{pmatrix}A - \lambda I \\ C\end{pmatrix}) = n, \quad \forall\lambda\in\mathbb{C}: \Re[\lambda] \ge 0
 *     \f]
 *   - The discrete-time LTI system is detectable if and only if
 *     \f[
 *       \operatorname{rank}(\begin{pmatrix}A - \lambda I \\ C\end{pmatrix}) = n, \quad \forall\lambda\in\mathbb{C}: |\lambda| \ge 1
 *     \f]
 *   .
 * - Lyapunov test [1]:
 *   The LTI system (AB-LTI) is detectable if and only if there is a
 *   positive-definite solution \f$P\f$ to the following Lyapunov matrix
 *   inequality:
 *   - Continuous-time case: \f$A^{T}P + PA - C^{T}C < 0\f$
 *   - Discrete-time case: \f$A^{T}PA - P - C^{T}C < 0\f$
 *   .
 * - Stabilization test [2]:
 *   The matrix pair \f$(C,A)\f$ is detectable if and only if the dual par \f$(A^T,C^T)\f$ is stabilizable.
 * .
 *
 * \note
 *  Estimating the rank of a matrix is ill-conditioned; that is, it is very
 *  sensitive to roundoff errors and errors in the data.
 *
 * \todo
 *  Implement other (more numerically robust) tests (e.g., see Octave and SCILAB).
 *
 *
 * References:
 * -# J.P. Hesphana, "Linear Systems Theory", Princeton University Press, 2009.
 * -# W.J. Terrel, "Stability and Stabilization: An Introduction", Princeton University Press, 2009
 * .
 *
 *

 *
 * Copyright (C) 2011  Distributed Computing System (DCS) Group, Computer
 * Science Department - University of Piemonte Orientale, Alessandria (Italy).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 * \author Marco Guazzone, <marco.guazzone@mfn.unipmn.it>
 */

#ifndef DCS_CONTROL_ANALYSIS_DETECTABILITY_HPP
#define DCS_CONTROL_ANALYSIS_DETECTABILITY_HPP

#include <boost/numeric/ublas/expression_types.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/traits.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublasx/operation/eigen.hpp>
#include <boost/numeric/ublasx/operation/num_columns.hpp>
#include <boost/numeric/ublasx/operation/num_rows.hpp>
#include <boost/numeric/ublasx/operation/rank.hpp>
#include <boost/numeric/ublasx/operation/size.hpp>
#include <cmath>
#include <complex>
#include <dcs/assert.hpp>
#include <dcs/macro.hpp>
#include <stdexcept>

namespace dcs { namespace control {

template <
    typename AMatrixT,
    typename CMatrixT,
	typename RealT
>
bool is_detectable(::boost::numeric::ublas::matrix_expression<AMatrixT> const& A,
				   ::boost::numeric::ublas::matrix_expression<CMatrixT> const& C,
				   bool discrete,
				   RealT tol)
{
	DCS_MACRO_SUPPRESS_UNUSED_VARIABLE_WARNING( tol );

	namespace ublas = ::boost::numeric::ublas;
	namespace ublasx = ::boost::numeric::ublasx;

	// pre: A must be square
	DCS_ASSERT(
			ublasx::num_rows(A) == ublasx::num_columns(A),
			throw ::std::invalid_argument("[dcs::control::is_detectable] The A matrix must be square.")
		);
	// pre: num_rows(A) == num_rows(B)
	DCS_ASSERT(
			ublasx::num_rows(A) == ublasx::num_columns(C),
			throw ::std::invalid_argument("[dcs::control::is_detectable] The size of A and C matrices are not conformant.")
		);

	typedef typename ublas::promote_traits<typename AMatrixT::value_type,
										   typename CMatrixT::value_type>::promote_type value_type;
	typedef typename ublas::promote_traits<typename AMatrixT::size_type,
										   typename CMatrixT::size_type>::promote_type size_type;
	typedef typename ublas::type_traits<value_type>::real_type real_type;
	typedef ::std::complex<real_type> complex_type;

	// Use the Popov-Belevitch-Hautus [PBH] test for detectability.
	// E.g., see Hespanha, "Linear System Theory", 2009, pp.152-153.

	ublas::vector<complex_type> v;
	ublasx::eigenvalues(A, v);
	size_type nv(ublasx::size(v));
	size_type na(ublas::num_rows(A));
	size_type nc(ublas::num_rows(C));
	size_type ns(na+nc);
	ublas::matrix<complex_type> cA(A);
	ublas::identity_matrix<complex_type> I(na);
	for (size_type i = 0; i < nv; ++i)
	{
		if (discrete)
		{
			if (::std::abs(v(i)) < 1)
			{
				continue;
			}
		}
		else
		{
			if (v(i).real() < 0)
			{
				continue;
			}
		}

		//ublas::matrix<complex_type> AA(ublas::matrix<complex_type>(A)-v(i)*I);
		ublas::matrix<complex_type> S(ns,na);
		ublas::subrange(S, 0, na, 0, na) = cA-v(i)*I;
		ublas::subrange(S, na, ns, 0, na) = C;

		if (ublasx::rank(S) != na)
		{
			return false;
		}
	}

	return true;
}

template <
    typename AMatrixT,
    typename BMatrixT
>
inline
bool is_detectable(::boost::numeric::ublas::matrix_expression<AMatrixT> const& A,
				 	 ::boost::numeric::ublas::matrix_expression<BMatrixT> const& B,
					 bool discrete)
{
	return is_detectable(A, B, discrete, 0.0);
}

}} // Namespace dcs::control

#endif // DCS_CONTROL_ANALYSIS_DETECTABILITY_HPP
<commit_msg>(changes|minor) Doc.<commit_after>/**
 * \file src/dcs/control/analysis/detectability.hpp
 *
 * \brief Detectability analysis for linear systems.
 *
 * A system:
 * - continuous-time case: \f$\dot{x}=Ax,\quad y=Cxf$
 * - discrete-time case: \f$x(k+1)=Ax(k)\quad y(k)=Cx(k)\f$
 * is detectable if all its unobservable modes decay to zero asymptotically.
 *
 * The detectability test can be performed in different ways:
 * - Eigenvector test [1]:
 *   - The continuous-time LTI system is detectable if and only if every
 *     eigenvector of A^T corresponding to an eigenvalue with a positive or zero
 *     real part is not in the kernel of C.
 *   - The discrete-time LTI system is detectable if and only if every
 *     eigenvector of A^T corresponding to an eigenvalue with magnitude larger
 *     or equal to 1 is not in the kernel of C.
 *   .
 * - Popov-Belevitch-Hautus [PBH] test [1,2]:
 *   - The continuous-time LTI system is detectable if and only if
 *     \f[
 *       \operatorname{rank}(\begin{pmatrix}A - \lambda I \\ C\end{pmatrix}) = n, \quad \forall\lambda\in\mathbb{C}: \Re[\lambda] \ge 0
 *     \f]
 *   - The discrete-time LTI system is detectable if and only if
 *     \f[
 *       \operatorname{rank}(\begin{pmatrix}A - \lambda I \\ C\end{pmatrix}) = n, \quad \forall\lambda\in\mathbb{C}: |\lambda| \ge 1
 *     \f]
 *   .
 * - Lyapunov test [1]:
 *   The LTI system (AB-LTI) is detectable if and only if there is a
 *   positive-definite solution \f$P\f$ to the following Lyapunov matrix
 *   inequality:
 *   - Continuous-time case: \f$A^{T}P + PA - C^{T}C < 0\f$
 *   - Discrete-time case: \f$A^{T}PA - P - C^{T}C < 0\f$
 *   .
 * - Stabilization test [2]:
 *   The matrix pair \f$(C,A)\f$ is detectable if and only if the dual par \f$(A^T,C^T)\f$ is stabilizable.
 * .
 *
 * \note
 *  Estimating the rank of a matrix is ill-conditioned; that is, it is very
 *  sensitive to roundoff errors and errors in the data.
 *
 * \todo
 *  Implement other (more numerically robust) tests (e.g., see Octave and SCILAB).
 *
 *
 * References:
 * -# J.P. Hesphana, "Linear Systems Theory", Princeton University Press, 2009.
 * -# W.J. Terrel, "Stability and Stabilization: An Introduction", Princeton University Press, 2009
 * .
 *
 *

 *
 * Copyright (C) 2011  Distributed Computing System (DCS) Group, Computer
 * Science Department - University of Piemonte Orientale, Alessandria (Italy).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 * \author Marco Guazzone, <marco.guazzone@mfn.unipmn.it>
 */

#ifndef DCS_CONTROL_ANALYSIS_DETECTABILITY_HPP
#define DCS_CONTROL_ANALYSIS_DETECTABILITY_HPP

#include <boost/numeric/ublas/expression_types.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/traits.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublasx/operation/eigen.hpp>
#include <boost/numeric/ublasx/operation/num_columns.hpp>
#include <boost/numeric/ublasx/operation/num_rows.hpp>
#include <boost/numeric/ublasx/operation/rank.hpp>
#include <boost/numeric/ublasx/operation/size.hpp>
#include <cmath>
#include <complex>
#include <dcs/assert.hpp>
#include <dcs/macro.hpp>
#include <stdexcept>

namespace dcs { namespace control {

template <
    typename AMatrixT,
    typename CMatrixT,
	typename RealT
>
bool is_detectable(::boost::numeric::ublas::matrix_expression<AMatrixT> const& A,
				   ::boost::numeric::ublas::matrix_expression<CMatrixT> const& C,
				   bool discrete,
				   RealT tol)
{
	DCS_MACRO_SUPPRESS_UNUSED_VARIABLE_WARNING( tol );

	namespace ublas = ::boost::numeric::ublas;
	namespace ublasx = ::boost::numeric::ublasx;

	// pre: A must be square
	DCS_ASSERT(
			ublasx::num_rows(A) == ublasx::num_columns(A),
			throw ::std::invalid_argument("[dcs::control::is_detectable] The A matrix must be square.")
		);
	// pre: num_rows(A) == num_rows(B)
	DCS_ASSERT(
			ublasx::num_rows(A) == ublasx::num_columns(C),
			throw ::std::invalid_argument("[dcs::control::is_detectable] The size of A and C matrices are not conformant.")
		);

	typedef typename ublas::promote_traits<typename AMatrixT::value_type,
										   typename CMatrixT::value_type>::promote_type value_type;
	typedef typename ublas::promote_traits<typename AMatrixT::size_type,
										   typename CMatrixT::size_type>::promote_type size_type;
	typedef typename ublas::type_traits<value_type>::real_type real_type;
	typedef ::std::complex<real_type> complex_type;

	// Use the Popov-Belevitch-Hautus [PBH] test for detectability.
	// E.g., see Hespanha, "Linear System Theory", 2009, pp.152-153.

	ublas::vector<complex_type> v;
	ublasx::eigenvalues(A, v);
	size_type nv(ublasx::size(v));
	size_type na(ublas::num_rows(A));
	size_type nc(ublas::num_rows(C));
	size_type ns(na+nc);
	ublas::matrix<complex_type> cA(A);
	ublas::identity_matrix<complex_type> I(na);
	for (size_type i = 0; i < nv; ++i)
	{
		if (discrete)
		{
			if (::std::abs(v(i)) < 1)
			{
				continue;
			}
		}
		else
		{
			if (v(i).real() < 0)
			{
				continue;
			}
		}

		//ublas::matrix<complex_type> AA(ublas::matrix<complex_type>(A)-v(i)*I);
		ublas::matrix<complex_type> S(ns,na);
		ublas::subrange(S, 0, na, 0, na) = cA-v(i)*I;
		ublas::subrange(S, na, ns, 0, na) = C;

		if (ublasx::rank(S) != na)
		{
			return false;
		}
	}

	return true;
}

template <
    typename AMatrixT,
    typename BMatrixT
>
inline
bool is_detectable(::boost::numeric::ublas::matrix_expression<AMatrixT> const& A,
				 	 ::boost::numeric::ublas::matrix_expression<BMatrixT> const& B,
					 bool discrete)
{
	return is_detectable(A, B, discrete, 0.0);
}

}} // Namespace dcs::control

#endif // DCS_CONTROL_ANALYSIS_DETECTABILITY_HPP
<|endoftext|>


<commit_before>#include "DagMan.h"
#include "log.h"

#include <pwd.h>
#include <unistd.h>
#include <iostream.h>

char *DAG_LOG = "dagman.log";
char *CONDOR_LOG = "condor.log";
char *DAGMAN_LOCKFILE = ".dagmanLock";
int   DEFAULT_SLEEP = 10;

int DagLog_fd;

DagMan *dagman;

static void Usage() {
  fprintf(stderr, "Usage: condor_dagman [-condorlog file] [-daglog file] "
          "[-lockfile file] <input filename>\n");
  exit(1);
}

int main(int argc, char **argv) {
  // DagMan recovery occurs if the file pointed to by
  // DAGMAN_LOCKFILE exists. This file is deleted on normal
  // completion of condor_dagman

  // DAG_LOG is used for DagMan to maintain the order of events
  // Recovery only uses the condor log
  // In this version, the DAG_LOG is not used

  char *datafile = NULL;

  int parseStatus;
  int nJobsSubmitted;

  dagman = new DagMan();

  if (argc < 2) Usage();  //  Make sure an input file was specified

  //
  // Process command-line arguments
  //
  for (int i = 1; i < argc; i++) {
	if (!strcmp("-condorlog", argv[i])) {
      i++;
      if (argc <= i) {
        printf("No condor log specified\n");
		Usage();
      }
      CONDOR_LOG = argv[i];
    } else if (!strcmp("-daglog", argv[i])) {
      i++;
      if (argc <= i) {
		printf("No DagMan logfile name specified\n");
		Usage();
      }
      DAG_LOG = argv[i];
    } else if (!strcmp("-lockfile", argv[i])) {
      i++;
      if (argc <= i) {
		printf("No DagMan lockfile specified\n");
		Usage();
      }
      DAGMAN_LOCKFILE = argv[i];
    } else if (!strcmp("-help", argv[i])) {
      Usage();
    } else if (i == (argc - 1)) {
      datafile = argv[i];
    } else Usage();
  }

  if (datafile == NULL) {
    printf("No input file was specified\n");
    Usage();
  }

#ifdef VERBOSE
  fprintf(stderr, "Dagman log will be written to %s\n",
          DAG_LOG);
  fprintf(stderr, "Condor log will be written to %s\n",
          CONDOR_LOG);
  fprintf(stderr, "Dagman Lockfile will be written to %s\n",
          DAGMAN_LOCKFILE);
  fprintf(stderr, "Input file is %s\n",
          datafile);
#endif

  //
  // Initialize the DagMan object
  //
  struct passwd *p;
  p = getpwuid(getuid());
  if (p == NULL) {
    fprintf(stderr, "Error looking up username\n");
    return (1);
  }

  int status = dagman->Init(CONDOR_LOG,
                        DAG_LOG,
                        DAGMAN_LOCKFILE,
                        p->pw_name);
#ifdef DEBUG
  fprintf(stderr, "finished dagman->Init");
#endif

  if (status != OK) {
    fprintf(stderr, "ERROR in DagMan initialization: ");
    fprintf(stderr, "\n");
    return (1);
  }

  //
  // Open the input file, then parse it. The Parse() routine
  // takes care of adding jobs and dependencies to the DagMan
  //
  FILE *fp = fopen(datafile, "r");
  if (fp == NULL) {
    fprintf(stderr, "Could not open file %s for input\n", datafile);
    return (1);
  }

  parseStatus = Parse(fp, dagman);

  if (parseStatus != 0) {
    printf("Error Parsing\n");
    return (1);
  }

#ifdef DEBUG
  printf("After Parsing ...\n");
  dagman->PrintNodeList();
#endif

#ifdef VERBOSE
  fprintf(stderr, "Starting DagMan ... \n");
#endif

  // check if any jobs have been pre-defined as being done
  // if so, report this to the other nodes

#ifdef DEBUG
  cout << "# of Jobs" << dagman->NumJobs() << endl;
  cout << "# of Jobs completed" << dagman->NumJobsCompleted() << endl;
#endif

  dagman->CheckForFinishedJobs();

  // If the Lockfile exists, this indicates a premature termination
  // of a previous run of Dagman. If condor log is also present,
  // we run in recovery mode

  // If the Daglog is not present, then we do not run in recovery
  // mode

  if ( (access(DAGMAN_LOCKFILE, F_OK) == 0) &&
       (access(CONDOR_LOG,      F_OK) == 0) &&
       (access(DAG_LOG,         F_OK) == 0) ) {     // RECOVERY MODE

    DagLog_fd = open(DAG_LOG, O_RDWR, 0600);
    dagman->Recover_DagMan();
  } else {                                          // NORMAL MODE

    // if there is an older version of the log files,
    // we need to delete these.

    if ( access( CONDOR_LOG, F_OK) == 0 ) {
#ifdef DEBUG
	  fprintf(stderr, "Deleting an older version of %s\n", CONDOR_LOG);
#endif

	  FILE *fp;
	  char *DeleteCommand;
	  DeleteCommand = new char[strlen(CONDOR_LOG) + 10];

	  sprintf(DeleteCommand, "rm %s", CONDOR_LOG);

	  fp = popen(DeleteCommand, "r");
	  if (fp == NULL) {
        fprintf(stderr, "popen in main() failed\n");
        return (1);
      }

	  pclose(fp);
	}

    if ( access( DAG_LOG, F_OK) == 0 ) {
#ifdef DEBUG
	  fprintf(stderr, "Deleting an older version of %s\n", DAG_LOG);
#endif

	  FILE *fp;
	  char *DeleteCommand;
	  DeleteCommand = new char[strlen(DAG_LOG) + 10];

	  sprintf(DeleteCommand, "rm %s", DAG_LOG);

	  fp = popen(DeleteCommand, "r");
	  if (fp == NULL) {
        fprintf(stderr, "popen in main() failed\n");
        return (1);
      }

	  pclose(fp);
	}

    DagLog_fd = open(DAG_LOG, O_RDWR | O_CREAT, 0600);
    open(DAGMAN_LOCKFILE, O_RDWR | O_CREAT, 0600);
  }

#ifdef DEBUG
  cout << "# of Jobs " << dagman->NumJobs() << endl;
  cout << "# of Jobs completed " << dagman->NumJobsCompleted() << endl;
#endif

  while (dagman->NumJobsCompleted() < dagman->NumJobs()) {

    status = dagman->RunReadyJobs(nJobsSubmitted);

    if (status != OK) {
	  fprintf(stderr, "ERROR submitting ready jobs: ");
	  fprintf(stderr, "\n");
	  return (1);
	}

    //
    // Wait for new events to be written into the condor log
    //

    // need to put a check here for the case
    // where the condor log doesnt exist (i.e., no jobs submitted)

    bool ready;
    do {
      // fprintf (stderr, "dagman->DetectLogGrowth(): "); fflush(stderr);
      ready = dagman->DetectLogGrowth();

      // fprintf (stderr, "dagman->WaitForLogEvents(): "); fflush(stderr);
      // ready = dagman->WaitForLogEvents();

      // fprintf (stderr, "%s\n", ready ? "true" : "false"); fflush(stderr);
    } while (!ready);

    fprintf (stderr, "dagman->ProcessLogEvents()\n"); fflush(stderr);
    status = dagman->ProcessLogEvents();
    if (status != OK) {
	  fprintf(stderr, "ERROR processing condor log.\n");
	  return (1);
	}

    // Nothing to do if all jobs have finished
    //
    // (This is not needed, look at the while loop condition above!)
    // if (dagman->NumJobsCompleted() == dagman->NumJobs()) break;

  }

#ifdef DEBUG
  fprintf(stderr, "No more jobs to run\n");
#endif
  delete dagman;
  close(DagLog_fd);

  return (0);
}
<commit_msg>Added -debug option (See debug.h for Doc++ description) Modified usage of DagMan class to match new interface in Dag class.<commit_after>#include "condor_common.h"
#include "dag.h"
#include "log.h"
#include "debug.h"
#include "parse.h"

#if 0  /* sig_install.h is broken, so I will defined my own installer */
#include "sig_install.h"
#else
//typedef void (*SIG_HANDLER)(int);
//void install_sig_handler (int sig, SIG_HANDLER handler);
#endif

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

// Required for linking with condor libs
extern "C" int SetSyscalls() { return 0; }

//---------------------------------------------------------------------------
static void Usage() {
  debug_printf (DEBUG_SILENT, "Usage: condor_dagman [-debug <level>] "
                "[-condorlog <file>] \n\t\t"
                "[-lockfile <file>] <file.dag>\n");
  exit(1);
}

//---------------------------------------------------------------------------
int main(int argc, char **argv) {

  debug_progname = argv[0];
  debug_level = DEBUG_NORMAL;  // Default debug level is normal output

#if !defined(WIN32)
  // the following is used to avoid 'broken pipe' messages
  install_sig_handler (SIGPIPE, SIG_IGN);
#endif

  char *condorLogName = "condor.log";
  char *lockFileName = ".dagmanLock";

  // DagMan recovery occurs if the file pointed to by
  // lockFileName exists. This file is deleted on normal
  // completion of condor_dagman

  // DAG_LOG is used for DagMan to maintain the order of events
  // Recovery only uses the condor log
  // In this version, the DAG_LOG is not used

  char *datafile = NULL;

  Dag * dag = new Dag();

  if (argc < 2) Usage();  //  Make sure an input file was specified

  //
  // Process command-line arguments
  //
  for (int i = 1; i < argc; i++) {
    if (!strcmp("-debug", argv[i])) {
      i++;
      if (argc <= i) {
        debug_println (DEBUG_SILENT, "No debug level specified");
        Usage();
      }
      debug_level = (debug_level_t) atoi (argv[i]);
	} else if (!strcmp("-condorlog", argv[i])) {
      i++;
      if (argc <= i) {
        debug_println (DEBUG_SILENT, "No condor log specified");
		Usage();
      }
      condorLogName = argv[i];
    } else if (!strcmp("-lockfile", argv[i])) {
      i++;
      if (argc <= i) {
		debug_println (DEBUG_SILENT, "No DagMan lockfile specified");
		Usage();
      }
      lockFileName = argv[i];
    } else if (!strcmp("-help", argv[i])) {
      Usage();
    } else if (i == (argc - 1)) {
      datafile = argv[i];
    } else Usage();
  }

  if (datafile == NULL) {
    debug_println (DEBUG_SILENT, "No input file was specified");
    Usage();
  }

  debug_println (DEBUG_VERBOSE,"Condor log will be written to %s", condorLogName);
  debug_println (DEBUG_VERBOSE,"Dagman Lockfile will be written to %s",
                 lockFileName);
  debug_println (DEBUG_VERBOSE,"Input file is %s", datafile);

  //
  // Initialize the DagMan object
  //

  if (dag->Init(condorLogName, lockFileName) != OK) {
    debug_error (1, DEBUG_QUIET, "ERROR in DagMan initialization!");
  }

  //
  // Parse the input file.  The parse() routine
  // takes care of adding jobs and dependencies to the DagMan
  //
  debug_println (DEBUG_VERBOSE, "Parsing %s ...", datafile);
  if (!parse (datafile, dag)) {
    debug_error (1, DEBUG_QUIET, "Failed to parse %s", datafile);
  }

  debug_println (DEBUG_VERBOSE, "Dag contains %d total jobs",
                 dag->NumJobs());

  if (DEBUG_LEVEL(DEBUG_DEBUG_3)) dag->PrintJobList();

  // check if any jobs have been pre-defined as being done
  // if so, report this to the other nodes
  dag->TerminateFinishedJobs();

  debug_println (DEBUG_VERBOSE, "Number of Pre-completed Jobs: %d",
                 dag->NumJobsDone());

  debug_println (DEBUG_NORMAL, "Starting DagMan ...");

  // If the Lockfile exists, this indicates a premature termination
  // of a previous run of Dagman. If condor log is also present,
  // we run in recovery mode

  // If the Daglog is not present, then we do not run in recovery
  // mode

  if (access(lockFileName,  F_OK) == 0 &&
      access(condorLogName, F_OK) == 0) {

    debug_println (DEBUG_VERBOSE, "Lock file %s detected, "
                   "running in RECOVER mode", lockFileName);
    dag->Recover();

  } else {                                      // NORMAL MODE

    // if there is an older version of the log files,
    // we need to delete these.

    if ( access( condorLogName, F_OK) == 0 ) {
      debug_println (DEBUG_VERBOSE, "Deleting older version of %s",
                     condorLogName);
      if (remove (condorLogName) == -1)
        debug_perror (1, DEBUG_QUIET, condorLogName);
    }
    open(lockFileName, O_RDWR | O_CREAT, 0600);
  }

  while (dag->NumJobsDone() < dag->NumJobs()) {

    debug_println (DEBUG_DEBUG_2, "%s: Jobs Done: %d/%d", __FUNCTION__,
                   dag->NumJobsDone(), dag->NumJobs());

    if (DEBUG_LEVEL(DEBUG_DEBUG_3)) dag->PrintJobList();

    int ran = dag->SubmitReadyJobs();
    debug_println (DEBUG_DEBUG_2, "%s: Jobs Submitted: %d",
                   __FUNCTION__, ran);

    //
    // Wait for new events to be written into the condor log
    //

    // need to put a check here for the case
    // where the condor log doesn't exist (i.e., no jobs submitted)

    while (!dag->DetectLogGrowth());

    if (dag->ProcessLogEvents() != OK) {
      debug_error (1, DEBUG_QUIET, "ERROR processing condor log!");
	}

    // Nothing to do if all jobs have finished
  }

  debug_println (DEBUG_NORMAL, "All jobs Completed!");
  delete dag;
  return (0);
}
<|endoftext|>


<commit_before>/*
===========================================================================
Daemon BSD Source Code
Copyright (c) 2013-2016, Daemon Developers
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Daemon developers nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS 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 "Common.h"

namespace Log {

    Logger::Logger(Str::StringRef name, std::string prefix, Level defaultLevel)
        : filterLevel(new Cvar::Cvar<Log::Level>(
              "logs.logLevel." + name, "Log::Level - logs from '" + name + "' below the level specified are filtered", 0, defaultLevel)),
          prefix(prefix), enableSuppression(true) {
    }

    std::string Logger::Prefix(std::string message) const {
        if (prefix.empty()) {
            return message;
        } else {
            return prefix + " " + message;
        }
    }

    void Logger::Dispatch(std::string message, Log::Level level, Str::StringRef format) {
        if (enableSuppression) {
            Log::DispatchWithSuppression(std::move(message), level, format);
        } else {
            Log::DispatchByLevel(std::move(message), level);
        }
    }

    Logger Logger::WithoutSuppression() {
        Logger unsuppressed = *this;
        unsuppressed.enableSuppression = false;
        return unsuppressed;
    }

    Logger defaultLogger(VM_STRING_PREFIX "default", "", Level::NOTICE);

    bool ParseCvarValue(std::string value, Log::Level& result) {
        if (value == "warning" or value == "warn") {
            result = Log::Level::WARNING;
            return true;
        }

        if (value == "info" or value == "notice") {
            result = Log::Level::NOTICE;
            return true;
        }

        if (value == "verbose") {
            result = Log::Level::VERBOSE;
            return true;
        }

        if (value == "debug" or value == "all") {
            result = Log::Level::DEBUG;
            return true;
        }

        return false;
    }

    std::string SerializeCvarValue(Log::Level value) {
        switch(value) {
            case Log::Level::WARNING:
                return "warning";
            case Log::Level::NOTICE:
                return "notice";
            case Log::Level::VERBOSE:
                return "verbose";
            case Log::Level::DEBUG:
                return "debug";
            default:
                return "";
        }
    }

    static const int debugTargets = (1 << GRAPHICAL_CONSOLE) | (1 << TTY_CONSOLE);
    static const int verboseTargets = (1 << GRAPHICAL_CONSOLE) | (1 << TTY_CONSOLE) | (1 << LOGFILE);
    static const int noticeTargets = (1 << GRAPHICAL_CONSOLE) | (1 << TTY_CONSOLE) | (1 << LOGFILE);
    static const int warnTargets = (1 << GRAPHICAL_CONSOLE) | (1 << TTY_CONSOLE) | (1 << LOGFILE);

    //TODO add the time (broken for now because it is journaled) use Sys_Milliseconds instead (Utils::Milliseconds ?)
    void DispatchByLevel(std::string message, Log::Level level) {
        switch (level) {
        case Level::DEBUG:
            Log::Dispatch({"^5Debug: " + message}, debugTargets);
            break;
        case Level::VERBOSE:
            Log::Dispatch({std::move(message)}, verboseTargets);
            break;
        case Level::NOTICE:
            Log::Dispatch({std::move(message)}, noticeTargets);
            break;
        case Level::WARNING:
            Log::Dispatch({"^3Warn: " + message}, warnTargets);
        }
    }

    template <typename T>
    static T GetCvarOrDie(Str::StringRef cvar) {
        T value;
        std::string valueString = Cvar::GetValue(cvar);
        if (!Cvar::ParseCvarValue(valueString, value)) {
            Sys::Error("Failed to deserialize cvar %s with value: %s", cvar, valueString);
        }
        return value;
    }
    namespace {
        // Log-spam suppression: if more than MAX_OCCURRENCES log messages with the same format string
        // are sent in less than INTERVAL_MS milliseconds, they will stop being printed.
        // (Unless the spammy message desists long enough to be flushed out of the buffer: then it can
        // be printed again.)
        class LogSpamSuppressor {
            struct MessageStatistics {
                std::string messageFormat;
                int numOccurrences;
                int intervalStartTime = -2000000;
            };
            std::vector<MessageStatistics> buf;
            std::mutex mutex;
        public:
            enum Result {
                OK, // not log spam
                LAST_CHANCE, // any more messages after this will be considered log spam
                KNOWN_SPAM
            };
            Result UpdateAndEvaluate(Str::StringRef messageFormat) {
                std::lock_guard<std::mutex> lock(mutex);
                int intervalMs = GetCvarOrDie<int>("logs.suppression.interval");
                int maxOccurrences = GetCvarOrDie<int>("logs.suppression.count");
                int bufferSize = GetCvarOrDie<int>("logs.suppression.bufferSize");
                buf.resize(bufferSize);
                MessageStatistics* oldest = &buf[0];
                int now = Sys::Milliseconds();
                // Search for an existing entry. An entry is considered expired
                // if it is both older than INTERVAL_MS and has less than MAX_OCCURRENCES.
                for (MessageStatistics& stats : buf) {
                    if ((stats.numOccurrences >= maxOccurrences || stats.intervalStartTime > now - intervalMs)
                        && stats.messageFormat == messageFormat) {
                        ++stats.numOccurrences;
                        if (stats.numOccurrences < maxOccurrences) {
                            return OK;
                        }
                        stats.intervalStartTime = now;
                        return stats.numOccurrences == maxOccurrences ? LAST_CHANCE : KNOWN_SPAM;
                    }
                    if (stats.intervalStartTime < oldest->intervalStartTime) {
                        oldest = &stats;
                    }
                }
                // Replace oldest entry if there is not already one for this message
                oldest->intervalStartTime = now;
                oldest->messageFormat = messageFormat;
                oldest->numOccurrences = 1;
                return OK;
            }
        };
    } // namespace

    void DispatchWithSuppression(std::string message, Log::Level level, Str::StringRef format) {
        static LogSpamSuppressor suppressor;
        if (!GetCvarOrDie<bool>("logs.suppression.enabled")) {
            DispatchByLevel(std::move(message), level);
            return;
        }
        switch (suppressor.UpdateAndEvaluate(format)) {
        case LogSpamSuppressor::LAST_CHANCE:
            message += " [further messages like this will be suppressed]";
            DAEMON_FALLTHROUGH;
        case LogSpamSuppressor::OK:
            DispatchByLevel(std::move(message), level);
            break;
        case LogSpamSuppressor::KNOWN_SPAM:
            break;
        }
    }

    void CommandInteractionMessage(std::string message) {
        DispatchByLevel(std::move(message), Log::Level::NOTICE);
    }
} // namespace Log

namespace Cvar {
    template<>
    std::string GetCvarTypeName<Log::Level>() {
        return "log level";
    }
}
<commit_msg>Log all messages to the log file<commit_after>/*
===========================================================================
Daemon BSD Source Code
Copyright (c) 2013-2016, Daemon Developers
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Daemon developers nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS 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 "Common.h"

namespace Log {

    Logger::Logger(Str::StringRef name, std::string prefix, Level defaultLevel)
        : filterLevel(new Cvar::Cvar<Log::Level>(
              "logs.logLevel." + name, "Log::Level - logs from '" + name + "' below the level specified are filtered", 0, defaultLevel)),
          prefix(prefix), enableSuppression(true) {
    }

    std::string Logger::Prefix(std::string message) const {
        if (prefix.empty()) {
            return message;
        } else {
            return prefix + " " + message;
        }
    }

    void Logger::Dispatch(std::string message, Log::Level level, Str::StringRef format) {
        if (enableSuppression) {
            Log::DispatchWithSuppression(std::move(message), level, format);
        } else {
            Log::DispatchByLevel(std::move(message), level);
        }
    }

    Logger Logger::WithoutSuppression() {
        Logger unsuppressed = *this;
        unsuppressed.enableSuppression = false;
        return unsuppressed;
    }

    Logger defaultLogger(VM_STRING_PREFIX "default", "", Level::NOTICE);

    bool ParseCvarValue(std::string value, Log::Level& result) {
        if (value == "warning" or value == "warn") {
            result = Log::Level::WARNING;
            return true;
        }

        if (value == "info" or value == "notice") {
            result = Log::Level::NOTICE;
            return true;
        }

        if (value == "verbose") {
            result = Log::Level::VERBOSE;
            return true;
        }

        if (value == "debug" or value == "all") {
            result = Log::Level::DEBUG;
            return true;
        }

        return false;
    }

    std::string SerializeCvarValue(Log::Level value) {
        switch(value) {
            case Log::Level::WARNING:
                return "warning";
            case Log::Level::NOTICE:
                return "notice";
            case Log::Level::VERBOSE:
                return "verbose";
            case Log::Level::DEBUG:
                return "debug";
            default:
                return "";
        }
    }

    static const int debugTargets = (1 << GRAPHICAL_CONSOLE) | (1 << TTY_CONSOLE) | (1 << LOGFILE);
    static const int verboseTargets = (1 << GRAPHICAL_CONSOLE) | (1 << TTY_CONSOLE) | (1 << LOGFILE);
    static const int noticeTargets = (1 << GRAPHICAL_CONSOLE) | (1 << TTY_CONSOLE) | (1 << LOGFILE);
    static const int warnTargets = (1 << GRAPHICAL_CONSOLE) | (1 << TTY_CONSOLE) | (1 << LOGFILE);

    //TODO add the time (broken for now because it is journaled) use Sys_Milliseconds instead (Utils::Milliseconds ?)
    void DispatchByLevel(std::string message, Log::Level level) {
        switch (level) {
        case Level::DEBUG:
            Log::Dispatch({"^5Debug: " + message}, debugTargets);
            break;
        case Level::VERBOSE:
            Log::Dispatch({std::move(message)}, verboseTargets);
            break;
        case Level::NOTICE:
            Log::Dispatch({std::move(message)}, noticeTargets);
            break;
        case Level::WARNING:
            Log::Dispatch({"^3Warn: " + message}, warnTargets);
        }
    }

    template <typename T>
    static T GetCvarOrDie(Str::StringRef cvar) {
        T value;
        std::string valueString = Cvar::GetValue(cvar);
        if (!Cvar::ParseCvarValue(valueString, value)) {
            Sys::Error("Failed to deserialize cvar %s with value: %s", cvar, valueString);
        }
        return value;
    }
    namespace {
        // Log-spam suppression: if more than MAX_OCCURRENCES log messages with the same format string
        // are sent in less than INTERVAL_MS milliseconds, they will stop being printed.
        // (Unless the spammy message desists long enough to be flushed out of the buffer: then it can
        // be printed again.)
        class LogSpamSuppressor {
            struct MessageStatistics {
                std::string messageFormat;
                int numOccurrences;
                int intervalStartTime = -2000000;
            };
            std::vector<MessageStatistics> buf;
            std::mutex mutex;
        public:
            enum Result {
                OK, // not log spam
                LAST_CHANCE, // any more messages after this will be considered log spam
                KNOWN_SPAM
            };
            Result UpdateAndEvaluate(Str::StringRef messageFormat) {
                std::lock_guard<std::mutex> lock(mutex);
                int intervalMs = GetCvarOrDie<int>("logs.suppression.interval");
                int maxOccurrences = GetCvarOrDie<int>("logs.suppression.count");
                int bufferSize = GetCvarOrDie<int>("logs.suppression.bufferSize");
                buf.resize(bufferSize);
                MessageStatistics* oldest = &buf[0];
                int now = Sys::Milliseconds();
                // Search for an existing entry. An entry is considered expired
                // if it is both older than INTERVAL_MS and has less than MAX_OCCURRENCES.
                for (MessageStatistics& stats : buf) {
                    if ((stats.numOccurrences >= maxOccurrences || stats.intervalStartTime > now - intervalMs)
                        && stats.messageFormat == messageFormat) {
                        ++stats.numOccurrences;
                        if (stats.numOccurrences < maxOccurrences) {
                            return OK;
                        }
                        stats.intervalStartTime = now;
                        return stats.numOccurrences == maxOccurrences ? LAST_CHANCE : KNOWN_SPAM;
                    }
                    if (stats.intervalStartTime < oldest->intervalStartTime) {
                        oldest = &stats;
                    }
                }
                // Replace oldest entry if there is not already one for this message
                oldest->intervalStartTime = now;
                oldest->messageFormat = messageFormat;
                oldest->numOccurrences = 1;
                return OK;
            }
        };
    } // namespace

    void DispatchWithSuppression(std::string message, Log::Level level, Str::StringRef format) {
        static LogSpamSuppressor suppressor;
        if (!GetCvarOrDie<bool>("logs.suppression.enabled")) {
            DispatchByLevel(std::move(message), level);
            return;
        }
        switch (suppressor.UpdateAndEvaluate(format)) {
        case LogSpamSuppressor::LAST_CHANCE:
            message += " [further messages like this will be suppressed]";
            DAEMON_FALLTHROUGH;
        case LogSpamSuppressor::OK:
            DispatchByLevel(std::move(message), level);
            break;
        case LogSpamSuppressor::KNOWN_SPAM:
            break;
        }
    }

    void CommandInteractionMessage(std::string message) {
        DispatchByLevel(std::move(message), Log::Level::NOTICE);
    }
} // namespace Log

namespace Cvar {
    template<>
    std::string GetCvarTypeName<Log::Level>() {
        return "log level";
    }
}
<|endoftext|>