<commit_before>//===- DataStructureAA.cpp - Data Structure Based Alias Analysis ----------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass uses the top-down data structure graphs to implement a simple
// context sensitive alias analysis.
//
//===----------------------------------------------------------------------===//

#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/Analysis/DataStructure/DataStructure.h"
#include "llvm/Analysis/DataStructure/DSGraph.h"
using namespace llvm;

namespace {
  class DSAA : public ModulePass, public AliasAnalysis {
    TDDataStructures *TD;
    BUDataStructures *BU;

    // These members are used to cache mod/ref information to make us return
    // results faster, particularly for aa-eval.  On the first request of
    // mod/ref information for a particular call site, we compute and store the
    // calculated nodemap for the call site.  Any time DSA info is updated we
    // free this information, and when we move onto a new call site, this
    // information is also freed.
    CallSite MapCS;
    std::multimap<DSNode*, const DSNode*> CallerCalleeMap;
  public:
    DSAA() : TD(0) {}
    ~DSAA() {
      InvalidateCache();
    }

    void InvalidateCache() {
      MapCS = CallSite();
      CallerCalleeMap.clear();
    }

    //------------------------------------------------
    // Implement the Pass API
    //

    // run - Build up the result graph, representing the pointer graph for the
    // program.
    //
    bool runOnModule(Module &M) {
      InitializeAliasAnalysis(this);
      TD = &getAnalysis<TDDataStructures>();
      BU = &getAnalysis<BUDataStructures>();
      return false;
    }

    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AliasAnalysis::getAnalysisUsage(AU);
      AU.setPreservesAll();                         // Does not transform code
      AU.addRequiredTransitive<TDDataStructures>(); // Uses TD Datastructures
      AU.addRequiredTransitive<BUDataStructures>(); // Uses BU Datastructures
    }

    //------------------------------------------------
    // Implement the AliasAnalysis API
    //

    AliasResult alias(const Value *V1, unsigned V1Size,
                      const Value *V2, unsigned V2Size);

    ModRefResult getModRefInfo(CallSite CS, Value *P, unsigned Size);
    ModRefResult getModRefInfo(CallSite CS1, CallSite CS2) {
      return AliasAnalysis::getModRefInfo(CS1,CS2);
    }

    virtual void deleteValue(Value *V) {
      InvalidateCache();
      BU->deleteValue(V);
      TD->deleteValue(V);
    }

    virtual void copyValue(Value *From, Value *To) {
      if (From == To) return;
      InvalidateCache();
      BU->copyValue(From, To);
      TD->copyValue(From, To);
    }

  private:
    DSGraph *getGraphForValue(const Value *V);
  };

  // Register the pass...
  RegisterOpt<DSAA> X("ds-aa", "Data Structure Graph Based Alias Analysis");

  // Register as an implementation of AliasAnalysis
  RegisterAnalysisGroup<AliasAnalysis, DSAA> Y;
}

ModulePass *llvm::createDSAAPass() { return new DSAA(); }

// getGraphForValue - Return the DSGraph to use for queries about the specified
// value...
//
DSGraph *DSAA::getGraphForValue(const Value *V) {
  if (const Instruction *I = dyn_cast<Instruction>(V))
    return &TD->getDSGraph(*I->getParent()->getParent());
  else if (const Argument *A = dyn_cast<Argument>(V))
    return &TD->getDSGraph(*A->getParent());
  else if (const BasicBlock *BB = dyn_cast<BasicBlock>(V))
    return &TD->getDSGraph(*BB->getParent());
  return 0;
}

AliasAnalysis::AliasResult DSAA::alias(const Value *V1, unsigned V1Size,
                                       const Value *V2, unsigned V2Size) {
  if (V1 == V2) return MustAlias;

  DSGraph *G1 = getGraphForValue(V1);
  DSGraph *G2 = getGraphForValue(V2);
  assert((!G1 || !G2 || G1 == G2) && "Alias query for 2 different functions?");

  // Get the graph to use...
  DSGraph &G = *(G1 ? G1 : (G2 ? G2 : &TD->getGlobalsGraph()));

  const DSGraph::ScalarMapTy &GSM = G.getScalarMap();
  DSGraph::ScalarMapTy::const_iterator I = GSM.find((Value*)V1);
  if (I == GSM.end()) return NoAlias;

  DSGraph::ScalarMapTy::const_iterator J = GSM.find((Value*)V2);
  if (J == GSM.end()) return NoAlias;

  DSNode  *N1 = I->second.getNode(),  *N2 = J->second.getNode();
  unsigned O1 = I->second.getOffset(), O2 = J->second.getOffset();
  if (N1 == 0 || N2 == 0)
    return MayAlias;  // Can't tell whether anything aliases null.

  // We can only make a judgment of one of the nodes is complete...
  if (N1->isComplete() || N2->isComplete()) {
    if (N1 != N2)
      return NoAlias;   // Completely different nodes.

    // See if they point to different offsets...  if so, we may be able to
    // determine that they do not alias...
    if (O1 != O2) {
      if (O2 < O1) {    // Ensure that O1 <= O2
        std::swap(V1, V2);
        std::swap(O1, O2);
        std::swap(V1Size, V2Size);
      }

      if (O1+V1Size <= O2)
        return NoAlias;
    }
  }

  // FIXME: we could improve on this by checking the globals graph for aliased
  // global queries...
  return AliasAnalysis::alias(V1, V1Size, V2, V2Size);
}

/// getModRefInfo - does a callsite modify or reference a value?
///
AliasAnalysis::ModRefResult
DSAA::getModRefInfo(CallSite CS, Value *P, unsigned Size) {
  DSNode *N = 0;
  // First step, check our cache.
  if (CS.getInstruction() == MapCS.getInstruction()) {
    {
      const Function *Caller = CS.getInstruction()->getParent()->getParent();
      DSGraph &CallerTDGraph = TD->getDSGraph(*Caller);

      // Figure out which node in the TD graph this pointer corresponds to.
      DSScalarMap &CallerSM = CallerTDGraph.getScalarMap();
      DSScalarMap::iterator NI = CallerSM.find(P);
      if (NI == CallerSM.end()) {
        InvalidateCache();
        return DSAA::getModRefInfo(CS, P, Size);
      }
      N = NI->second.getNode();
    }

  HaveMappingInfo:
    assert(N && "Null pointer in scalar map??");

    typedef std::multimap<DSNode*, const DSNode*>::iterator NodeMapIt;
    std::pair<NodeMapIt, NodeMapIt> Range = CallerCalleeMap.equal_range(N);

    // Loop over all of the nodes in the callee that correspond to "N", keeping
    // track of aggregate mod/ref info.
    bool NeverReads = true, NeverWrites = true;
    for (; Range.first != Range.second; ++Range.first) {
      if (Range.first->second->isModified())
        NeverWrites = false;
      if (Range.first->second->isRead())
        NeverReads = false;
      if (NeverReads == false && NeverWrites == false)
        return AliasAnalysis::getModRefInfo(CS, P, Size);
    }

    ModRefResult Result = ModRef;
    if (NeverWrites)      // We proved it was not modified.
      Result = ModRefResult(Result & ~Mod);
    if (NeverReads)       // We proved it was not read.
      Result = ModRefResult(Result & ~Ref);

    return ModRefResult(Result & AliasAnalysis::getModRefInfo(CS, P, Size));
  }

  // Any cached info we have is for the wrong function.
  InvalidateCache();

  Function *F = CS.getCalledFunction();

  if (!F) return AliasAnalysis::getModRefInfo(CS, P, Size);

  if (F->isExternal()) {
    // If we are calling an external function, and if this global doesn't escape
    // the portion of the program we have analyzed, we can draw conclusions
    // based on whether the global escapes the program.
    Function *Caller = CS.getInstruction()->getParent()->getParent();
    DSGraph *G = &TD->getDSGraph(*Caller);
    DSScalarMap::iterator NI = G->getScalarMap().find(P);
    if (NI == G->getScalarMap().end()) {
      // If it wasn't in the local function graph, check the global graph.  This
      // can occur for globals who are locally reference but hoisted out to the
      // globals graph despite that.
      G = G->getGlobalsGraph();
      NI = G->getScalarMap().find(P);
      if (NI == G->getScalarMap().end())
        return AliasAnalysis::getModRefInfo(CS, P, Size);
    }

    // If we found a node and it's complete, it cannot be passed out to the
    // called function.
    if (NI->second.getNode()->isComplete())
      return NoModRef;
    return AliasAnalysis::getModRefInfo(CS, P, Size);
  }

  // Get the graphs for the callee and caller.  Note that we want the BU graph
  // for the callee because we don't want all caller's effects incorporated!
  const Function *Caller = CS.getInstruction()->getParent()->getParent();
  DSGraph &CallerTDGraph = TD->getDSGraph(*Caller);
  DSGraph &CalleeBUGraph = BU->getDSGraph(*F);

  // Figure out which node in the TD graph this pointer corresponds to.
  DSScalarMap &CallerSM = CallerTDGraph.getScalarMap();
  DSScalarMap::iterator NI = CallerSM.find(P);
  if (NI == CallerSM.end()) {
    ModRefResult Result = ModRef;
    if (isa<ConstantPointerNull>(P) || isa<UndefValue>(P))
      return NoModRef;                 // null is never modified :)
    else {
      assert(isa<GlobalVariable>(P) &&
    cast<GlobalVariable>(P)->getType()->getElementType()->isFirstClassType() &&
             "This isn't a global that DSA inconsiderately dropped "
             "from the graph?");

      DSGraph &GG = *CallerTDGraph.getGlobalsGraph();
      DSScalarMap::iterator NI = GG.getScalarMap().find(P);
      if (NI != GG.getScalarMap().end() && !NI->second.isNull()) {
        // Otherwise, if the node is only M or R, return this.  This can be
        // useful for globals that should be marked const but are not.
        DSNode *N = NI->second.getNode();
        if (!N->isModified())
          Result = (ModRefResult)(Result & ~Mod);
        if (!N->isRead())
          Result = (ModRefResult)(Result & ~Ref);
      }
    }

    if (Result == NoModRef) return Result;
    return ModRefResult(Result & AliasAnalysis::getModRefInfo(CS, P, Size));
  }

  // Compute the mapping from nodes in the callee graph to the nodes in the
  // caller graph for this call site.
  DSGraph::NodeMapTy CalleeCallerMap;
  DSCallSite DSCS = CallerTDGraph.getDSCallSiteForCallSite(CS);
  CallerTDGraph.computeCalleeCallerMapping(DSCS, *F, CalleeBUGraph,
                                           CalleeCallerMap);

  // Remember the mapping and the call site for future queries.
  MapCS = CS;

  // Invert the mapping into CalleeCallerInvMap.
  for (DSGraph::NodeMapTy::iterator I = CalleeCallerMap.begin(),
         E = CalleeCallerMap.end(); I != E; ++I)
    CallerCalleeMap.insert(std::make_pair(I->second.getNode(), I->first));

  N = NI->second.getNode();
  goto HaveMappingInfo;
}
<commit_msg>Don't give up completely, maybe other AA can say something about this.<commit_after>//===- DataStructureAA.cpp - Data Structure Based Alias Analysis ----------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass uses the top-down data structure graphs to implement a simple
// context sensitive alias analysis.
//
//===----------------------------------------------------------------------===//

#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/Analysis/DataStructure/DataStructure.h"
#include "llvm/Analysis/DataStructure/DSGraph.h"
using namespace llvm;

namespace {
  class DSAA : public ModulePass, public AliasAnalysis {
    TDDataStructures *TD;
    BUDataStructures *BU;

    // These members are used to cache mod/ref information to make us return
    // results faster, particularly for aa-eval.  On the first request of
    // mod/ref information for a particular call site, we compute and store the
    // calculated nodemap for the call site.  Any time DSA info is updated we
    // free this information, and when we move onto a new call site, this
    // information is also freed.
    CallSite MapCS;
    std::multimap<DSNode*, const DSNode*> CallerCalleeMap;
  public:
    DSAA() : TD(0) {}
    ~DSAA() {
      InvalidateCache();
    }

    void InvalidateCache() {
      MapCS = CallSite();
      CallerCalleeMap.clear();
    }

    //------------------------------------------------
    // Implement the Pass API
    //

    // run - Build up the result graph, representing the pointer graph for the
    // program.
    //
    bool runOnModule(Module &M) {
      InitializeAliasAnalysis(this);
      TD = &getAnalysis<TDDataStructures>();
      BU = &getAnalysis<BUDataStructures>();
      return false;
    }

    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AliasAnalysis::getAnalysisUsage(AU);
      AU.setPreservesAll();                         // Does not transform code
      AU.addRequiredTransitive<TDDataStructures>(); // Uses TD Datastructures
      AU.addRequiredTransitive<BUDataStructures>(); // Uses BU Datastructures
    }

    //------------------------------------------------
    // Implement the AliasAnalysis API
    //

    AliasResult alias(const Value *V1, unsigned V1Size,
                      const Value *V2, unsigned V2Size);

    ModRefResult getModRefInfo(CallSite CS, Value *P, unsigned Size);
    ModRefResult getModRefInfo(CallSite CS1, CallSite CS2) {
      return AliasAnalysis::getModRefInfo(CS1,CS2);
    }

    virtual void deleteValue(Value *V) {
      InvalidateCache();
      BU->deleteValue(V);
      TD->deleteValue(V);
    }

    virtual void copyValue(Value *From, Value *To) {
      if (From == To) return;
      InvalidateCache();
      BU->copyValue(From, To);
      TD->copyValue(From, To);
    }

  private:
    DSGraph *getGraphForValue(const Value *V);
  };

  // Register the pass...
  RegisterOpt<DSAA> X("ds-aa", "Data Structure Graph Based Alias Analysis");

  // Register as an implementation of AliasAnalysis
  RegisterAnalysisGroup<AliasAnalysis, DSAA> Y;
}

ModulePass *llvm::createDSAAPass() { return new DSAA(); }

// getGraphForValue - Return the DSGraph to use for queries about the specified
// value...
//
DSGraph *DSAA::getGraphForValue(const Value *V) {
  if (const Instruction *I = dyn_cast<Instruction>(V))
    return &TD->getDSGraph(*I->getParent()->getParent());
  else if (const Argument *A = dyn_cast<Argument>(V))
    return &TD->getDSGraph(*A->getParent());
  else if (const BasicBlock *BB = dyn_cast<BasicBlock>(V))
    return &TD->getDSGraph(*BB->getParent());
  return 0;
}

AliasAnalysis::AliasResult DSAA::alias(const Value *V1, unsigned V1Size,
                                       const Value *V2, unsigned V2Size) {
  if (V1 == V2) return MustAlias;

  DSGraph *G1 = getGraphForValue(V1);
  DSGraph *G2 = getGraphForValue(V2);
  assert((!G1 || !G2 || G1 == G2) && "Alias query for 2 different functions?");

  // Get the graph to use...
  DSGraph &G = *(G1 ? G1 : (G2 ? G2 : &TD->getGlobalsGraph()));

  const DSGraph::ScalarMapTy &GSM = G.getScalarMap();
  DSGraph::ScalarMapTy::const_iterator I = GSM.find((Value*)V1);
  if (I == GSM.end()) return NoAlias;

  DSGraph::ScalarMapTy::const_iterator J = GSM.find((Value*)V2);
  if (J == GSM.end()) return NoAlias;

  DSNode  *N1 = I->second.getNode(),  *N2 = J->second.getNode();
  unsigned O1 = I->second.getOffset(), O2 = J->second.getOffset();
  if (N1 == 0 || N2 == 0)
    // Can't tell whether anything aliases null.
    return AliasAnalysis::alias(V1, V1Size, V2, V2Size);

  // We can only make a judgment of one of the nodes is complete...
  if (N1->isComplete() || N2->isComplete()) {
    if (N1 != N2)
      return NoAlias;   // Completely different nodes.

    // See if they point to different offsets...  if so, we may be able to
    // determine that they do not alias...
    if (O1 != O2) {
      if (O2 < O1) {    // Ensure that O1 <= O2
        std::swap(V1, V2);
        std::swap(O1, O2);
        std::swap(V1Size, V2Size);
      }

      if (O1+V1Size <= O2)
        return NoAlias;
    }
  }

  // FIXME: we could improve on this by checking the globals graph for aliased
  // global queries...
  return AliasAnalysis::alias(V1, V1Size, V2, V2Size);
}

/// getModRefInfo - does a callsite modify or reference a value?
///
AliasAnalysis::ModRefResult
DSAA::getModRefInfo(CallSite CS, Value *P, unsigned Size) {
  DSNode *N = 0;
  // First step, check our cache.
  if (CS.getInstruction() == MapCS.getInstruction()) {
    {
      const Function *Caller = CS.getInstruction()->getParent()->getParent();
      DSGraph &CallerTDGraph = TD->getDSGraph(*Caller);

      // Figure out which node in the TD graph this pointer corresponds to.
      DSScalarMap &CallerSM = CallerTDGraph.getScalarMap();
      DSScalarMap::iterator NI = CallerSM.find(P);
      if (NI == CallerSM.end()) {
        InvalidateCache();
        return DSAA::getModRefInfo(CS, P, Size);
      }
      N = NI->second.getNode();
    }

  HaveMappingInfo:
    assert(N && "Null pointer in scalar map??");

    typedef std::multimap<DSNode*, const DSNode*>::iterator NodeMapIt;
    std::pair<NodeMapIt, NodeMapIt> Range = CallerCalleeMap.equal_range(N);

    // Loop over all of the nodes in the callee that correspond to "N", keeping
    // track of aggregate mod/ref info.
    bool NeverReads = true, NeverWrites = true;
    for (; Range.first != Range.second; ++Range.first) {
      if (Range.first->second->isModified())
        NeverWrites = false;
      if (Range.first->second->isRead())
        NeverReads = false;
      if (NeverReads == false && NeverWrites == false)
        return AliasAnalysis::getModRefInfo(CS, P, Size);
    }

    ModRefResult Result = ModRef;
    if (NeverWrites)      // We proved it was not modified.
      Result = ModRefResult(Result & ~Mod);
    if (NeverReads)       // We proved it was not read.
      Result = ModRefResult(Result & ~Ref);

    return ModRefResult(Result & AliasAnalysis::getModRefInfo(CS, P, Size));
  }

  // Any cached info we have is for the wrong function.
  InvalidateCache();

  Function *F = CS.getCalledFunction();

  if (!F) return AliasAnalysis::getModRefInfo(CS, P, Size);

  if (F->isExternal()) {
    // If we are calling an external function, and if this global doesn't escape
    // the portion of the program we have analyzed, we can draw conclusions
    // based on whether the global escapes the program.
    Function *Caller = CS.getInstruction()->getParent()->getParent();
    DSGraph *G = &TD->getDSGraph(*Caller);
    DSScalarMap::iterator NI = G->getScalarMap().find(P);
    if (NI == G->getScalarMap().end()) {
      // If it wasn't in the local function graph, check the global graph.  This
      // can occur for globals who are locally reference but hoisted out to the
      // globals graph despite that.
      G = G->getGlobalsGraph();
      NI = G->getScalarMap().find(P);
      if (NI == G->getScalarMap().end())
        return AliasAnalysis::getModRefInfo(CS, P, Size);
    }

    // If we found a node and it's complete, it cannot be passed out to the
    // called function.
    if (NI->second.getNode()->isComplete())
      return NoModRef;
    return AliasAnalysis::getModRefInfo(CS, P, Size);
  }

  // Get the graphs for the callee and caller.  Note that we want the BU graph
  // for the callee because we don't want all caller's effects incorporated!
  const Function *Caller = CS.getInstruction()->getParent()->getParent();
  DSGraph &CallerTDGraph = TD->getDSGraph(*Caller);
  DSGraph &CalleeBUGraph = BU->getDSGraph(*F);

  // Figure out which node in the TD graph this pointer corresponds to.
  DSScalarMap &CallerSM = CallerTDGraph.getScalarMap();
  DSScalarMap::iterator NI = CallerSM.find(P);
  if (NI == CallerSM.end()) {
    ModRefResult Result = ModRef;
    if (isa<ConstantPointerNull>(P) || isa<UndefValue>(P))
      return NoModRef;                 // null is never modified :)
    else {
      assert(isa<GlobalVariable>(P) &&
    cast<GlobalVariable>(P)->getType()->getElementType()->isFirstClassType() &&
             "This isn't a global that DSA inconsiderately dropped "
             "from the graph?");

      DSGraph &GG = *CallerTDGraph.getGlobalsGraph();
      DSScalarMap::iterator NI = GG.getScalarMap().find(P);
      if (NI != GG.getScalarMap().end() && !NI->second.isNull()) {
        // Otherwise, if the node is only M or R, return this.  This can be
        // useful for globals that should be marked const but are not.
        DSNode *N = NI->second.getNode();
        if (!N->isModified())
          Result = (ModRefResult)(Result & ~Mod);
        if (!N->isRead())
          Result = (ModRefResult)(Result & ~Ref);
      }
    }

    if (Result == NoModRef) return Result;
    return ModRefResult(Result & AliasAnalysis::getModRefInfo(CS, P, Size));
  }

  // Compute the mapping from nodes in the callee graph to the nodes in the
  // caller graph for this call site.
  DSGraph::NodeMapTy CalleeCallerMap;
  DSCallSite DSCS = CallerTDGraph.getDSCallSiteForCallSite(CS);
  CallerTDGraph.computeCalleeCallerMapping(DSCS, *F, CalleeBUGraph,
                                           CalleeCallerMap);

  // Remember the mapping and the call site for future queries.
  MapCS = CS;

  // Invert the mapping into CalleeCallerInvMap.
  for (DSGraph::NodeMapTy::iterator I = CalleeCallerMap.begin(),
         E = CalleeCallerMap.end(); I != E; ++I)
    CallerCalleeMap.insert(std::make_pair(I->second.getNode(), I->first));

  N = NI->second.getNode();
  goto HaveMappingInfo;
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////

#include "MaxMapCountFeature.h"
#include "ApplicationFeatures/ApplicationServer.h"
#include "Basics/process-utils.h"
#include "Basics/FileUtils.h"
#include "Basics/Mutex.h"
#include "Basics/MutexLocker.h"
#include "Basics/StringBuffer.h"
#include "Basics/StringUtils.h"
#include "Basics/Thread.h"
#include "Logger/Logger.h"
#include "ProgramOptions/ProgramOptions.h"
#include "ProgramOptions/Section.h"

#include <algorithm>

using namespace arangodb;
using namespace arangodb::basics;
using namespace arangodb::options;

namespace {
static bool checkMaxMappings = arangodb::MaxMapCountFeature::needsChecking();
static uint64_t maxMappings = UINT64_MAX;
static std::string mapsFilename;

static StringBuffer fileBuffer(8192, false);

// cache for the current number of mappings for this process
// (read from /proc/<pid>/maps
static Mutex mutex;
static double lastStamp = 0.0;
static bool lastValue = false;
static bool checkInFlight = false;

static constexpr double cacheLifetime = 7.5;

static double lastLogStamp = 0.0;
static constexpr double logFrequency = 10.0;
}

MaxMapCountFeature::MaxMapCountFeature(
    application_features::ApplicationServer* server)
    : ApplicationFeature(server, "MaxMapCount") {
  setOptional(false);
  requiresElevatedPrivileges(false);

  maxMappings = UINT64_MAX;
  mapsFilename.clear();
  lastStamp = 0.0;
  lastValue = false;
  checkInFlight = false;
  lastLogStamp = 0.0;
}

MaxMapCountFeature::~MaxMapCountFeature() {
  // reset values
  maxMappings = UINT64_MAX;
  mapsFilename.clear();
  lastStamp = 0.0;
  lastValue = false;
  checkInFlight = false;
  lastLogStamp = 0.0;
}

void MaxMapCountFeature::collectOptions(std::shared_ptr<options::ProgramOptions> options) {
  options->addSection("server", "Server Options");

  if (needsChecking()) {
    options->addHiddenOption("--server.check-max-memory-mappings, mappings", "check the maximum number of memory mappings at runtime",
                             new BooleanParameter(&checkMaxMappings));
  } else {
    options->addObsoleteOption("--server.check-max-memory-mappings", "check the maximum number of memory mappings at runtime", true);
  }
}

void MaxMapCountFeature::prepare() {
  if (!needsChecking() || !checkMaxMappings) {
    return;
  }

  mapsFilename = "/proc/" + std::to_string(Thread::currentProcessId()) + "/maps";

  if (!FileUtils::exists(mapsFilename)) {
    mapsFilename.clear();
  } else {
    try {
      basics::FileUtils::slurp(mapsFilename);
    } catch (...) {
      // maps file not readable
      mapsFilename.clear();
    }
  }

  LOG_TOPIC(DEBUG, Logger::MEMORY) << "using process maps filename '" << mapsFilename << "'";

  // in case we cannot determine the number of max_map_count, we will
  // assume an effectively unlimited number of mappings
  TRI_ASSERT(maxMappings == UINT64_MAX);

#ifdef __linux__
  // test max_map_count value in /proc/sys/vm
  try {
    std::string value =
        basics::FileUtils::slurp("/proc/sys/vm/max_map_count");

    maxMappings = basics::StringUtils::uint64(value);
  } catch (...) {
    // file not found or values not convertible into integers
  }
#endif
}

uint64_t MaxMapCountFeature::actualMaxMappings() { return maxMappings; }

uint64_t MaxMapCountFeature::minimumExpectedMaxMappings() {
  TRI_ASSERT(needsChecking());

  uint64_t expected = 65530; // kernel default

  uint64_t nproc = TRI_numberProcessors();

  // we expect at most 8 times the number of cores as the effective number of threads,
  // and we want to allow at least 8000 mmaps per thread
  if (nproc * 8 * 8000 > expected) {
    expected = nproc * 8 * 8000;
  }

  return expected;
}

bool MaxMapCountFeature::isNearMaxMappings() {
  if (!needsChecking() || !checkMaxMappings || mapsFilename.empty()) {
    return false;
  }

  double const now = TRI_microtime();

  {
    // we do not want any cache stampede
    MUTEX_LOCKER(locker, mutex);
    if (lastStamp >= now || checkInFlight) {
      // serve value from cache
      return lastValue;
    }

    checkInFlight = true;
  }

  // check current maps count without holding the mutex
  double cacheTime;
  bool const value = isNearMaxMappingsInternal(cacheTime);

  {
    // update cache
    MUTEX_LOCKER(locker, mutex);
    lastValue = value;
    lastStamp = now + cacheTime;
    checkInFlight = false;
  }

  return value;
}

bool MaxMapCountFeature::isNearMaxMappingsInternal(double& suggestedCacheTime) noexcept {
  try {
    // recycle the same buffer for reading the maps file
    basics::FileUtils::slurp(mapsFilename, fileBuffer);

    size_t const nmaps = std::count(fileBuffer.begin(), fileBuffer.end(), '\n');
    if (nmaps + 1024 < maxMappings) {
      if (nmaps > maxMappings * 0.90) {
        // more than 90% of the max mappings are in use. don't cache for too long
        suggestedCacheTime = 0.001;
      } else if (nmaps >= maxMappings / 2.0) {
        // we're above half of the max mappings. reduce cache time a bit
        suggestedCacheTime = cacheLifetime / 2.0;
      } else {
        suggestedCacheTime = cacheLifetime;
      }
      return false;
    }
    // we're near the maximum number of mappings. don't cache for too long
    suggestedCacheTime = 0.001;

    double now = TRI_microtime();
    if (lastLogStamp + logFrequency < now) {
      // do not log too often to avoid log spamming
      lastLogStamp = now;
      LOG_TOPIC(WARN, Logger::MEMORY) << "process is near the maximum number of memory mappings. current: " << nmaps << ", maximum: " << maxMappings
                                      << ". it may be sensible to increase the maximum number of mappings per process";
    }
    return true;
  } catch (...) {
    // something went wrong. don't cache for too long
    suggestedCacheTime = 0.001;
    return false;
  }
}
<commit_msg>increase message level to ERR for max mappings (#3426)<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////

#include "MaxMapCountFeature.h"
#include "ApplicationFeatures/ApplicationServer.h"
#include "Basics/process-utils.h"
#include "Basics/FileUtils.h"
#include "Basics/Mutex.h"
#include "Basics/MutexLocker.h"
#include "Basics/StringBuffer.h"
#include "Basics/StringUtils.h"
#include "Basics/Thread.h"
#include "Logger/Logger.h"
#include "ProgramOptions/ProgramOptions.h"
#include "ProgramOptions/Section.h"

#include <algorithm>

using namespace arangodb;
using namespace arangodb::basics;
using namespace arangodb::options;

namespace {
static bool checkMaxMappings = arangodb::MaxMapCountFeature::needsChecking();
static uint64_t maxMappings = UINT64_MAX;
static std::string mapsFilename;

static StringBuffer fileBuffer(8192, false);

// cache for the current number of mappings for this process
// (read from /proc/<pid>/maps
static Mutex mutex;
static double lastStamp = 0.0;
static bool lastValue = false;
static bool checkInFlight = false;

static constexpr double cacheLifetime = 7.5;

static double lastLogStamp = 0.0;
static constexpr double logFrequency = 10.0;
}

MaxMapCountFeature::MaxMapCountFeature(
    application_features::ApplicationServer* server)
    : ApplicationFeature(server, "MaxMapCount") {
  setOptional(false);
  requiresElevatedPrivileges(false);

  maxMappings = UINT64_MAX;
  mapsFilename.clear();
  lastStamp = 0.0;
  lastValue = false;
  checkInFlight = false;
  lastLogStamp = 0.0;
}

MaxMapCountFeature::~MaxMapCountFeature() {
  // reset values
  maxMappings = UINT64_MAX;
  mapsFilename.clear();
  lastStamp = 0.0;
  lastValue = false;
  checkInFlight = false;
  lastLogStamp = 0.0;
}

void MaxMapCountFeature::collectOptions(std::shared_ptr<options::ProgramOptions> options) {
  options->addSection("server", "Server Options");

  if (needsChecking()) {
    options->addHiddenOption("--server.check-max-memory-mappings, mappings", "check the maximum number of memory mappings at runtime",
                             new BooleanParameter(&checkMaxMappings));
  } else {
    options->addObsoleteOption("--server.check-max-memory-mappings", "check the maximum number of memory mappings at runtime", true);
  }
}

void MaxMapCountFeature::prepare() {
  if (!needsChecking() || !checkMaxMappings) {
    return;
  }

  mapsFilename = "/proc/" + std::to_string(Thread::currentProcessId()) + "/maps";

  if (!FileUtils::exists(mapsFilename)) {
    mapsFilename.clear();
  } else {
    try {
      basics::FileUtils::slurp(mapsFilename);
    } catch (...) {
      // maps file not readable
      mapsFilename.clear();
    }
  }

  LOG_TOPIC(DEBUG, Logger::MEMORY) << "using process maps filename '" << mapsFilename << "'";

  // in case we cannot determine the number of max_map_count, we will
  // assume an effectively unlimited number of mappings
  TRI_ASSERT(maxMappings == UINT64_MAX);

#ifdef __linux__
  // test max_map_count value in /proc/sys/vm
  try {
    std::string value =
        basics::FileUtils::slurp("/proc/sys/vm/max_map_count");

    maxMappings = basics::StringUtils::uint64(value);
  } catch (...) {
    // file not found or values not convertible into integers
  }
#endif
}

uint64_t MaxMapCountFeature::actualMaxMappings() { return maxMappings; }

uint64_t MaxMapCountFeature::minimumExpectedMaxMappings() {
  TRI_ASSERT(needsChecking());

  uint64_t expected = 65530; // kernel default

  uint64_t nproc = TRI_numberProcessors();

  // we expect at most 8 times the number of cores as the effective number of threads,
  // and we want to allow at least 8000 mmaps per thread
  if (nproc * 8 * 8000 > expected) {
    expected = nproc * 8 * 8000;
  }

  return expected;
}

bool MaxMapCountFeature::isNearMaxMappings() {
  if (!needsChecking() || !checkMaxMappings || mapsFilename.empty()) {
    return false;
  }

  double const now = TRI_microtime();

  {
    // we do not want any cache stampede
    MUTEX_LOCKER(locker, mutex);
    if (lastStamp >= now || checkInFlight) {
      // serve value from cache
      return lastValue;
    }

    checkInFlight = true;
  }

  // check current maps count without holding the mutex
  double cacheTime;
  bool const value = isNearMaxMappingsInternal(cacheTime);

  {
    // update cache
    MUTEX_LOCKER(locker, mutex);
    lastValue = value;
    lastStamp = now + cacheTime;
    checkInFlight = false;
  }

  return value;
}

bool MaxMapCountFeature::isNearMaxMappingsInternal(double& suggestedCacheTime) noexcept {
  try {
    // recycle the same buffer for reading the maps file
    basics::FileUtils::slurp(mapsFilename, fileBuffer);

    size_t const nmaps = std::count(fileBuffer.begin(), fileBuffer.end(), '\n');
    if (nmaps + 1024 < maxMappings) {
      if (nmaps > maxMappings * 0.90) {
        // more than 90% of the max mappings are in use. don't cache for too long
        suggestedCacheTime = 0.001;
      } else if (nmaps >= maxMappings / 2.0) {
        // we're above half of the max mappings. reduce cache time a bit
        suggestedCacheTime = cacheLifetime / 2.0;
      } else {
        suggestedCacheTime = cacheLifetime;
      }
      return false;
    }
    // we're near the maximum number of mappings. don't cache for too long
    suggestedCacheTime = 0.001;

    double now = TRI_microtime();
    if (lastLogStamp + logFrequency < now) {
      // do not log too often to avoid log spamming
      lastLogStamp = now;
      LOG_TOPIC(ERR, Logger::MEMORY) << "process is near the maximum number of memory mappings. current: " << nmaps << ", maximum: " << maxMappings
                                     << ". it may be sensible to increase the maximum number of mappings per process";
    }
    return true;
  } catch (...) {
    // something went wrong. don't cache for too long
    suggestedCacheTime = 0.001;
    return false;
  }
}
<|endoftext|>


<commit_before>/*
This file is part of HadesMem.
Copyright  2010 RaptorFactor (aka Cypherjb, Cypher, Chazwazza).
<http://www.raptorfactor.com/> <raptorfactor@raptorfactor.com>

HadesMem 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.

HadesMem 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 HadesMem.  If not, see <http://www.gnu.org/licenses/>.
*/

// C++ Standard Library
#include <fstream>
#include <sstream>

// Boost
#pragma warning(push, 1)
#include <boost/lexical_cast.hpp>
#pragma warning(pop)

// RapidXML
#pragma warning(push, 1)
#include <RapidXML/rapidxml.hpp>
#pragma warning(pop)

// Hades
#include "PeFile.h"
#include "Module.h"
#include "Scanner.h"
#include "DosHeader.h"
#include "NtHeaders.h"
#include "FindPattern.h"
#include "Hades-Common/I18n.h"

namespace Hades
{
  namespace Memory
  {
    // Constructor
    FindPattern::FindPattern(MemoryMgr const& MyMemory)
      : m_Memory(MyMemory),
      m_Start(nullptr),
      m_End(nullptr),
      m_Addresses()
    {
      // Get pointer to image headers
      ModuleListIter ModIter(m_Memory);
      PBYTE const pBase = reinterpret_cast<PBYTE>((*ModIter)->GetBase());
      PeFile MyPeFile(m_Memory, pBase);
      DosHeader const MyDosHeader(MyPeFile);
      NtHeaders const MyNtHeaders(MyPeFile);

      // Get base of code section
      m_Start = pBase + MyNtHeaders.GetBaseOfCode();

      // Calculate end of code section
      m_End = m_Start + MyNtHeaders.GetSizeOfCode();
    }

    // Constructor
    FindPattern::FindPattern(MemoryMgr const& MyMemory, HMODULE Module)
      : m_Memory(MyMemory),
      m_Start(nullptr),
      m_End(nullptr),
      m_Addresses()
    {
      // Ensure file is a valid PE file
      PBYTE const pBase = reinterpret_cast<PBYTE>(Module);
      PeFile MyPeFile(m_Memory, pBase);
      DosHeader const MyDosHeader(MyPeFile);
      NtHeaders const MyNtHeaders(MyPeFile);

      // Get base of code section
      m_Start = pBase + MyNtHeaders.GetBaseOfCode();

      // Calculate end of code section
      m_End = m_Start + MyNtHeaders.GetSizeOfCode();
    }

    // Find pattern
    PVOID FindPattern::Find(std::basic_string<TCHAR> const& Data,
      std::basic_string<TCHAR> const& Mask) const
    {
      // Ensure pattern attributes are valid
      if (Data.empty() || Mask.empty())
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::Find") <<
          ErrorString("Empty pattern or mask data."));
      }

      // Ensure data is valid
      if (Data.size() % 2)
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::Find") <<
          ErrorString("Data size invalid."));
      }

      // Ensure mask is valid
      if (Mask.size() * 2 != Data.size())
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::Find") <<
          ErrorString("Mask size invalid."));
      }

      // Convert data to byte buffer
      std::vector<std::pair<BYTE, bool>> DataBuf;
      for (auto i = Data.cbegin(), j = Mask.cbegin(); i != Data.cend();
        i += 2, ++j)
      {
        std::basic_string<TCHAR> const CurrentStr(i, i + 2);
        std::basic_stringstream<TCHAR> Converter(CurrentStr);
        int Current(0);
        if (!(Converter >> std::hex >> Current >> std::dec))
        {
          BOOST_THROW_EXCEPTION(Error() <<
            ErrorFunction("FindPattern::Find") <<
            ErrorString("Invalid data conversion."));
        }

        BYTE CurrentReal = static_cast<BYTE>(Current);
        bool MaskFlag = *j == _T('x');

        DataBuf.push_back(std::make_pair(CurrentReal, MaskFlag));
      }

      // Search memory for pattern
      return Find(DataBuf);
    }

    // Search memory
    // Note: Previous implementation used modified Boyer-Moore-Horspool, but
    // this was dropped as the C++ 'search' algorithm provided with MSVC
    // performs equally if not slightly better than my custom search.
    PVOID FindPattern::Find(std::vector<std::pair<BYTE, bool>> const& Data)
      const
    {
      // Cache all memory to be scanned
      std::size_t MemSize = m_End - m_Start;
      std::vector<BYTE> Buffer(m_Memory.Read<std::vector<BYTE>>(m_Start,
        MemSize));

      // Scan memory
      auto Iter = std::search(Buffer.begin(), Buffer.end(), Data.begin(),
        Data.end(),
        [&] (BYTE HCur, std::pair<BYTE, bool> NCur)
      {
        return (!NCur.second) || (HCur == NCur.first);
      });

      // Return address if found or null if not found
      return
        (Iter != Buffer.end())
        ? (m_Start + std::distance(Buffer.begin(), Iter))
        : nullptr;
    }

    // Load patterns from XML file
    void FindPattern::LoadFromXML(boost::filesystem::path const& Path)
    {
      // Open current file
      std::wifstream PatternFile(Path.string<std::basic_string<TCHAR>>().
        c_str());
      if (!PatternFile)
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::LoadFromXML") <<
          ErrorString("Could not open pattern file."));
      }

      // Copy file to buffer
      std::istreambuf_iterator<wchar_t> const PatFileBeg(PatternFile);
      std::istreambuf_iterator<wchar_t> const PatFileEnd;
      std::vector<wchar_t> PatFileBuf(PatFileBeg, PatFileEnd);
      PatFileBuf.push_back(L'\0');

      // Open XML document
      std::shared_ptr<rapidxml::xml_document<wchar_t>> const AccountsDoc(
        std::make_shared<rapidxml::xml_document<wchar_t>>());
      AccountsDoc->parse<0>(&PatFileBuf[0]);

      // Ensure pattern tag is found
      rapidxml::xml_node<wchar_t>* PatternsTag = AccountsDoc->first_node(
        L"Patterns");
      if (!PatternsTag)
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::LoadFromXML") <<
          ErrorString("Invalid pattern file format."));
      }

      // Loop over all patterns
      for (rapidxml::xml_node<wchar_t>* Pattern(PatternsTag->first_node(
        L"Pattern")); Pattern; Pattern = Pattern->next_sibling(L"Pattern"))
      {
        // Get pattern attributes
        rapidxml::xml_attribute<wchar_t> const* NameNode = Pattern->
          first_attribute(L"Name");
        rapidxml::xml_attribute<wchar_t> const* MaskNode = Pattern->
          first_attribute(L"Mask");
        rapidxml::xml_attribute<wchar_t> const* DataNode = Pattern->
          first_attribute(L"Data");
        std::wstring const Name(NameNode ? NameNode->value() : L"");
        std::wstring const Mask(MaskNode ? MaskNode->value() : L"");
        std::wstring const Data(DataNode ? DataNode->value() : L"");
        std::string const DataReal(boost::lexical_cast<std::string>(Data));

        // Ensure pattern attributes are valid
        if (Name.empty())
        {
          BOOST_THROW_EXCEPTION(Error() <<
            ErrorFunction("FindPattern::LoadFromXML") <<
            ErrorString("Empty pattern name."));
        }

        // Find pattern
        PBYTE Address = static_cast<PBYTE>(Find(
          boost::lexical_cast<std::basic_string<TCHAR>>(Data),
          boost::lexical_cast<std::basic_string<TCHAR>>(Mask)));

        // Only apply options if pattern was found
        if (Address != 0)
        {
          // Loop over all pattern options
          for (rapidxml::xml_node<wchar_t> const* PatOpts = Pattern->
            first_node(); PatOpts; PatOpts = PatOpts->next_sibling())
          {
            // Get option name
            std::wstring const OptionName(PatOpts->name());

            // Handle 'Add' and 'Sub' options
            bool const IsAdd = (OptionName == L"Add");
            bool const IsSub = (OptionName == L"Sub");
            if (IsAdd || IsSub)
            {
              // Get the modification value
              rapidxml::xml_attribute<wchar_t> const* ModVal = PatOpts->
                first_attribute(L"Value");
              if (!ModVal)
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("No value specified for 'Add' option."));
              }

              // Convert value to usable form
              std::wstringstream Converter(ModVal->value());
              DWORD_PTR AddValReal = 0;
              if (!(Converter >> std::hex >> AddValReal >> std::dec))
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("Invalid conversion for 'Add' option."));
              }

              // Perform modification
              if (IsAdd)
              {
                Address += AddValReal;
              }
              else if (IsSub)
              {
                Address -= AddValReal;
              }
              else
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("Unsupported pattern option."));
              }
            }
            // Handle 'Lea' option (abs deref)
            else if (OptionName == L"Lea")
            {
              // Perform absolute 'dereference'
              Address = m_Memory.Read<PBYTE>(Address);
            }
            // Handle 'Rel' option (rel deref)
            else if (OptionName == L"Rel")
            {
              // Get instruction size
              rapidxml::xml_attribute<wchar_t> const* SizeAttr = PatOpts->
                first_attribute(L"Size");
              if (!SizeAttr)
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("No size specified for 'Size' in 'Rel' "
                  "option."));
              }

              // Convert instruction size to usable format
              std::wstringstream SizeConverter(SizeAttr->value());
              DWORD_PTR Size(0);
              if (!(SizeConverter >> std::hex >> Size >> std::dec))
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("Invalid conversion for 'Size' in 'Rel' "
                  "option."));
              }

              // Get instruction offset
              rapidxml::xml_attribute<wchar_t> const* OffsetAttr = PatOpts->
                first_attribute(L"Offset");
              if (!OffsetAttr)
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("No value specified for 'Offset' in 'Rel' "
                  "option."));
              }

              // Convert instruction offset to usable format
              std::wstringstream OffsetConverter(OffsetAttr->value());
              DWORD_PTR Offset(0);
              if (!(OffsetConverter >> std::hex >> Offset >> std::dec))
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("Invalid conversion for 'Offset' in 'Rel' "
                  "option."));
              }

              // Perform relative 'dereference'
              Address = m_Memory.Read<PBYTE>(Address) +
                reinterpret_cast<DWORD_PTR>(Address) + Size - Offset;
            }
            else
            {
              // Unknown pattern option
              BOOST_THROW_EXCEPTION(Error() <<
                ErrorFunction("FindPattern::LoadFromXML") <<
                ErrorString("Unknown pattern option."));
            }
          }
        }

        // Check for duplicate entry
        auto const Iter = m_Addresses.find(boost::lexical_cast<std::
          basic_string<TCHAR>>(Name));
        if (Iter != m_Addresses.end())
        {
          BOOST_THROW_EXCEPTION(Error() <<
            ErrorFunction("FindPattern::LoadFromXML") <<
            ErrorString("Duplicate pattern name."));
        }

        // Add address to map
        m_Addresses[boost::lexical_cast<std::basic_string<TCHAR>>(Name)] =
          Address;
      }
    }

    // Get address map
    std::map<std::basic_string<TCHAR>, PVOID> FindPattern::GetAddresses() const
    {
      return m_Addresses;
    }

    // Operator[] overload to allow retrieving addresses by name
    PVOID FindPattern::operator[](std::basic_string<TCHAR> const& Name) const
    {
      auto const Iter = m_Addresses.find(Name);
      return Iter != m_Addresses.end() ? Iter->second : nullptr;
    }
  }
}
<commit_msg>* Started FindPattern 'rewrite'.<commit_after>/*
This file is part of HadesMem.
Copyright  2010 RaptorFactor (aka Cypherjb, Cypher, Chazwazza).
<http://www.raptorfactor.com/> <raptorfactor@raptorfactor.com>

HadesMem 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.

HadesMem 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 HadesMem.  If not, see <http://www.gnu.org/licenses/>.
*/

// C++ Standard Library
#include <fstream>
#include <sstream>

// Boost
#pragma warning(push, 1)
#include <boost/lexical_cast.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support.hpp>
#pragma warning(pop)

// RapidXML
#pragma warning(push, 1)
#include <RapidXML/rapidxml.hpp>
#pragma warning(pop)

// Hades
#include "PeFile.h"
#include "Module.h"
#include "Scanner.h"
#include "DosHeader.h"
#include "NtHeaders.h"
#include "FindPattern.h"
#include "Hades-Common/I18n.h"

namespace Hades
{
  namespace Memory
  {
    // Constructor
    FindPattern::FindPattern(MemoryMgr const& MyMemory)
      : m_Memory(MyMemory),
      m_Start(nullptr),
      m_End(nullptr),
      m_Addresses()
    {
      // Get pointer to image headers
      ModuleListIter ModIter(m_Memory);
      PBYTE const pBase = reinterpret_cast<PBYTE>((*ModIter)->GetBase());
      PeFile MyPeFile(m_Memory, pBase);
      DosHeader const MyDosHeader(MyPeFile);
      NtHeaders const MyNtHeaders(MyPeFile);

      // Get base of code section
      m_Start = pBase + MyNtHeaders.GetBaseOfCode();

      // Calculate end of code section
      m_End = m_Start + MyNtHeaders.GetSizeOfCode();
    }

    // Constructor
    FindPattern::FindPattern(MemoryMgr const& MyMemory, HMODULE Module)
      : m_Memory(MyMemory),
      m_Start(nullptr),
      m_End(nullptr),
      m_Addresses()
    {
      // Ensure file is a valid PE file
      PBYTE const pBase = reinterpret_cast<PBYTE>(Module);
      PeFile MyPeFile(m_Memory, pBase);
      DosHeader const MyDosHeader(MyPeFile);
      NtHeaders const MyNtHeaders(MyPeFile);

      // Get base of code section
      m_Start = pBase + MyNtHeaders.GetBaseOfCode();

      // Calculate end of code section
      m_End = m_Start + MyNtHeaders.GetSizeOfCode();
    }

    // Find pattern
    PVOID FindPattern::Find(std::basic_string<TCHAR> const& Data,
      std::basic_string<TCHAR> const& Mask) const
    {
      // Ensure pattern attributes are valid
      if (Data.empty() || Mask.empty())
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::Find") <<
          ErrorString("Empty pattern or mask data."));
      }

      // Convert data to byte buffer
      // Todo: Ensure each 'byte' is two characters long
      std::vector<BYTE> Bytes;
      auto DataBeg = Data.cbegin();
      auto DataEnd = Data.cend();
      if (!boost::spirit::qi::phrase_parse(DataBeg, DataEnd,
        *boost::spirit::qi::hex, boost::spirit::qi::space, Bytes)
        || DataBeg != DataEnd)
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::Find") <<
          ErrorString("Failed to parse bytes."));
      }

      // Convert mask to bit flag
      std::vector<bool> MaskFlags;
      auto MaskBeg = Mask.cbegin();
      auto MaskEnd = Mask.cend();
      if (!boost::spirit::qi::parse(MaskBeg, MaskEnd,
        *((boost::spirit::qi::lit("x") >> boost::spirit::qi::attr(true)) |
        (boost::spirit::qi::lit("?") >> boost::spirit::qi::attr(false))),
        MaskFlags) || MaskBeg != MaskEnd)
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::Find") <<
          ErrorString("Failed to parse mask."));
      }

      // Sanity check
      if (Bytes.size() != MaskFlags.size())
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::Find") <<
          ErrorString("Parsed data differs in size to parsed mask."));
      }

      // Convert to required format
      // Todo: Do this as part of the parsing process
      std::vector<std::pair<BYTE, bool>> DataBuf;
      for (std::size_t i = 0; i != Bytes.size(); ++i)
      {
        DataBuf.push_back(std::make_pair(Bytes[i], MaskFlags[i]));
      }

      // Search memory for pattern
      return Find(DataBuf);
    }

    // Search memory
    // Note: Previous implementation used modified Boyer-Moore-Horspool, but
    // this was dropped as the C++ 'search' algorithm provided with MSVC
    // performs equally if not slightly better than my custom search.
    PVOID FindPattern::Find(std::vector<std::pair<BYTE, bool>> const& Data)
      const
    {
      // Cache all memory to be scanned
      std::size_t MemSize = m_End - m_Start;
      std::vector<BYTE> Buffer(m_Memory.Read<std::vector<BYTE>>(m_Start,
        MemSize));

      // Scan memory
      auto Iter = std::search(Buffer.begin(), Buffer.end(), Data.begin(),
        Data.end(),
        [&] (BYTE HCur, std::pair<BYTE, bool> NCur)
      {
        return (!NCur.second) || (HCur == NCur.first);
      });

      // Return address if found or null if not found
      return
        (Iter != Buffer.end())
        ? (m_Start + std::distance(Buffer.begin(), Iter))
        : nullptr;
    }

    // Load patterns from XML file
    void FindPattern::LoadFromXML(boost::filesystem::path const& Path)
    {
      // Open current file
      std::wifstream PatternFile(Path.string<std::basic_string<TCHAR>>().
        c_str());
      if (!PatternFile)
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::LoadFromXML") <<
          ErrorString("Could not open pattern file."));
      }

      // Copy file to buffer
      std::istreambuf_iterator<wchar_t> const PatFileBeg(PatternFile);
      std::istreambuf_iterator<wchar_t> const PatFileEnd;
      std::vector<wchar_t> PatFileBuf(PatFileBeg, PatFileEnd);
      PatFileBuf.push_back(L'\0');

      // Open XML document
      std::shared_ptr<rapidxml::xml_document<wchar_t>> const AccountsDoc(
        std::make_shared<rapidxml::xml_document<wchar_t>>());
      AccountsDoc->parse<0>(&PatFileBuf[0]);

      // Ensure pattern tag is found
      rapidxml::xml_node<wchar_t>* PatternsTag = AccountsDoc->first_node(
        L"Patterns");
      if (!PatternsTag)
      {
        BOOST_THROW_EXCEPTION(Error() <<
          ErrorFunction("FindPattern::LoadFromXML") <<
          ErrorString("Invalid pattern file format."));
      }

      // Loop over all patterns
      for (rapidxml::xml_node<wchar_t>* Pattern(PatternsTag->first_node(
        L"Pattern")); Pattern; Pattern = Pattern->next_sibling(L"Pattern"))
      {
        // Get pattern attributes
        rapidxml::xml_attribute<wchar_t> const* NameNode = Pattern->
          first_attribute(L"Name");
        rapidxml::xml_attribute<wchar_t> const* MaskNode = Pattern->
          first_attribute(L"Mask");
        rapidxml::xml_attribute<wchar_t> const* DataNode = Pattern->
          first_attribute(L"Data");
        std::wstring const Name(NameNode ? NameNode->value() : L"");
        std::wstring const Mask(MaskNode ? MaskNode->value() : L"");
        std::wstring const Data(DataNode ? DataNode->value() : L"");
        std::string const DataReal(boost::lexical_cast<std::string>(Data));

        // Ensure pattern attributes are valid
        if (Name.empty())
        {
          BOOST_THROW_EXCEPTION(Error() <<
            ErrorFunction("FindPattern::LoadFromXML") <<
            ErrorString("Empty pattern name."));
        }

        // Find pattern
        PBYTE Address = static_cast<PBYTE>(Find(
          boost::lexical_cast<std::basic_string<TCHAR>>(Data),
          boost::lexical_cast<std::basic_string<TCHAR>>(Mask)));

        // Only apply options if pattern was found
        if (Address != 0)
        {
          // Loop over all pattern options
          for (rapidxml::xml_node<wchar_t> const* PatOpts = Pattern->
            first_node(); PatOpts; PatOpts = PatOpts->next_sibling())
          {
            // Get option name
            std::wstring const OptionName(PatOpts->name());

            // Handle 'Add' and 'Sub' options
            bool const IsAdd = (OptionName == L"Add");
            bool const IsSub = (OptionName == L"Sub");
            if (IsAdd || IsSub)
            {
              // Get the modification value
              rapidxml::xml_attribute<wchar_t> const* ModVal = PatOpts->
                first_attribute(L"Value");
              if (!ModVal)
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("No value specified for 'Add' option."));
              }

              // Convert value to usable form
              std::wstringstream Converter(ModVal->value());
              DWORD_PTR AddValReal = 0;
              if (!(Converter >> std::hex >> AddValReal >> std::dec))
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("Invalid conversion for 'Add' option."));
              }

              // Perform modification
              if (IsAdd)
              {
                Address += AddValReal;
              }
              else if (IsSub)
              {
                Address -= AddValReal;
              }
              else
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("Unsupported pattern option."));
              }
            }
            // Handle 'Lea' option (abs deref)
            else if (OptionName == L"Lea")
            {
              // Perform absolute 'dereference'
              Address = m_Memory.Read<PBYTE>(Address);
            }
            // Handle 'Rel' option (rel deref)
            else if (OptionName == L"Rel")
            {
              // Get instruction size
              rapidxml::xml_attribute<wchar_t> const* SizeAttr = PatOpts->
                first_attribute(L"Size");
              if (!SizeAttr)
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("No size specified for 'Size' in 'Rel' "
                  "option."));
              }

              // Convert instruction size to usable format
              std::wstringstream SizeConverter(SizeAttr->value());
              DWORD_PTR Size(0);
              if (!(SizeConverter >> std::hex >> Size >> std::dec))
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("Invalid conversion for 'Size' in 'Rel' "
                  "option."));
              }

              // Get instruction offset
              rapidxml::xml_attribute<wchar_t> const* OffsetAttr = PatOpts->
                first_attribute(L"Offset");
              if (!OffsetAttr)
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("No value specified for 'Offset' in 'Rel' "
                  "option."));
              }

              // Convert instruction offset to usable format
              std::wstringstream OffsetConverter(OffsetAttr->value());
              DWORD_PTR Offset(0);
              if (!(OffsetConverter >> std::hex >> Offset >> std::dec))
              {
                BOOST_THROW_EXCEPTION(Error() <<
                  ErrorFunction("FindPattern::LoadFromXML") <<
                  ErrorString("Invalid conversion for 'Offset' in 'Rel' "
                  "option."));
              }

              // Perform relative 'dereference'
              Address = m_Memory.Read<PBYTE>(Address) +
                reinterpret_cast<DWORD_PTR>(Address) + Size - Offset;
            }
            else
            {
              // Unknown pattern option
              BOOST_THROW_EXCEPTION(Error() <<
                ErrorFunction("FindPattern::LoadFromXML") <<
                ErrorString("Unknown pattern option."));
            }
          }
        }

        // Check for duplicate entry
        auto const Iter = m_Addresses.find(boost::lexical_cast<std::
          basic_string<TCHAR>>(Name));
        if (Iter != m_Addresses.end())
        {
          BOOST_THROW_EXCEPTION(Error() <<
            ErrorFunction("FindPattern::LoadFromXML") <<
            ErrorString("Duplicate pattern name."));
        }

        // Add address to map
        m_Addresses[boost::lexical_cast<std::basic_string<TCHAR>>(Name)] =
          Address;
      }
    }

    // Get address map
    std::map<std::basic_string<TCHAR>, PVOID> FindPattern::GetAddresses() const
    {
      return m_Addresses;
    }

    // Operator[] overload to allow retrieving addresses by name
    PVOID FindPattern::operator[](std::basic_string<TCHAR> const& Name) const
    {
      auto const Iter = m_Addresses.find(Name);
      return Iter != m_Addresses.end() ? Iter->second : nullptr;
    }
  }
}
<|endoftext|>


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

#define V8_USE_UNSAFE_HANDLES

#include "content/nw/src/api/dispatcher.h"

#include "content/nw/src/api/api_messages.h"
#include "content/public/renderer/render_view.h"
#include "content/renderer/v8_value_converter_impl.h"
#include "third_party/node/src/node.h"
#undef CHECK
#include "third_party/node/src/req_wrap.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "v8/include/v8.h"

#undef LOG
#undef ASSERT
#undef FROM_HERE

#if defined(OS_WIN)
#define _USE_MATH_DEFINES
#include <math.h>
#endif
#include "third_party/WebKit/Source/config.h"
#include "third_party/WebKit/Source/core/frame/Frame.h"
#include "third_party/WebKit/Source/web/WebFrameImpl.h"
#include "V8HTMLElement.h"

namespace nwapi {

static inline v8::Local<v8::String> v8_str(const char* x) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  return v8::String::NewFromUtf8(isolate, x);
}

Dispatcher::Dispatcher(content::RenderView* render_view)
    : content::RenderViewObserver(render_view) {
}

Dispatcher::~Dispatcher() {
}

bool Dispatcher::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(Dispatcher, message)
    IPC_MESSAGE_HANDLER(ShellViewMsg_Object_On_Event, OnEvent)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()

  return handled;
}

void Dispatcher::DraggableRegionsChanged(blink::WebFrame* frame) {
  blink::WebVector<blink::WebDraggableRegion> webregions =
      frame->document().draggableRegions();
  std::vector<extensions::DraggableRegion> regions;
  for (size_t i = 0; i < webregions.size(); ++i) {
    extensions::DraggableRegion region;
    region.bounds = webregions[i].bounds;
    region.draggable = webregions[i].draggable;
    regions.push_back(region);
  }
  Send(new ShellViewHostMsg_UpdateDraggableRegions(routing_id(), regions));
}

void Dispatcher::OnEvent(int object_id,
                         std::string event,
                         const base::ListValue& arguments) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope scope(isolate);
  blink::WebView* web_view = render_view()->GetWebView();
  if (web_view == NULL)
    return;

  DVLOG(1) << "Dispatcher::OnEvent(object_id=" << object_id << ", event=\"" << event << "\")";

  content::V8ValueConverterImpl converter;
  v8::Local<v8::Context> context =
    v8::Local<v8::Context>::New(isolate, node::g_context);

  v8::Handle<v8::Value> args = converter.ToV8Value(&arguments, context);
  DCHECK(!args.IsEmpty()) << "Invalid 'arguments' in Dispatcher::OnEvent";
  v8::Handle<v8::Value> argv[] = {
    v8::Integer::New(isolate, object_id), v8_str(event.c_str()), args };

  // __nwObjectsRegistry.handleEvent(object_id, event, arguments);
  v8::Handle<v8::Value> val =
    context->Global()->Get(v8_str("__nwObjectsRegistry"));
  if (val->IsNull() || val->IsUndefined())
    return; // need to find out why it's undefined here in debugger
  v8::Handle<v8::Object> objects_registry = val->ToObject();
  DVLOG(1) << "handleEvent(object_id=" << object_id << ", event=\"" << event << "\")";
  node::MakeCallback(isolate, objects_registry, "handleEvent", 3, argv);
}

v8::Handle<v8::Object> Dispatcher::GetObjectRegistry() {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Local<v8::Context> context =
    v8::Local<v8::Context>::New(isolate, node::g_context);
  // need to enter node context to access the registry in
  // some cases, e.g. normal frame in #1519
  context->Enter();
  v8::Handle<v8::Value> registry =
    context->Global()->Get(v8_str("__nwObjectsRegistry"));
  context->Exit();
  ASSERT(!(registry->IsNull() || registry->IsUndefined()));
  // if (registry->IsNull() || registry->IsUndefined())
  //   return v8::Undefined();
  return registry->ToObject();
}

v8::Handle<v8::Value> Dispatcher::GetWindowId(blink::WebFrame* frame) {
  v8::Handle<v8::Value> v8win = frame->mainWorldScriptContext()->Global();
  v8::Handle<v8::Value> val = v8win->ToObject()->Get(v8_str("__nwWindowId"));

  return val;
}

void Dispatcher::ZoomLevelChanged() {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope scope(isolate);

  blink::WebView* web_view = render_view()->GetWebView();
  float zoom_level = web_view->zoomLevel();

  v8::Handle<v8::Value> val = GetWindowId(web_view->mainFrame());

  if (val->IsNull() || val->IsUndefined())
    return;

  v8::Handle<v8::Object> objects_registry = GetObjectRegistry();
  if (objects_registry->IsUndefined())
    return;

  v8::Local<v8::Array> args = v8::Array::New(isolate);
  args->Set(0, v8::Number::New(isolate, zoom_level));
  v8::Handle<v8::Value> argv[] = {val, v8_str("zoom"), args };

  node::MakeCallback(isolate, objects_registry, "handleEvent", 3, argv);
}

void Dispatcher::DidCreateDocumentElement(blink::WebFrame* frame) {
  documentCallback("document-start", frame);
}

void Dispatcher::DidFinishDocumentLoad(blink::WebFrame* frame) {
  documentCallback("document-end", frame);
}

void Dispatcher::documentCallback(const char* ev, blink::WebFrame* frame) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  blink::WebView* web_view = render_view()->GetWebView();
  v8::HandleScope scope(isolate);

  if (!web_view)
    return;
  v8::Context::Scope cscope (web_view->mainFrame()->mainWorldScriptContext());

  v8::Handle<v8::Value> val = GetWindowId(web_view->mainFrame());
  if (val->IsNull() || val->IsUndefined())
    return;

  v8::Handle<v8::Object> objects_registry = GetObjectRegistry();
  if (objects_registry->IsUndefined())
    return;

  v8::Local<v8::Array> args = v8::Array::New(isolate);
  v8::Handle<v8::Value> element = v8::Null(isolate);
  WebCore::LocalFrame* core_frame = blink::toWebFrameImpl(frame)->frame();
  if (core_frame->ownerElement()) {
    element = WebCore::toV8((WebCore::HTMLElement*)core_frame->ownerElement(),
                            frame->mainWorldScriptContext()->Global(),
                            frame->mainWorldScriptContext()->GetIsolate());
  }
  args->Set(0, element);
  v8::Handle<v8::Value> argv[] = {val, v8_str(ev), args };

  node::MakeCallback(isolate, objects_registry, "handleEvent", 3, argv);
}

void Dispatcher::willHandleNavigationPolicy(
    content::RenderView* rv,
    blink::WebFrame* frame,
    const blink::WebURLRequest& request,
    blink::WebNavigationPolicy* policy) {

  blink::WebView* web_view = rv->GetWebView();

  if (!web_view)
    return;

  v8::Isolate* isolate = v8::Isolate::GetCurrent();

  v8::Handle<v8::Value> id_val = nwapi::Dispatcher::GetWindowId(web_view->mainFrame());
  if (id_val->IsNull() || id_val->IsUndefined())
    return;

  v8::Handle<v8::Object> objects_registry = nwapi::Dispatcher::GetObjectRegistry();
  if (objects_registry->IsUndefined())
    return;

  v8::Context::Scope cscope (web_view->mainFrame()->mainWorldScriptContext());

  v8::Local<v8::Array> args = v8::Array::New(isolate);
  v8::Handle<v8::Value> element = v8::Null(isolate);
  v8::Handle<v8::Object> policy_obj = v8::Object::New(isolate);

  WebCore::LocalFrame* core_frame = blink::toWebFrameImpl(frame)->frame();
  if (core_frame->ownerElement()) {
    element = WebCore::toV8((WebCore::HTMLElement*)core_frame->ownerElement(),
                            frame->mainWorldScriptContext()->Global(),
                            frame->mainWorldScriptContext()->GetIsolate());
  }
  args->Set(0, element);
  args->Set(1, v8_str(request.url().string().utf8().c_str()));
  args->Set(2, policy_obj);

  v8::Handle<v8::Value> argv[] = {id_val, v8_str("new-win-policy"), args };

  node::MakeCallback(isolate, objects_registry, "handleEvent", 3, argv);
  v8::Local<v8::Value> val = policy_obj->Get(v8_str("val"));
  if (!val->IsString())
    return;
  v8::String::Utf8Value policy_str(val);
  if (!strcmp(*policy_str, "ignore"))
    *policy = blink::WebNavigationPolicyIgnore;
  else if (!strcmp(*policy_str, "download"))
    *policy = blink::WebNavigationPolicyDownload;
  else if (!strcmp(*policy_str, "current"))
    *policy = blink::WebNavigationPolicyCurrentTab;
  else if (!strcmp(*policy_str, "new-window"))
    *policy = blink::WebNavigationPolicyNewWindow;
  else if (!strcmp(*policy_str, "new-popup"))
    *policy = blink::WebNavigationPolicyNewPopup;
}

}  // namespace nwapi
<commit_msg>handle empty id value in dispatcher callbacks<commit_after>// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#define V8_USE_UNSAFE_HANDLES

#include "content/nw/src/api/dispatcher.h"

#include "content/nw/src/api/api_messages.h"
#include "content/public/renderer/render_view.h"
#include "content/renderer/v8_value_converter_impl.h"
#include "third_party/node/src/node.h"
#undef CHECK
#include "third_party/node/src/req_wrap.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "v8/include/v8.h"

#undef LOG
#undef ASSERT
#undef FROM_HERE

#if defined(OS_WIN)
#define _USE_MATH_DEFINES
#include <math.h>
#endif
#include "third_party/WebKit/Source/config.h"
#include "third_party/WebKit/Source/core/frame/Frame.h"
#include "third_party/WebKit/Source/web/WebFrameImpl.h"
#include "V8HTMLElement.h"

namespace nwapi {

static inline v8::Local<v8::String> v8_str(const char* x) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  return v8::String::NewFromUtf8(isolate, x);
}

Dispatcher::Dispatcher(content::RenderView* render_view)
    : content::RenderViewObserver(render_view) {
}

Dispatcher::~Dispatcher() {
}

bool Dispatcher::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(Dispatcher, message)
    IPC_MESSAGE_HANDLER(ShellViewMsg_Object_On_Event, OnEvent)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()

  return handled;
}

void Dispatcher::DraggableRegionsChanged(blink::WebFrame* frame) {
  blink::WebVector<blink::WebDraggableRegion> webregions =
      frame->document().draggableRegions();
  std::vector<extensions::DraggableRegion> regions;
  for (size_t i = 0; i < webregions.size(); ++i) {
    extensions::DraggableRegion region;
    region.bounds = webregions[i].bounds;
    region.draggable = webregions[i].draggable;
    regions.push_back(region);
  }
  Send(new ShellViewHostMsg_UpdateDraggableRegions(routing_id(), regions));
}

void Dispatcher::OnEvent(int object_id,
                         std::string event,
                         const base::ListValue& arguments) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope scope(isolate);
  blink::WebView* web_view = render_view()->GetWebView();
  if (web_view == NULL)
    return;

  DVLOG(1) << "Dispatcher::OnEvent(object_id=" << object_id << ", event=\"" << event << "\")";

  content::V8ValueConverterImpl converter;
  v8::Local<v8::Context> context =
    v8::Local<v8::Context>::New(isolate, node::g_context);

  v8::Handle<v8::Value> args = converter.ToV8Value(&arguments, context);
  DCHECK(!args.IsEmpty()) << "Invalid 'arguments' in Dispatcher::OnEvent";
  v8::Handle<v8::Value> argv[] = {
    v8::Integer::New(isolate, object_id), v8_str(event.c_str()), args };

  // __nwObjectsRegistry.handleEvent(object_id, event, arguments);
  v8::Handle<v8::Value> val =
    context->Global()->Get(v8_str("__nwObjectsRegistry"));
  if (val->IsNull() || val->IsUndefined())
    return; // need to find out why it's undefined here in debugger
  v8::Handle<v8::Object> objects_registry = val->ToObject();
  DVLOG(1) << "handleEvent(object_id=" << object_id << ", event=\"" << event << "\")";
  node::MakeCallback(isolate, objects_registry, "handleEvent", 3, argv);
}

v8::Handle<v8::Object> Dispatcher::GetObjectRegistry() {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Local<v8::Context> context =
    v8::Local<v8::Context>::New(isolate, node::g_context);
  // need to enter node context to access the registry in
  // some cases, e.g. normal frame in #1519
  context->Enter();
  v8::Handle<v8::Value> registry =
    context->Global()->Get(v8_str("__nwObjectsRegistry"));
  context->Exit();
  ASSERT(!(registry->IsNull() || registry->IsUndefined()));
  // if (registry->IsNull() || registry->IsUndefined())
  //   return v8::Undefined();
  return registry->ToObject();
}

v8::Handle<v8::Value> Dispatcher::GetWindowId(blink::WebFrame* frame) {
  v8::Handle<v8::Value> v8win = frame->mainWorldScriptContext()->Global();
  v8::Handle<v8::Value> val = v8win->ToObject()->Get(v8_str("__nwWindowId"));

  return val;
}

void Dispatcher::ZoomLevelChanged() {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope scope(isolate);

  blink::WebView* web_view = render_view()->GetWebView();
  float zoom_level = web_view->zoomLevel();

  v8::Handle<v8::Value> val = GetWindowId(web_view->mainFrame());

  if (id_val.IsEmpty())
    return;
  if (val->IsNull() || val->IsUndefined())
    return;

  v8::Handle<v8::Object> objects_registry = GetObjectRegistry();
  if (objects_registry->IsUndefined())
    return;

  v8::Local<v8::Array> args = v8::Array::New(isolate);
  args->Set(0, v8::Number::New(isolate, zoom_level));
  v8::Handle<v8::Value> argv[] = {val, v8_str("zoom"), args };

  node::MakeCallback(isolate, objects_registry, "handleEvent", 3, argv);
}

void Dispatcher::DidCreateDocumentElement(blink::WebFrame* frame) {
  documentCallback("document-start", frame);
}

void Dispatcher::DidFinishDocumentLoad(blink::WebFrame* frame) {
  documentCallback("document-end", frame);
}

void Dispatcher::documentCallback(const char* ev, blink::WebFrame* frame) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  blink::WebView* web_view = render_view()->GetWebView();
  v8::HandleScope scope(isolate);

  if (!web_view)
    return;
  v8::Context::Scope cscope (web_view->mainFrame()->mainWorldScriptContext());

  v8::Handle<v8::Value> val = GetWindowId(web_view->mainFrame());
  if (id_val.IsEmpty())
    return;
  if (val->IsNull() || val->IsUndefined())
    return;

  v8::Handle<v8::Object> objects_registry = GetObjectRegistry();
  if (objects_registry->IsUndefined())
    return;

  v8::Local<v8::Array> args = v8::Array::New(isolate);
  v8::Handle<v8::Value> element = v8::Null(isolate);
  WebCore::LocalFrame* core_frame = blink::toWebFrameImpl(frame)->frame();
  if (core_frame->ownerElement()) {
    element = WebCore::toV8((WebCore::HTMLElement*)core_frame->ownerElement(),
                            frame->mainWorldScriptContext()->Global(),
                            frame->mainWorldScriptContext()->GetIsolate());
  }
  args->Set(0, element);
  v8::Handle<v8::Value> argv[] = {val, v8_str(ev), args };

  node::MakeCallback(isolate, objects_registry, "handleEvent", 3, argv);
}

void Dispatcher::willHandleNavigationPolicy(
    content::RenderView* rv,
    blink::WebFrame* frame,
    const blink::WebURLRequest& request,
    blink::WebNavigationPolicy* policy) {

  blink::WebView* web_view = rv->GetWebView();

  if (!web_view)
    return;

  v8::Isolate* isolate = v8::Isolate::GetCurrent();

  v8::Handle<v8::Value> id_val = nwapi::Dispatcher::GetWindowId(web_view->mainFrame());
  if (id_val.IsEmpty())
    return;
  if (id_val->IsUndefined() || id_val->IsNull())
    return;

  v8::Handle<v8::Object> objects_registry = nwapi::Dispatcher::GetObjectRegistry();
  if (objects_registry->IsUndefined())
    return;

  v8::Context::Scope cscope (web_view->mainFrame()->mainWorldScriptContext());

  v8::Local<v8::Array> args = v8::Array::New(isolate);
  v8::Handle<v8::Value> element = v8::Null(isolate);
  v8::Handle<v8::Object> policy_obj = v8::Object::New(isolate);

  WebCore::LocalFrame* core_frame = blink::toWebFrameImpl(frame)->frame();
  if (core_frame->ownerElement()) {
    element = WebCore::toV8((WebCore::HTMLElement*)core_frame->ownerElement(),
                            frame->mainWorldScriptContext()->Global(),
                            frame->mainWorldScriptContext()->GetIsolate());
  }
  args->Set(0, element);
  args->Set(1, v8_str(request.url().string().utf8().c_str()));
  args->Set(2, policy_obj);

  v8::Handle<v8::Value> argv[] = {id_val, v8_str("new-win-policy"), args };

  node::MakeCallback(isolate, objects_registry, "handleEvent", 3, argv);
  v8::Local<v8::Value> val = policy_obj->Get(v8_str("val"));
  if (!val->IsString())
    return;
  v8::String::Utf8Value policy_str(val);
  if (!strcmp(*policy_str, "ignore"))
    *policy = blink::WebNavigationPolicyIgnore;
  else if (!strcmp(*policy_str, "download"))
    *policy = blink::WebNavigationPolicyDownload;
  else if (!strcmp(*policy_str, "current"))
    *policy = blink::WebNavigationPolicyCurrentTab;
  else if (!strcmp(*policy_str, "new-window"))
    *policy = blink::WebNavigationPolicyNewWindow;
  else if (!strcmp(*policy_str, "new-popup"))
    *policy = blink::WebNavigationPolicyNewPopup;
}

}  // namespace nwapi
<|endoftext|>


<commit_before>/* The goal of these tests is to verify the behavior of all blob commands given
 * the current state is updateStarted.  This state is achieved as an exit from
 * updatePending.
 */
#include "firmware_handler.hpp"
#include "firmware_unittest.hpp"
#include "status.hpp"
#include "util.hpp"

#include <cstdint>
#include <string>
#include <vector>

#include <gtest/gtest.h>

namespace ipmi_flash
{
namespace
{

using ::testing::Return;

/*
 * There are the following calls (parameters may vary):
 * canHandleBlob(blob)
 * getBlobIds
 * deleteBlob(blob)
 * stat(blob)
 * stat(session)
 * open(blob)
 * close(session)
 * writemeta(session)
 * write(session)
 * read(session)
 * commit(session)
 */

class FirmwareHandlerUpdateStartedTest : public IpmiOnlyFirmwareStaticTest
{
};

/*
 * open(blob)
 */
TEST_F(FirmwareHandlerUpdateStartedTest, AttemptToOpenFilesReturnsFailure)
{
    /* In state updateStarted a file is open, which means no others can be. */
    getToUpdateStarted();

    auto blobsToOpen = handler->getBlobIds();
    for (const auto& blob : blobsToOpen)
    {
        EXPECT_FALSE(handler->open(session + 1, flags, blob));
    }
}

/* canHandleBlob(blob)
 * getBlobIds
 */

/*
 * TODO: deleteBlob(blob)
 */

/*
 * stat(blob)
 */

/*
 * stat(session)
 */

/*
 * close(session)
 */

/*
 * writemeta(session)
 */

/*
 * write(session)
 */

/*
 * read(session)
 */

/*
 * commit(session)
 */

} // namespace
} // namespace ipmi_flash
<commit_msg>test: firmware updateStarted: getBlobIds()<commit_after>/* The goal of these tests is to verify the behavior of all blob commands given
 * the current state is updateStarted.  This state is achieved as an exit from
 * updatePending.
 */
#include "firmware_handler.hpp"
#include "firmware_unittest.hpp"
#include "status.hpp"
#include "util.hpp"

#include <cstdint>
#include <string>
#include <vector>

#include <gtest/gtest.h>

namespace ipmi_flash
{
namespace
{

using ::testing::Return;
using ::testing::UnorderedElementsAreArray;

/*
 * There are the following calls (parameters may vary):
 * canHandleBlob(blob)
 * getBlobIds
 * deleteBlob(blob)
 * stat(blob)
 * stat(session)
 * open(blob)
 * close(session)
 * writemeta(session)
 * write(session)
 * read(session)
 * commit(session)
 */

class FirmwareHandlerUpdateStartedTest : public IpmiOnlyFirmwareStaticTest
{
};

/*
 * open(blob)
 */
TEST_F(FirmwareHandlerUpdateStartedTest, AttemptToOpenFilesReturnsFailure)
{
    /* In state updateStarted a file is open, which means no others can be. */
    getToUpdateStarted();

    auto blobsToOpen = handler->getBlobIds();
    for (const auto& blob : blobsToOpen)
    {
        EXPECT_FALSE(handler->open(session + 1, flags, blob));
    }
}

/* canHandleBlob(blob)
 * getBlobIds
 */
TEST_F(FirmwareHandlerUpdateStartedTest, VerifyListOfBlobs)
{
    getToUpdateStarted();

    std::vector<std::string> expected = {updateBlobId, hashBlobId,
                                         activeImageBlobId, staticLayoutBlobId};
    EXPECT_THAT(handler->getBlobIds(), UnorderedElementsAreArray(expected));
}

/*
 * TODO: deleteBlob(blob)
 */

/*
 * stat(blob)
 */

/*
 * stat(session)
 */

/*
 * close(session)
 */

/*
 * writemeta(session)
 */

/*
 * write(session)
 */

/*
 * read(session)
 */

/*
 * commit(session)
 */

} // namespace
} // namespace ipmi_flash
<|endoftext|>