<commit_before>// BUG: this whole thing depends on the specifics of how the clang version I
// am using emits llvm bitcode for the hacky RMC protocol.
// We rely on how basic blocks get named, on the labels forcing things
// into their own basic blocks, and probably will rely on this block
// having one predecessor and one successor. We could probably even
// force those to be empty without too much work by adding more labels...

#include <llvm/Pass.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/InlineAsm.h>
#include <llvm/IR/CFG.h>
#include <llvm/IR/InstIterator.h>
#include <llvm/Transforms/Scalar.h>

#include <llvm/ADT/ArrayRef.h>

#include <llvm/Transforms/Utils/BasicBlockUtils.h>
#include <llvm/Support/raw_ostream.h>

#include <ostream>
#include <fstream>
#include <iostream>
#include <map>
#include <set>

using namespace llvm;

namespace {
#if 0 // I hate you, emacs. Is there a better way to work around this?
}
#endif

/* To do bogus data dependencies, we will emit inline assembly
  instructions. This is sort of tasteless; we should add an intrinsic;
  but it means we don't need to modify llvm. This is what the
  instruction should look like (probably the constraints aren't
  right?):

  %2 = call i32 asm sideeffect "eor $0, $0;", "=r,0"(i32 %1) #2, !srcloc !11

  Also for ctrlisb we do:
  call void asm sideeffect "cmp $0, $0;beq 1f;1: isb", "r,~{memory}"(i32 %1) #2, !srcloc !4

  And for dmb:
  call void asm sideeffect "dmb", "~{memory}"() #2, !srcloc !5

  We can probably do a bogus inline asm on x86 to prevent reordering:
  %2 = call i32 asm sideeffect "", "=r,r,0,~{dirflag},~{fpsr},~{flags}"(i32 %1, i32 %0) #3, !srcloc !9

  What about this makes the right value come out:
    =r specifies an output parameter and the 0 says that that input parameter
    is paired with param 0.

  Hm. Does this *actually* work?? What does "sideeffect" actually mean
  for asm and do we need it.

  --
  Should we emit dmb directly, or try to use llvm's fences?
*/

//// Some auxillary data structures
enum RMCEdgeType {
  VisbilityEdge,
  ExecutionEdge
};

raw_ostream& operator<<(raw_ostream& os, const RMCEdgeType& t) {
  switch (t) {
    case VisbilityEdge:
      os << "v";
      break;
    case ExecutionEdge:
      os << "x";
      break;
    default:
      os << "?";
      break;
  }
  return os;
}

// Info about an RMC edge
struct RMCEdge {
  RMCEdgeType edgeType;
  BasicBlock *src;
  BasicBlock *dst;

  bool operator<(const RMCEdge& rhs) const {
    return std::tie(edgeType, src, dst)
      < std::tie(rhs.edgeType, rhs.src, rhs.dst);
  }

  void print(raw_ostream &os) const {
    // substr(5) is to drop "_rmc_" from the front
    os << src->getName().substr(5) << " -" << edgeType <<
      "-> " << dst->getName().substr(5);
  }
};

raw_ostream& operator<<(raw_ostream& os, const RMCEdge& e) {
  e.print(os);
  return os;
}

///////////////////////////////////////////////////////////////////////////
// Information for a node in the RMC graph.
struct Block {
  Block(BasicBlock *p_bb) : bb(p_bb), stores(0), loads(0), RMWs(0), calls(0) {}
  void operator=(const Block &) LLVM_DELETED_FUNCTION;
  Block(const Block &) LLVM_DELETED_FUNCTION;
  Block(Block &&) = default; // move constructor!

  BasicBlock *bb;

  // Some basic info about what sort of instructions live in the block
  int stores;
  int loads;
  int RMWs;
  int calls;

  // Edges in the graph.
  // XXX: Would we be better off storing this some other way?
  // a <ptr, type> pair?
  // And should we store v edges in x
  SmallPtrSet<Block *, 2> execEdges;
  SmallPtrSet<Block *, 2> visEdges;
};

enum CutType {
  CutNone,
  CutCtrlIsync, // needs to be paired with a dep
  CutLwsync,
  CutSync
};
struct EdgeCut {
  EdgeCut() : type(CutNone), front(false), read(nullptr) {}
  CutType type;
  bool front;
  Value *read;
};

///////////////////////////////////////////////////////////////////////////
// Code to find all simple paths between two basic blocks.
// Could generalize more to graphs if we wanted, but I don't right
// now.
typedef std::vector<BasicBlock *> Path;
typedef SmallVector<Path, 2> PathList;
typedef SmallPtrSet<BasicBlock *, 8> GreySet;

PathList findAllSimplePaths_(GreySet *grey, BasicBlock *src, BasicBlock *dst) {
  PathList paths;
  if (grey->count(src)) return paths;
  if (src == dst) {
    Path path;
    path.push_back(dst);
    paths.push_back(std::move(path));
    return paths;
  }

  grey->insert(src);

  // Go search all the successors
  for (auto i = succ_begin(src), e = succ_end(src); i != e; i++) {
    PathList subpaths = findAllSimplePaths_(grey, *i, dst);
    std::move(subpaths.begin(), subpaths.end(), back_inserter(paths));
  }

  // Add our step to all of the vectors
  for (auto & path : paths) {
    path.push_back(src);
  }

  // Remove it from the set of things we've seen. We might come
  // through here again.
  // We can't really do any sort of memoization, since in a cyclic
  // graph the possible simple paths depend not just on what node we
  // are on, but our previous path (to avoid looping).
  grey->erase(src);

  return paths;
}

PathList findAllSimplePaths(BasicBlock *src, BasicBlock *dst) {
  GreySet grey;
  return findAllSimplePaths_(&grey, src, dst);
}

void dumpPaths(const PathList &paths) {
  for (auto & path : paths) {
    for (auto block : path) {
      errs() << block->getName() << " <- ";
    }
    errs() << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////
// XXX: This is the wrong way to do this!
bool targetARM = true;
bool targetx86 = false;

// Some llvm nonsense. I should probably find a way to clean this up.
// do we put ~{dirflag},~{fpsr},~{flags} for the x86 ones? don't think so.
Instruction *makeSync(Value *dummy) {
  LLVMContext &C = dummy->getContext();
  FunctionType *f_ty = FunctionType::get(FunctionType::getVoidTy(C), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "dmb", "~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "msync", "~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "sync");
}
Instruction *makeLwsync(Value *dummy) {
  LLVMContext &C = dummy->getContext();
  FunctionType *f_ty = FunctionType::get(FunctionType::getVoidTy(C), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "dmb", "~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "", "~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "lwsync");
}
Instruction *makeCtrlIsync(Value *v) {
  LLVMContext &C = v->getContext();
  FunctionType *f_ty =
    FunctionType::get(FunctionType::getVoidTy(C), v->getType(), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "cmp $0, $0;beq 1f;1: isb", "r,~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "", "r,~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "ctrlisync");
}
// We also need to add a thing for fake data deps, which is more annoying.

///////////////////////////////////////////////////////////////////////////
//// Actual code for the pass

class RMCPass : public FunctionPass {
private:
  std::vector<Block> blocks_;
  DenseMap<BasicBlock *, Block *> bb2block_;
  DenseMap<BasicBlock *, EdgeCut> cuts_;

public:
  static char ID;
  RMCPass() : FunctionPass(ID) {

  }
  ~RMCPass() { }
  std::vector<RMCEdge> findEdges(Function &F);
  void buildGraph(std::vector<RMCEdge> &edges, Function &F);
  virtual bool runOnFunction(Function &F);

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.addRequiredID(BreakCriticalEdgesID);
    AU.setPreservesCFG();
  }

};

bool nameMatches(StringRef blockName, StringRef target) {
  StringRef name = "_rmc_" + target.str() + "_";
  if (!blockName.startswith(name)) return false;
  // Now make sure the rest is an int
  APInt dummy;
  return !blockName.drop_front(name.size()).getAsInteger(10, dummy);
}

StringRef getStringArg(Value *v) {
  const Value *g = cast<Constant>(v)->stripPointerCasts();
  const Constant *ptr = cast<GlobalVariable>(g)->getInitializer();
  StringRef str = cast<ConstantDataSequential>(ptr)->getAsString();
  // Drop the '\0' at the end
  return str.drop_back();
}

std::vector<RMCEdge> RMCPass::findEdges(Function &F) {
  std::vector<RMCEdge> edges;

  for (inst_iterator is = inst_begin(F), ie = inst_end(F); is != ie;) {
    // Grab the instruction and advance the iterator at the start, since
    // we might delete the instruction.
    Instruction *i = &*is;
    is++;

    CallInst *load = dyn_cast<CallInst>(i);
    if (!load) continue;
    Function *target = load->getCalledFunction();
    // We look for calls to the bogus function
    // __rmc_edge_register, pull out the information about them,
    // and delete the calls.
    if (!target || target->getName() != "__rmc_edge_register") continue;

    // Pull out what the operands have to be.
    // We just assert if something is wrong, which is not great UX.
    bool isVisibility = cast<ConstantInt>(load->getOperand(0))
      ->getValue().getBoolValue();
    RMCEdgeType edgeType = isVisibility ? VisbilityEdge : ExecutionEdge;
    StringRef srcName = getStringArg(load->getOperand(1));
    StringRef dstName = getStringArg(load->getOperand(2));

    // Since multiple blocks can have the same tag, we search for
    // them by name.
    // We could make this more efficient by building maps but I don't think
    // it is going to matter.
    for (auto & src : F) {
      if (!nameMatches(src.getName(), srcName)) continue;
      for (auto & dst : F) {
        if (!nameMatches(dst.getName(), dstName)) continue;

        edges.push_back({edgeType, &src, &dst});
      }
    }

    // Delete the bogus call.
    i->eraseFromParent();
  }

  return edges;
}

void analyzeBlock(Block &info) {
  for (auto & i : *info.bb) {
    if (isa<LoadInst>(i)) {
      info.loads++;
    } else if (isa<StoreInst>(i)) {
      info.stores++;
    // What else counts as a call? I'm counting fences I guess.
    } else if (isa<CallInst>(i) || isa<FenceInst>(i)) {
      info.calls++;
    } else if (isa<AtomicCmpXchgInst>(i) || isa<AtomicRMWInst>(i)) {
      info.RMWs++;
    }
  }
}

void RMCPass::buildGraph(std::vector<RMCEdge> &edges, Function &F) {
  // First, collect all the basic blocks with edges attached to them
  SmallPtrSet<BasicBlock *, 8> basicBlocks;
  for (auto & edge : edges) {
    basicBlocks.insert(edge.src);
    basicBlocks.insert(edge.dst);
  }

  // Now, make the vector of blocks and a mapping from BasicBlock *.
  blocks_.reserve(basicBlocks.size());
  for (auto bb : basicBlocks) {
    blocks_.emplace_back(bb);
    bb2block_[bb] = &blocks_.back();
  }

  // Analyze the instructions in blocks to see what sort of actions
  // they peform.
  for (auto & block : blocks_) {
    analyzeBlock(block);
  }

  // Build our list of edges into a more explicit graph
  for (auto & edge : edges) {
    Block *src = bb2block_[edge.src];
    Block *dst = bb2block_[edge.dst];
    if (edge.edgeType == VisbilityEdge) {
      src->visEdges.insert(dst);
    } else {
      src->execEdges.insert(dst);
    }
  }
}

bool RMCPass::runOnFunction(Function &F) {
  auto edges = findEdges(F);

  for (auto & edge : edges) {
    errs() << "Found an edge: " << edge << "\n";
  }

  buildGraph(edges, F);
  bool changed = !edges.empty();

  // Clear our data structures to save memory, make things clean for
  // future runs.
  blocks_.clear();
  bb2block_.clear();
  cuts_.clear();

  return changed;
}

char RMCPass::ID = 0;
RegisterPass<RMCPass> X("rmc-pass", "RMC");
}
<commit_msg>Collect a bunch more information on blocks.<commit_after>// BUG: this whole thing depends on the specifics of how the clang version I
// am using emits llvm bitcode for the hacky RMC protocol.
// We rely on how basic blocks get named, on the labels forcing things
// into their own basic blocks, and probably will rely on this block
// having one predecessor and one successor. We could probably even
// force those to be empty without too much work by adding more labels...

#include <llvm/Pass.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/InlineAsm.h>
#include <llvm/IR/CFG.h>
#include <llvm/IR/InstIterator.h>
#include <llvm/Transforms/Scalar.h>

#include <llvm/ADT/ArrayRef.h>

#include <llvm/Transforms/Utils/BasicBlockUtils.h>
#include <llvm/Support/raw_ostream.h>

#include <ostream>
#include <fstream>
#include <iostream>
#include <map>
#include <set>

using namespace llvm;

namespace {
#if 0 // I hate you, emacs. Is there a better way to work around this?
}
#endif

/* To do bogus data dependencies, we will emit inline assembly
  instructions. This is sort of tasteless; we should add an intrinsic;
  but it means we don't need to modify llvm. This is what the
  instruction should look like (probably the constraints aren't
  right?):

  %2 = call i32 asm sideeffect "eor $0, $0;", "=r,0"(i32 %1) #2, !srcloc !11

  Also for ctrlisb we do:
  call void asm sideeffect "cmp $0, $0;beq 1f;1: isb", "r,~{memory}"(i32 %1) #2, !srcloc !4

  And for dmb:
  call void asm sideeffect "dmb", "~{memory}"() #2, !srcloc !5

  We can probably do a bogus inline asm on x86 to prevent reordering:
  %2 = call i32 asm sideeffect "", "=r,r,0,~{dirflag},~{fpsr},~{flags}"(i32 %1, i32 %0) #3, !srcloc !9

  What about this makes the right value come out:
    =r specifies an output parameter and the 0 says that that input parameter
    is paired with param 0.

  Hm. Does this *actually* work?? What does "sideeffect" actually mean
  for asm and do we need it.

  --
  Should we emit dmb directly, or try to use llvm's fences?
*/

//// Some auxillary data structures
enum RMCEdgeType {
  VisbilityEdge,
  ExecutionEdge
};

raw_ostream& operator<<(raw_ostream& os, const RMCEdgeType& t) {
  switch (t) {
    case VisbilityEdge:
      os << "v";
      break;
    case ExecutionEdge:
      os << "x";
      break;
    default:
      os << "?";
      break;
  }
  return os;
}

// Info about an RMC edge
struct RMCEdge {
  RMCEdgeType edgeType;
  BasicBlock *src;
  BasicBlock *dst;

  bool operator<(const RMCEdge& rhs) const {
    return std::tie(edgeType, src, dst)
      < std::tie(rhs.edgeType, rhs.src, rhs.dst);
  }

  void print(raw_ostream &os) const {
    // substr(5) is to drop "_rmc_" from the front
    os << src->getName().substr(5) << " -" << edgeType <<
      "-> " << dst->getName().substr(5);
  }
};

raw_ostream& operator<<(raw_ostream& os, const RMCEdge& e) {
  e.print(os);
  return os;
}

///////////////////////////////////////////////////////////////////////////
// Information for a node in the RMC graph.
enum BlockType {
  BlockComplex,
  BlockSimpleRead,
  BlockSimpleWrites, // needs to be paired with a dep
  BlockSimpleRMW
};
struct Block {
  Block(BasicBlock *p_bb) :
    bb(p_bb),
    type(BlockComplex),
    stores(0), loads(0), RMWs(0), calls(0), soleLoad(nullptr) {}
  void operator=(const Block &) LLVM_DELETED_FUNCTION;
  Block(const Block &) LLVM_DELETED_FUNCTION;
  Block(Block &&) = default; // move constructor!

  BasicBlock *bb;

  // Some basic info about what sort of instructions live in the block
  BlockType type;
  int stores;
  int loads;
  int RMWs;
  int calls;
  LoadInst *soleLoad;

  // Edges in the graph.
  // XXX: Would we be better off storing this some other way?
  // a <ptr, type> pair?
  // And should we store v edges in x
  SmallPtrSet<Block *, 2> execEdges;
  SmallPtrSet<Block *, 2> visEdges;
};

enum CutType {
  CutNone,
  CutCtrlIsync, // needs to be paired with a dep
  CutLwsync,
  CutSync
};
struct EdgeCut {
  EdgeCut() : type(CutNone), front(false), read(nullptr) {}
  CutType type;
  bool front;
  Value *read;
};

///////////////////////////////////////////////////////////////////////////
// Code to find all simple paths between two basic blocks.
// Could generalize more to graphs if we wanted, but I don't right
// now.
typedef std::vector<BasicBlock *> Path;
typedef SmallVector<Path, 2> PathList;
typedef SmallPtrSet<BasicBlock *, 8> GreySet;

PathList findAllSimplePaths_(GreySet *grey, BasicBlock *src, BasicBlock *dst) {
  PathList paths;
  if (grey->count(src)) return paths;
  if (src == dst) {
    Path path;
    path.push_back(dst);
    paths.push_back(std::move(path));
    return paths;
  }

  grey->insert(src);

  // Go search all the successors
  for (auto i = succ_begin(src), e = succ_end(src); i != e; i++) {
    PathList subpaths = findAllSimplePaths_(grey, *i, dst);
    std::move(subpaths.begin(), subpaths.end(), back_inserter(paths));
  }

  // Add our step to all of the vectors
  for (auto & path : paths) {
    path.push_back(src);
  }

  // Remove it from the set of things we've seen. We might come
  // through here again.
  // We can't really do any sort of memoization, since in a cyclic
  // graph the possible simple paths depend not just on what node we
  // are on, but our previous path (to avoid looping).
  grey->erase(src);

  return paths;
}

PathList findAllSimplePaths(BasicBlock *src, BasicBlock *dst) {
  GreySet grey;
  return findAllSimplePaths_(&grey, src, dst);
}

void dumpPaths(const PathList &paths) {
  for (auto & path : paths) {
    for (auto block : path) {
      errs() << block->getName() << " <- ";
    }
    errs() << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////
// XXX: This is the wrong way to do this!
bool targetARM = true;
bool targetx86 = false;

// Some llvm nonsense. I should probably find a way to clean this up.
// do we put ~{dirflag},~{fpsr},~{flags} for the x86 ones? don't think so.
Instruction *makeSync(Value *dummy) {
  LLVMContext &C = dummy->getContext();
  FunctionType *f_ty = FunctionType::get(FunctionType::getVoidTy(C), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "dmb", "~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "msync", "~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "sync");
}
Instruction *makeLwsync(Value *dummy) {
  LLVMContext &C = dummy->getContext();
  FunctionType *f_ty = FunctionType::get(FunctionType::getVoidTy(C), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "dmb", "~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "", "~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "lwsync");
}
Instruction *makeCtrlIsync(Value *v) {
  LLVMContext &C = v->getContext();
  FunctionType *f_ty =
    FunctionType::get(FunctionType::getVoidTy(C), v->getType(), false);
  InlineAsm *a;
  if (targetARM) {
    a = InlineAsm::get(f_ty, "cmp $0, $0;beq 1f;1: isb", "r,~{memory}", true);
  } else if (targetx86) {
    a = InlineAsm::get(f_ty, "", "r,~{memory}", true);
  } else {
    assert(false && "invalid target");
  }
  return CallInst::Create(a, None, "ctrlisync");
}
// We also need to add a thing for fake data deps, which is more annoying.

///////////////////////////////////////////////////////////////////////////
//// Actual code for the pass

class RMCPass : public FunctionPass {
private:
  std::vector<Block> blocks_;
  DenseMap<BasicBlock *, Block *> bb2block_;
  DenseMap<BasicBlock *, EdgeCut> cuts_;

public:
  static char ID;
  RMCPass() : FunctionPass(ID) {

  }
  ~RMCPass() { }
  std::vector<RMCEdge> findEdges(Function &F);
  void buildGraph(std::vector<RMCEdge> &edges, Function &F);
  virtual bool runOnFunction(Function &F);

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.addRequiredID(BreakCriticalEdgesID);
    AU.setPreservesCFG();
  }

};

bool nameMatches(StringRef blockName, StringRef target) {
  StringRef name = "_rmc_" + target.str() + "_";
  if (!blockName.startswith(name)) return false;
  // Now make sure the rest is an int
  APInt dummy;
  return !blockName.drop_front(name.size()).getAsInteger(10, dummy);
}

StringRef getStringArg(Value *v) {
  const Value *g = cast<Constant>(v)->stripPointerCasts();
  const Constant *ptr = cast<GlobalVariable>(g)->getInitializer();
  StringRef str = cast<ConstantDataSequential>(ptr)->getAsString();
  // Drop the '\0' at the end
  return str.drop_back();
}

std::vector<RMCEdge> RMCPass::findEdges(Function &F) {
  std::vector<RMCEdge> edges;

  for (inst_iterator is = inst_begin(F), ie = inst_end(F); is != ie;) {
    // Grab the instruction and advance the iterator at the start, since
    // we might delete the instruction.
    Instruction *i = &*is;
    is++;

    CallInst *load = dyn_cast<CallInst>(i);
    if (!load) continue;
    Function *target = load->getCalledFunction();
    // We look for calls to the bogus function
    // __rmc_edge_register, pull out the information about them,
    // and delete the calls.
    if (!target || target->getName() != "__rmc_edge_register") continue;

    // Pull out what the operands have to be.
    // We just assert if something is wrong, which is not great UX.
    bool isVisibility = cast<ConstantInt>(load->getOperand(0))
      ->getValue().getBoolValue();
    RMCEdgeType edgeType = isVisibility ? VisbilityEdge : ExecutionEdge;
    StringRef srcName = getStringArg(load->getOperand(1));
    StringRef dstName = getStringArg(load->getOperand(2));

    // Since multiple blocks can have the same tag, we search for
    // them by name.
    // We could make this more efficient by building maps but I don't think
    // it is going to matter.
    for (auto & src : F) {
      if (!nameMatches(src.getName(), srcName)) continue;
      for (auto & dst : F) {
        if (!nameMatches(dst.getName(), dstName)) continue;

        edges.push_back({edgeType, &src, &dst});
      }
    }

    // Delete the bogus call.
    i->eraseFromParent();
  }

  return edges;
}

void analyzeBlock(Block &info) {
  LoadInst *soleLoad = nullptr;
  for (auto & i : *info.bb) {
    if (LoadInst *load = dyn_cast<LoadInst>(&i)) {
      info.loads++;
      soleLoad = load;
    } else if (isa<StoreInst>(i)) {
      info.stores++;
    // What else counts as a call? I'm counting fences I guess.
    } else if (isa<CallInst>(i) || isa<FenceInst>(i)) {
      info.calls++;
    } else if (isa<AtomicCmpXchgInst>(i) || isa<AtomicRMWInst>(i)) {
      info.RMWs++;
    }
  }
  // Try to characterize this block's actions
  // These categories might not be the best.
  if (info.loads == 1 && info.stores+info.calls+info.RMWs == 0) {
    info.soleLoad = soleLoad;
    info.type = BlockSimpleRead;
  } else if (info.stores >= 1 && info.loads+info.calls+info.RMWs == 0) {
    info.type = BlockSimpleWrites;
  } else if (info.RMWs == 1 && info.stores+info.loads+info.calls == 0) {
    info.type = BlockSimpleRMW;
  } else {
    info.type = BlockComplex;
  }
}

void RMCPass::buildGraph(std::vector<RMCEdge> &edges, Function &F) {
  // First, collect all the basic blocks with edges attached to them
  SmallPtrSet<BasicBlock *, 8> basicBlocks;
  for (auto & edge : edges) {
    basicBlocks.insert(edge.src);
    basicBlocks.insert(edge.dst);
  }

  // Now, make the vector of blocks and a mapping from BasicBlock *.
  blocks_.reserve(basicBlocks.size());
  for (auto bb : basicBlocks) {
    blocks_.emplace_back(bb);
    bb2block_[bb] = &blocks_.back();
  }

  // Analyze the instructions in blocks to see what sort of actions
  // they peform.
  for (auto & block : blocks_) {
    analyzeBlock(block);
  }

  // Build our list of edges into a more explicit graph
  for (auto & edge : edges) {
    Block *src = bb2block_[edge.src];
    Block *dst = bb2block_[edge.dst];
    if (edge.edgeType == VisbilityEdge) {
      src->visEdges.insert(dst);
    } else {
      src->execEdges.insert(dst);
    }
  }
}

bool RMCPass::runOnFunction(Function &F) {
  auto edges = findEdges(F);
  if (edges.empty()) return false;

  for (auto & edge : edges) {
    errs() << "Found an edge: " << edge << "\n";
  }

  buildGraph(edges, F);

  // Clear our data structures to save memory, make things clean for
  // future runs.
  blocks_.clear();
  bb2block_.clear();
  cuts_.clear();

  return true;
}

char RMCPass::ID = 0;
RegisterPass<RMCPass> X("rmc-pass", "RMC");
}
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "tabsettings.h"

#include <QtCore/QDebug>
#include <QtCore/QSettings>
#include <QtCore/QString>
#include <QtGui/QTextCursor>
#include <QtGui/QTextDocument>

static const char *spacesForTabsKey = "SpacesForTabs";
static const char *autoSpacesForTabsKey = "AutoSpacesForTabs";
static const char *smartBackspaceKey = "SmartBackspace";
static const char *autoIndentKey = "AutoIndent";
static const char *tabSizeKey = "TabSize";
static const char *indentSizeKey = "IndentSize";
static const char *indentBracesKey = "IndentBraces";
static const char *doubleIndentBlocksKey = "DoubleIndentBlocks";
static const char *tabKeyBehaviorKey = "TabKeyBehavior";
static const char *groupPostfix = "TabSettings";

namespace TextEditor {

TabSettings::TabSettings() :
    m_spacesForTabs(true),
    m_autoSpacesForTabs(false),
    m_autoIndent(true),
    m_smartBackspace(false),
    m_tabSize(8),
    m_indentSize(4),
    m_indentBraces(false),
    m_doubleIndentBlocks(false),
    m_tabKeyBehavior(TabNeverIndents)
{
}

void TabSettings::toSettings(const QString &category, QSettings *s) const
{
    QString group = QLatin1String(groupPostfix);
    if (!category.isEmpty())
        group.insert(0, category);
    s->beginGroup(group);
    s->setValue(QLatin1String(spacesForTabsKey),  m_spacesForTabs);
    s->setValue(QLatin1String(autoSpacesForTabsKey),  m_autoSpacesForTabs);
    s->setValue(QLatin1String(autoIndentKey), m_autoIndent);
    s->setValue(QLatin1String(smartBackspaceKey), m_smartBackspace);
    s->setValue(QLatin1String(tabSizeKey), m_tabSize);
    s->setValue(QLatin1String(indentSizeKey), m_indentSize);
    s->setValue(QLatin1String(indentBracesKey), m_indentBraces);
    s->setValue(QLatin1String(doubleIndentBlocksKey), m_doubleIndentBlocks);
    s->setValue(QLatin1String(tabKeyBehaviorKey), m_tabKeyBehavior);
    s->endGroup();
}

void TabSettings::fromSettings(const QString &category, const QSettings *s)
{
    QString group = QLatin1String(groupPostfix);
    if (!category.isEmpty())
        group.insert(0, category);
    group += QLatin1Char('/');

    *this = TabSettings(); // Assign defaults

    m_spacesForTabs     = s->value(group + QLatin1String(spacesForTabsKey), m_spacesForTabs).toBool();
    m_autoSpacesForTabs = s->value(group + QLatin1String(autoSpacesForTabsKey), m_autoSpacesForTabs).toBool();
    m_autoIndent        = s->value(group + QLatin1String(autoIndentKey), m_autoIndent).toBool();
    m_smartBackspace    = s->value(group + QLatin1String(smartBackspaceKey), m_smartBackspace).toBool();
    m_tabSize           = s->value(group + QLatin1String(tabSizeKey), m_tabSize).toInt();
    m_indentSize        = s->value(group + QLatin1String(indentSizeKey), m_indentSize).toInt();
    m_indentBraces      = s->value(group + QLatin1String(indentBracesKey), m_indentBraces).toBool();
    m_doubleIndentBlocks
			= s->value(group + QLatin1String(doubleIndentBlocksKey), m_doubleIndentBlocks).toBool();

    m_tabKeyBehavior    = (TabKeyBehavior)s->value(group + QLatin1String(tabKeyBehaviorKey), m_tabKeyBehavior).toInt();
}

bool TabSettings::cursorIsAtBeginningOfLine(const QTextCursor &cursor) const
{
    QString text = cursor.block().text();
    int fns = firstNonSpace(text);
    return (cursor.position() - cursor.block().position() <= fns);
}

int TabSettings::lineIndentPosition(const QString &text) const
{
    int i = 0;
    while (i < text.size()) {
        if (!text.at(i).isSpace())
            break;
        ++i;
    }
    int column = columnAt(text, i);
    return i - (column % m_indentSize);
}

int TabSettings::firstNonSpace(const QString &text) const
{
    int i = 0;
    while (i < text.size()) {
        if (!text.at(i).isSpace())
            return i;
        ++i;
    }
    return i;
}

QString TabSettings::indentationString(const QString &text) const
{
    return text.left(firstNonSpace(text));
}

int TabSettings::indentationColumn(const QString &text) const
{
    return columnAt(text, firstNonSpace(text));
}

int TabSettings::maximumPadding(const QString &text) const
{
    int fns = columnAt(text, firstNonSpace(text));
    int i = fns;
    while (i > 0) {
        if (text.at(i-1) != QLatin1Char(' '))
            break;
        --i;
    }
    return fns - i;
}

int TabSettings::trailingWhitespaces(const QString &text) const
{
    int i = 0;
    while (i < text.size()) {
        if (!text.at(text.size()-1-i).isSpace())
            return i;
        ++i;
    }
    return i;
}

bool TabSettings::isIndentationClean(const QTextBlock &block) const
{
    int i = 0;
    int spaceCount = 0;
    QString text = block.text();
    bool spacesForTabs = guessSpacesForTabs(block);
    while (i < text.size()) {
        QChar c = text.at(i);
        if (!c.isSpace())
            return true;

        if (c == QLatin1Char(' ')) {
            ++spaceCount;
            if (!spacesForTabs && spaceCount == m_tabSize)
                return false;
        } else if (c == QLatin1Char('\t')) {
            if (spacesForTabs || spaceCount != m_indentSize)
                return false;
            spaceCount = 0;
        }
        ++i;
    }
    return true;
}

bool TabSettings::tabShouldIndent(const QTextDocument *document, QTextCursor cursor, int *suggestedPosition) const
{
    if (m_tabKeyBehavior == TabNeverIndents)
        return false;
    QTextCursor tc = cursor;
    if (suggestedPosition)
        *suggestedPosition = tc.position(); // At least suggest original position
    tc.movePosition(QTextCursor::StartOfLine);
    if (tc.atBlockEnd()) // cursor was on a blank line
        return true;
    if (document->characterAt(tc.position()).isSpace()) {
        tc.movePosition(QTextCursor::WordRight);
        if (tc.positionInBlock() >= cursor.positionInBlock()) {
            if (suggestedPosition)
                *suggestedPosition = tc.position(); // Suggest position after whitespace
            if (m_tabKeyBehavior == TabLeadingWhitespaceIndents)
                return true;
        }
    }
    return (m_tabKeyBehavior == TabAlwaysIndents);
}

int TabSettings::columnAt(const QString &text, int position) const
{
    int column = 0;
    for (int i = 0; i < position; ++i) {
        if (text.at(i) == QLatin1Char('\t'))
            column = column - (column % m_tabSize) + m_tabSize;
        else
            ++column;
    }
    return column;
}

int TabSettings::spacesLeftFromPosition(const QString &text, int position) const
{
    int i = position;
    while (i > 0) {
        if (!text.at(i-1).isSpace())
            break;
        --i;
    }
    return position - i;
}

int TabSettings::indentedColumn(int column, bool doIndent) const
{
    int aligned = (column / m_indentSize) * m_indentSize;
    if (doIndent)
        return aligned + m_indentSize;
    if (aligned < column)
        return aligned;
    return qMax(0, aligned - m_indentSize);
}

bool TabSettings::guessSpacesForTabs(const QTextBlock &_block) const
{
    if (m_spacesForTabs && m_autoSpacesForTabs && _block.isValid()) {
        const QTextDocument *doc = _block.document();
        QVector<QTextBlock> currentBlocks(2, _block); // [0] looks back; [1] looks forward
        int maxLookAround = 100;
        while (maxLookAround-- > 0) {
            if (currentBlocks.at(0).isValid())
                currentBlocks[0] = currentBlocks.at(0).previous();
            if (currentBlocks.at(1).isValid())
                currentBlocks[1] = currentBlocks.at(1).next();
            bool done = true;
            foreach (const QTextBlock &block, currentBlocks) {
                if (block.isValid())
                    done = false;
                if (!block.isValid() || block.length() == 0)
                    continue;
                const QChar firstChar = doc->characterAt(block.position());
                if (firstChar == QLatin1Char(' ')) {
                    return true;
                } else if (firstChar == QLatin1Char('\t')) {
                    return false;
                }
            }
            if (done)
                break;
        }
    }
    return m_spacesForTabs;
}

QString TabSettings::indentationString(int startColumn, int targetColumn, const QTextBlock &block) const
{
    targetColumn = qMax(startColumn, targetColumn);
    if (guessSpacesForTabs(block))
        return QString(targetColumn - startColumn, QLatin1Char(' '));

    QString s;
    int alignedStart = startColumn - (startColumn % m_tabSize) + m_tabSize;
    if (alignedStart > startColumn && alignedStart <= targetColumn) {
        s += QLatin1Char('\t');
        startColumn = alignedStart;
    }
    if (int columns = targetColumn - startColumn) {
        int tabs = columns / m_tabSize;
        s += QString(tabs, QLatin1Char('\t'));
        s += QString(columns - tabs * m_tabSize, QLatin1Char(' '));
    }
    return s;
}

void TabSettings::indentLine(QTextBlock block, int newIndent, int padding) const
{
    const QString text = block.text();
    const int oldBlockLength = text.size();

    // Quickly check whether indenting is required.
    if (indentationColumn(text) == newIndent)
        return;

    QString indentString;

    if (!m_spacesForTabs && m_tabSize == m_indentSize) {
        // user likes tabs for spaces and uses tabs for indentation, preserve padding
        indentString = indentationString(0, newIndent - padding, block);
        indentString += QString(padding, QLatin1Char(' '));
    } else {
        indentString = indentationString(0, newIndent, block);
    }

    if (oldBlockLength == indentString.length() && text == indentString)
        return;

    QTextCursor cursor(block);
    cursor.beginEditBlock();
    cursor.movePosition(QTextCursor::StartOfBlock);
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace(text));
    cursor.removeSelectedText();
    cursor.insertText(indentString);
    cursor.endEditBlock();
}

void TabSettings::reindentLine(QTextBlock block, int delta) const
{
    const QString text = block.text();
    const int oldBlockLength = text.size();

    int oldIndent = indentationColumn(text);
    int newIndent = qMax(oldIndent + delta, 0);

    if (oldIndent == newIndent)
        return;

    QString indentString;
    if (!m_spacesForTabs && m_tabSize == m_indentSize) {
        // user likes tabs for spaces and uses tabs for indentation, preserve padding
        int padding = qMin(maximumPadding(text), newIndent);
        indentString = indentationString(0, newIndent - padding, block);
        indentString += QString(padding, QLatin1Char(' '));
    } else {
        indentString = indentationString(0, newIndent, block);
    }

    if (oldBlockLength == indentString.length() && text == indentString)
        return;

    QTextCursor cursor(block);
    cursor.beginEditBlock();
    cursor.movePosition(QTextCursor::StartOfBlock);
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace(text));
    cursor.removeSelectedText();
    cursor.insertText(indentString);
    cursor.endEditBlock();
}

bool TabSettings::equals(const TabSettings &ts) const
{
    return m_spacesForTabs == ts.m_spacesForTabs
        && m_autoSpacesForTabs == ts.m_autoSpacesForTabs
        && m_autoIndent == ts.m_autoIndent
        && m_smartBackspace == ts.m_smartBackspace
        && m_tabSize == ts.m_tabSize
        && m_indentSize == ts.m_indentSize
        && m_indentBraces == ts.m_indentBraces
	&& m_doubleIndentBlocks == ts.m_doubleIndentBlocks
        && m_tabKeyBehavior == ts.m_tabKeyBehavior;
}

} // namespace TextEditor
<commit_msg>fix crash<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "tabsettings.h"

#include <QtCore/QDebug>
#include <QtCore/QSettings>
#include <QtCore/QString>
#include <QtGui/QTextCursor>
#include <QtGui/QTextDocument>

static const char *spacesForTabsKey = "SpacesForTabs";
static const char *autoSpacesForTabsKey = "AutoSpacesForTabs";
static const char *smartBackspaceKey = "SmartBackspace";
static const char *autoIndentKey = "AutoIndent";
static const char *tabSizeKey = "TabSize";
static const char *indentSizeKey = "IndentSize";
static const char *indentBracesKey = "IndentBraces";
static const char *doubleIndentBlocksKey = "DoubleIndentBlocks";
static const char *tabKeyBehaviorKey = "TabKeyBehavior";
static const char *groupPostfix = "TabSettings";

namespace TextEditor {

TabSettings::TabSettings() :
    m_spacesForTabs(true),
    m_autoSpacesForTabs(false),
    m_autoIndent(true),
    m_smartBackspace(false),
    m_tabSize(8),
    m_indentSize(4),
    m_indentBraces(false),
    m_doubleIndentBlocks(false),
    m_tabKeyBehavior(TabNeverIndents)
{
}

void TabSettings::toSettings(const QString &category, QSettings *s) const
{
    QString group = QLatin1String(groupPostfix);
    if (!category.isEmpty())
        group.insert(0, category);
    s->beginGroup(group);
    s->setValue(QLatin1String(spacesForTabsKey),  m_spacesForTabs);
    s->setValue(QLatin1String(autoSpacesForTabsKey),  m_autoSpacesForTabs);
    s->setValue(QLatin1String(autoIndentKey), m_autoIndent);
    s->setValue(QLatin1String(smartBackspaceKey), m_smartBackspace);
    s->setValue(QLatin1String(tabSizeKey), m_tabSize);
    s->setValue(QLatin1String(indentSizeKey), m_indentSize);
    s->setValue(QLatin1String(indentBracesKey), m_indentBraces);
    s->setValue(QLatin1String(doubleIndentBlocksKey), m_doubleIndentBlocks);
    s->setValue(QLatin1String(tabKeyBehaviorKey), m_tabKeyBehavior);
    s->endGroup();
}

void TabSettings::fromSettings(const QString &category, const QSettings *s)
{
    QString group = QLatin1String(groupPostfix);
    if (!category.isEmpty())
        group.insert(0, category);
    group += QLatin1Char('/');

    *this = TabSettings(); // Assign defaults

    m_spacesForTabs     = s->value(group + QLatin1String(spacesForTabsKey), m_spacesForTabs).toBool();
    m_autoSpacesForTabs = s->value(group + QLatin1String(autoSpacesForTabsKey), m_autoSpacesForTabs).toBool();
    m_autoIndent        = s->value(group + QLatin1String(autoIndentKey), m_autoIndent).toBool();
    m_smartBackspace    = s->value(group + QLatin1String(smartBackspaceKey), m_smartBackspace).toBool();
    m_tabSize           = s->value(group + QLatin1String(tabSizeKey), m_tabSize).toInt();
    m_indentSize        = s->value(group + QLatin1String(indentSizeKey), m_indentSize).toInt();
    m_indentBraces      = s->value(group + QLatin1String(indentBracesKey), m_indentBraces).toBool();
    m_doubleIndentBlocks
			= s->value(group + QLatin1String(doubleIndentBlocksKey), m_doubleIndentBlocks).toBool();

    m_tabKeyBehavior    = (TabKeyBehavior)s->value(group + QLatin1String(tabKeyBehaviorKey), m_tabKeyBehavior).toInt();
}

bool TabSettings::cursorIsAtBeginningOfLine(const QTextCursor &cursor) const
{
    QString text = cursor.block().text();
    int fns = firstNonSpace(text);
    return (cursor.position() - cursor.block().position() <= fns);
}

int TabSettings::lineIndentPosition(const QString &text) const
{
    int i = 0;
    while (i < text.size()) {
        if (!text.at(i).isSpace())
            break;
        ++i;
    }
    int column = columnAt(text, i);
    return i - (column % m_indentSize);
}

int TabSettings::firstNonSpace(const QString &text) const
{
    int i = 0;
    while (i < text.size()) {
        if (!text.at(i).isSpace())
            return i;
        ++i;
    }
    return i;
}

QString TabSettings::indentationString(const QString &text) const
{
    return text.left(firstNonSpace(text));
}

int TabSettings::indentationColumn(const QString &text) const
{
    return columnAt(text, firstNonSpace(text));
}

int TabSettings::maximumPadding(const QString &text) const
{
    int fns = firstNonSpace(text);
    int i = fns;
    while (i > 0) {
        if (text.at(i-1) != QLatin1Char(' '))
            break;
        --i;
    }
    return fns - i;
}

int TabSettings::trailingWhitespaces(const QString &text) const
{
    int i = 0;
    while (i < text.size()) {
        if (!text.at(text.size()-1-i).isSpace())
            return i;
        ++i;
    }
    return i;
}

bool TabSettings::isIndentationClean(const QTextBlock &block) const
{
    int i = 0;
    int spaceCount = 0;
    QString text = block.text();
    bool spacesForTabs = guessSpacesForTabs(block);
    while (i < text.size()) {
        QChar c = text.at(i);
        if (!c.isSpace())
            return true;

        if (c == QLatin1Char(' ')) {
            ++spaceCount;
            if (!spacesForTabs && spaceCount == m_tabSize)
                return false;
        } else if (c == QLatin1Char('\t')) {
            if (spacesForTabs || spaceCount != m_indentSize)
                return false;
            spaceCount = 0;
        }
        ++i;
    }
    return true;
}

bool TabSettings::tabShouldIndent(const QTextDocument *document, QTextCursor cursor, int *suggestedPosition) const
{
    if (m_tabKeyBehavior == TabNeverIndents)
        return false;
    QTextCursor tc = cursor;
    if (suggestedPosition)
        *suggestedPosition = tc.position(); // At least suggest original position
    tc.movePosition(QTextCursor::StartOfLine);
    if (tc.atBlockEnd()) // cursor was on a blank line
        return true;
    if (document->characterAt(tc.position()).isSpace()) {
        tc.movePosition(QTextCursor::WordRight);
        if (tc.positionInBlock() >= cursor.positionInBlock()) {
            if (suggestedPosition)
                *suggestedPosition = tc.position(); // Suggest position after whitespace
            if (m_tabKeyBehavior == TabLeadingWhitespaceIndents)
                return true;
        }
    }
    return (m_tabKeyBehavior == TabAlwaysIndents);
}

int TabSettings::columnAt(const QString &text, int position) const
{
    int column = 0;
    for (int i = 0; i < position; ++i) {
        if (text.at(i) == QLatin1Char('\t'))
            column = column - (column % m_tabSize) + m_tabSize;
        else
            ++column;
    }
    return column;
}

int TabSettings::spacesLeftFromPosition(const QString &text, int position) const
{
    int i = position;
    while (i > 0) {
        if (!text.at(i-1).isSpace())
            break;
        --i;
    }
    return position - i;
}

int TabSettings::indentedColumn(int column, bool doIndent) const
{
    int aligned = (column / m_indentSize) * m_indentSize;
    if (doIndent)
        return aligned + m_indentSize;
    if (aligned < column)
        return aligned;
    return qMax(0, aligned - m_indentSize);
}

bool TabSettings::guessSpacesForTabs(const QTextBlock &_block) const
{
    if (m_spacesForTabs && m_autoSpacesForTabs && _block.isValid()) {
        const QTextDocument *doc = _block.document();
        QVector<QTextBlock> currentBlocks(2, _block); // [0] looks back; [1] looks forward
        int maxLookAround = 100;
        while (maxLookAround-- > 0) {
            if (currentBlocks.at(0).isValid())
                currentBlocks[0] = currentBlocks.at(0).previous();
            if (currentBlocks.at(1).isValid())
                currentBlocks[1] = currentBlocks.at(1).next();
            bool done = true;
            foreach (const QTextBlock &block, currentBlocks) {
                if (block.isValid())
                    done = false;
                if (!block.isValid() || block.length() == 0)
                    continue;
                const QChar firstChar = doc->characterAt(block.position());
                if (firstChar == QLatin1Char(' ')) {
                    return true;
                } else if (firstChar == QLatin1Char('\t')) {
                    return false;
                }
            }
            if (done)
                break;
        }
    }
    return m_spacesForTabs;
}

QString TabSettings::indentationString(int startColumn, int targetColumn, const QTextBlock &block) const
{
    targetColumn = qMax(startColumn, targetColumn);
    if (guessSpacesForTabs(block))
        return QString(targetColumn - startColumn, QLatin1Char(' '));

    QString s;
    int alignedStart = startColumn - (startColumn % m_tabSize) + m_tabSize;
    if (alignedStart > startColumn && alignedStart <= targetColumn) {
        s += QLatin1Char('\t');
        startColumn = alignedStart;
    }
    if (int columns = targetColumn - startColumn) {
        int tabs = columns / m_tabSize;
        s += QString(tabs, QLatin1Char('\t'));
        s += QString(columns - tabs * m_tabSize, QLatin1Char(' '));
    }
    return s;
}

void TabSettings::indentLine(QTextBlock block, int newIndent, int padding) const
{
    const QString text = block.text();
    const int oldBlockLength = text.size();

    // Quickly check whether indenting is required.
    if (indentationColumn(text) == newIndent)
        return;

    QString indentString;

    if (!m_spacesForTabs && m_tabSize == m_indentSize) {
        // user likes tabs for spaces and uses tabs for indentation, preserve padding
        indentString = indentationString(0, newIndent - padding, block);
        indentString += QString(padding, QLatin1Char(' '));
    } else {
        indentString = indentationString(0, newIndent, block);
    }

    if (oldBlockLength == indentString.length() && text == indentString)
        return;

    QTextCursor cursor(block);
    cursor.beginEditBlock();
    cursor.movePosition(QTextCursor::StartOfBlock);
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace(text));
    cursor.removeSelectedText();
    cursor.insertText(indentString);
    cursor.endEditBlock();
}

void TabSettings::reindentLine(QTextBlock block, int delta) const
{
    const QString text = block.text();
    const int oldBlockLength = text.size();

    int oldIndent = indentationColumn(text);
    int newIndent = qMax(oldIndent + delta, 0);

    if (oldIndent == newIndent)
        return;

    QString indentString;
    if (!m_spacesForTabs && m_tabSize == m_indentSize) {
        // user likes tabs for spaces and uses tabs for indentation, preserve padding
        int padding = qMin(maximumPadding(text), newIndent);
        indentString = indentationString(0, newIndent - padding, block);
        indentString += QString(padding, QLatin1Char(' '));
    } else {
        indentString = indentationString(0, newIndent, block);
    }

    if (oldBlockLength == indentString.length() && text == indentString)
        return;

    QTextCursor cursor(block);
    cursor.beginEditBlock();
    cursor.movePosition(QTextCursor::StartOfBlock);
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace(text));
    cursor.removeSelectedText();
    cursor.insertText(indentString);
    cursor.endEditBlock();
}

bool TabSettings::equals(const TabSettings &ts) const
{
    return m_spacesForTabs == ts.m_spacesForTabs
        && m_autoSpacesForTabs == ts.m_autoSpacesForTabs
        && m_autoIndent == ts.m_autoIndent
        && m_smartBackspace == ts.m_smartBackspace
        && m_tabSize == ts.m_tabSize
        && m_indentSize == ts.m_indentSize
        && m_indentBraces == ts.m_indentBraces
	&& m_doubleIndentBlocks == ts.m_doubleIndentBlocks
        && m_tabKeyBehavior == ts.m_tabKeyBehavior;
}

} // namespace TextEditor
<|endoftext|>


<commit_before>#include "libpstack/elf.h"
#ifdef WITH_ZLIB
#include "libpstack/inflatereader.h"
#endif
#ifdef WITH_LZMA
#include "libpstack/lzmareader.h"
#endif
#include "libpstack/util.h"

#include <unistd.h>

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <limits>

using std::string;
using std::make_shared;
using std::make_unique;

std::ostream *debug = &std::clog;
int verbose = 0;
static uint32_t elf_hash(const string &text);

GlobalDebugDirectories globalDebugDirectories;
GlobalDebugDirectories::GlobalDebugDirectories() throw()
{
   add("/usr/lib/debug");
   add("/usr/lib/debug/usr"); // Add as a hack for when linker loads from /lib, but package has /usr/lib
}

void
GlobalDebugDirectories::add(const string &str)
{
   dirs.push_back(str);
}

ElfNoteIter
ElfNotes::begin() const
{
   return ElfNoteIter(object, true);
}

ElfNoteIter
ElfNotes::end() const
{
   return ElfNoteIter(object, false);
}

string
ElfNoteDesc::name() const
{
   return io->readString(sizeof note);
}

Reader::csptr
ElfNoteDesc::data() const
{
   return make_shared<OffsetReader>(io, sizeof note + roundup2(note.n_namesz, 4), note.n_descsz);
}

size_t
ElfNoteDesc::size() const
{
   return note.n_descsz;
}

std::pair<const Elf_Sym, const string>
SymbolIterator::operator *()
{
    auto sym = sec->symbols->readObj<Elf_Sym>(off);
    string name = sec->strings->readString(sym.st_name);
    return std::make_pair(sym, name);
}

ElfObject::ElfObject(ImageCache &cache, Reader::csptr io_)
    : io(std::move(io_))
    , notes(this)
    , elfHeader(io->readObj<Elf_Ehdr>(0))
    , imageCache(cache)
{
    debugLoaded = false;
    int i;
    size_t off;

    /* Validate the ELF header */
    if (!IS_ELF(elfHeader) || elfHeader.e_ident[EI_VERSION] != EV_CURRENT)
        throw (Exception() << *io << ": content is not an ELF image");

    for (off = elfHeader.e_phoff, i = 0; i < elfHeader.e_phnum; i++) {
        auto hdr = io->readObj<Elf_Phdr>(off);
        programHeaders[hdr.p_type].push_back(hdr);
        off += elfHeader.e_phentsize;
    }

    if (elfHeader.e_shnum == 0) {
        sectionHeaders.push_back(ElfSection());
    } else {
        sectionHeaders.resize(elfHeader.e_shnum);
        for (off = elfHeader.e_shoff, i = 0; i < elfHeader.e_shnum; i++) {
            sectionHeaders[i].open(io, off);
            off += elfHeader.e_shentsize;
        }
    }

    if (elfHeader.e_shstrndx != SHN_UNDEF) {
        auto &sshdr = sectionHeaders[elfHeader.e_shstrndx];
        for (auto &h : sectionHeaders) {
            auto name = sshdr.io->readString(h.shdr.sh_name);
            namedSection[name] = &h;
            // .gnu_debugdata is a separate LZMA-compressed ELF image with just
            // a symbol table.
            if (name == ".gnu_debugdata") {
#ifdef WITH_LZMA
                debugData = make_shared<ElfObject>(imageCache,
                      make_shared<const LzmaReader>(h.io));
#else
                static bool warned = false;
                if (!warned) {
                    std::clog << "warning: no compiled support for LZMA - "
                          "can't decode debug data in " << *io << "\n";
                    warned = true;
                }
#endif
            }
        }
        auto &tab = getSection(".hash", SHT_HASH);
        auto &syms = getLinkedSection(tab);
        auto &strings = getLinkedSection(syms);
        if (tab && syms && strings)
            hash = make_unique<ElfSymHash>(tab.io, syms.io, strings.io);
    } else {
        hash = nullptr;
    }
}

const Elf_Phdr *
ElfObject::getSegmentForAddress(Elf_Off a) const
{
    for (const auto &hdr : getSegments(PT_LOAD))
        if (hdr.p_vaddr <= a && hdr.p_vaddr + hdr.p_memsz > a)
            return &hdr;
    return nullptr;
}

const ElfObject::ProgramHeaders &
ElfObject::getSegments(Elf_Word type) const
{
    auto it = programHeaders.find(type);
    if (it == programHeaders.end()) {
        static const ProgramHeaders empty;
        return empty;
    }
    return it->second;
}

string
ElfObject::getInterpreter() const
{
    for (auto &seg : getSegments(PT_INTERP))
        return io->readString(seg.p_offset);
    return "";
}

/*
 * Find the symbol that represents a particular address.
 */
bool
ElfObject::findSymbolByAddress(Elf_Addr addr, int type, Elf_Sym &sym, string &name)
{
    /* Try to find symbols in these sections */
    for (auto secname : { ".symtab", ".dynsym" }) {
        const auto &symSection = getSection(secname, SHT_NULL);
        if (symSection.shdr.sh_type == SHT_NOBITS)
            continue;
        SymbolSection syms(symSection.io, getLinkedSection(symSection).io);
        for (auto syminfo : syms) {
            auto &candidate = syminfo.first;
            if (candidate.st_shndx >= sectionHeaders.size())
                continue;
            if (type != STT_NOTYPE && ELF_ST_TYPE(candidate.st_info) != type)
                continue;
            if (candidate.st_value > addr)
                continue;
            if (candidate.st_size + candidate.st_value < addr) // allow up to and just past the symbol
                continue;
            auto &sec = sectionHeaders[candidate.st_shndx];
            if ((sec.shdr.sh_flags & SHF_ALLOC) == 0)
                continue;
            sym = candidate;
            name = syminfo.second;
            return true;
        }
    }
    if (debugData)
        return debugData->findSymbolByAddress(addr, type, sym, name);
    return false;;
}

const ElfSection &
ElfObject::getSection(const string &name, Elf_Word type) const
{
    auto s = namedSection.find(name);
    if (s == namedSection.end() || (s->second->shdr.sh_type != type && type != SHT_NULL)) {
        ElfObject *debug = getDebug();
        if (debug)
            return debug->getSection(name, type);
        return sectionHeaders[0];
    }
    return *s->second;
}

const ElfSection &
ElfObject::getSection(Elf_Word idx) const
{
    if (sectionHeaders[idx].shdr.sh_type != SHT_NULL)
        return sectionHeaders[idx];
    auto debug = getDebug();
    if (debug) {
        return debug->sectionHeaders[idx];
    }
    return sectionHeaders[0];
}

const ElfSection &
ElfObject::getLinkedSection(const ElfSection &from) const
{
    if (&from >= &sectionHeaders[0] && &from <= &sectionHeaders[sectionHeaders.size() - 1])
        return sectionHeaders[from.shdr.sh_link];
    return getDebug()->sectionHeaders[from.shdr.sh_link];
}

SymbolSection
ElfObject::getSymbols(const string &tableName)
{
    auto &table = getSection(tableName, SHT_NULL);
    string n = stringify(*io);
    if (table.shdr.sh_type == SHT_NOBITS || table.shdr.sh_type == SHT_NULL)
        return SymbolSection(sectionHeaders[0].io, sectionHeaders[0].io);
    auto &strings = getLinkedSection(table);
    return SymbolSection(table.io, strings.io);
}

static int symcacheAccess = 0;
static int symcacheHit = 0;
static struct X {
    ~X() {
        if (symcacheAccess)
            std::clog
                << "symcache: access " << symcacheAccess
                << ", hit: " << symcacheHit
                << ", rate: " << symcacheHit / symcacheAccess
                << std::endl;
    }
} x;
/*
 * Locate a named symbol in an ELF image.
 */
bool
ElfObject::findSymbolByName(const string &name, Elf_Sym &sym)
{
    auto &syment = cachedSymbols[name];

    auto findUncached  = [&](Elf_Sym &sym) {
        if (hash && hash->findSymbol(sym, name))
            return CachedSymbol::SYM_FOUND;

        for (const char *sec : { ".dynsym", ".symtab" }) {
            SymbolSection sect = getSymbols(sec);
            if (sect.linearSearch(name, sym))
                return CachedSymbol::SYM_FOUND;
        }
        return CachedSymbol::SYM_NOTFOUND;
    };
    symcacheAccess++;
    if (syment.disposition == CachedSymbol::SYM_NEW) {
        syment.disposition = findUncached(syment.sym);
    } else {
        symcacheHit++;
    }
    sym = syment.sym;
    return syment.disposition == CachedSymbol::SYM_FOUND;
}

ElfObject::~ElfObject() = default;

ElfObject *
ElfObject::getDebug() const
{
    if (!debugLoaded) {
        debugLoaded = true;
        auto &hdr = getSection(".gnu_debuglink", SHT_PROGBITS);
        if (!hdr) {
            return 0;
        }
        auto link = hdr.io->readString(0);
        auto dir = dirname(stringify(*io));
        debugObject = imageCache.getDebugImage(dir + "/" + link);

        if (!debugObject) {
            for (auto note : notes) {
                if (note.name() == "GNU" && note.type() == GNU_BUILD_ID) {
                    std::ostringstream dir;
                    dir << ".build-id/";
                    size_t i;
                    auto io = note.data();
                    std::vector<unsigned char> data(io->size());
                    io->readObj(0, &data[0], io->size());
                    dir << std::hex << std::setw(2) << std::setfill('0') << int(data[0]);
                    dir << "/";
                    for (i = 1; i < note.size(); ++i)
                        dir << std::setw(2) << int(data[i]);
                    dir << ".debug";
                    debugObject = imageCache.getDebugImage(dir.str());
                    break;
                }
            }
        }
        if (debugObject && verbose >= 2)
            *debug << "found debug object " << *debugObject->io << " for " << *io << "\n";
    }
    return debugObject.get();
}

bool
SymbolSection::linearSearch(const string &name, Elf_Sym &sym)
{
    for (const auto &info : *this) {
        if (name == info.second) {
            sym = info.first;
            return true;
        }
    }
    return false;
}

ElfSymHash::ElfSymHash(Reader::csptr hash_,
      Reader::csptr syms_, Reader::csptr strings_)
    : hash(std::move(hash_))
    , syms(std::move(syms_))
    , strings(std::move(strings_))
{
    // read the hash table into local memory.
    size_t words = hash->size() / sizeof (Elf_Word);
    data.resize(words);
    hash->readObj(0, &data[0], words);
    nbucket = data[0];
    nchain = data[1];
    buckets = &data[0] + 2;
    chains = buckets + nbucket;
}

bool
ElfSymHash::findSymbol(Elf_Sym &sym, const string &name)
{
    uint32_t bucket = elf_hash(name) % nbucket;
    for (Elf_Word i = buckets[bucket]; i != STN_UNDEF; i = chains[i]) {
        auto candidate = syms->readObj<Elf_Sym>(i * sizeof (Elf_Sym));
        auto candidateName = strings->readString(candidate.st_name);
        if (candidateName == name) {
            sym = candidate;
            return true;
        }
    }
    return false;
}
/*
 * Culled from System V Application Binary Interface
 */
static uint32_t
elf_hash(const string &text)
{
    uint32_t h = 0, g;
    for (auto c : text) {
        h = (h << 4) + c;
        if ((g = h & 0xf0000000) != 0)
            h ^= g >> 24;
        h &= ~g;
    }
    return (h);
}

void
ElfSection::open(const Reader::csptr &image, off_t off)
{
    image->readObj(off, &shdr);

    // Null sections get null readers.
    if (shdr.sh_type == SHT_NULL) {
        io = make_shared<NullReader>();
        return;
    }

    auto rawIo = make_shared<OffsetReader>(image, shdr.sh_offset, shdr.sh_size);
    if ((shdr.sh_flags & SHF_COMPRESSED) == 0) {
        io = rawIo;
    } else {

#ifdef WITH_ZLIB
        auto chdr = rawIo->readObj<Elf_Chdr>(0);
        io = make_shared<InflateReader>(chdr.ch_size, OffsetReader(rawIo,
                 sizeof chdr, shdr.sh_size - sizeof chdr));
#else
        static bool warned = false;
        if (!warned) {
            warned = true;
            std::clog <<"warning: no support configured for compressed debug info in "
               << *image << std::endl;
        }
        io = make_shared<NullReader>();
#endif
    }
}

ElfObject::sptr
ImageCache::getImageForName(const string &name) {
    bool found;
    auto res = getImageIfLoaded(name, found);
    if (found) {
        if (res != nullptr)
            return res;
        // Don't return null to keep it consistent with a previous failure to load.
        throw (Exception() << "previously failed to load " << name);
    }
    auto &item = cache[name];
    item = make_shared<ElfObject>(*this, loadFile(name));
    return item;
}

ImageCache::ImageCache() : elfHits(0), elfLookups(0) {}
ImageCache::~ImageCache() {
    if (verbose >= 2) {
        *debug << "ELF image cache: lookups: " << elfLookups << ", hits=" << elfHits << std::endl;
        for (auto &items : cache) {
            if (items.second)
                *debug << "\t" << *items.second->io << std::endl;
            else
                *debug << "\t" << "NEGATIVE: " << items.first << std::endl;
        }
    }
}

ElfObject::sptr
ImageCache::getImageIfLoaded(const string &name, bool &found)
{
    elfLookups++;
    auto it = cache.find(name);
    if (it != cache.end()) {
        elfHits++;
        found = true;
        return it->second;
    }
    found = false;
    return ElfObject::sptr();
}

ElfObject::sptr
ImageCache::getDebugImage(const string &name) {
    // XXX: verify checksum.
    for (const auto &dir : globalDebugDirectories.dirs) {
        bool found;
        auto img = getImageIfLoaded(stringify(dir, "/", name), found);
        if (found)
            return img;
    }
    for (const auto &dir : globalDebugDirectories.dirs) {
        try {
           return getImageForName(stringify(dir, "/", name));
        }
        catch (const std::exception &ex) {
            continue;
        }
    }
    return ElfObject::sptr();
}
<commit_msg>Remove cache counts<commit_after>#include "libpstack/elf.h"
#ifdef WITH_ZLIB
#include "libpstack/inflatereader.h"
#endif
#ifdef WITH_LZMA
#include "libpstack/lzmareader.h"
#endif
#include "libpstack/util.h"

#include <unistd.h>

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <limits>

using std::string;
using std::make_shared;
using std::make_unique;

std::ostream *debug = &std::clog;
int verbose = 0;
static uint32_t elf_hash(const string &text);

GlobalDebugDirectories globalDebugDirectories;
GlobalDebugDirectories::GlobalDebugDirectories() throw()
{
   add("/usr/lib/debug");
   add("/usr/lib/debug/usr"); // Add as a hack for when linker loads from /lib, but package has /usr/lib
}

void
GlobalDebugDirectories::add(const string &str)
{
   dirs.push_back(str);
}

ElfNoteIter
ElfNotes::begin() const
{
   return ElfNoteIter(object, true);
}

ElfNoteIter
ElfNotes::end() const
{
   return ElfNoteIter(object, false);
}

string
ElfNoteDesc::name() const
{
   return io->readString(sizeof note);
}

Reader::csptr
ElfNoteDesc::data() const
{
   return make_shared<OffsetReader>(io, sizeof note + roundup2(note.n_namesz, 4), note.n_descsz);
}

size_t
ElfNoteDesc::size() const
{
   return note.n_descsz;
}

std::pair<const Elf_Sym, const string>
SymbolIterator::operator *()
{
    auto sym = sec->symbols->readObj<Elf_Sym>(off);
    string name = sec->strings->readString(sym.st_name);
    return std::make_pair(sym, name);
}

ElfObject::ElfObject(ImageCache &cache, Reader::csptr io_)
    : io(std::move(io_))
    , notes(this)
    , elfHeader(io->readObj<Elf_Ehdr>(0))
    , imageCache(cache)
{
    debugLoaded = false;
    int i;
    size_t off;

    /* Validate the ELF header */
    if (!IS_ELF(elfHeader) || elfHeader.e_ident[EI_VERSION] != EV_CURRENT)
        throw (Exception() << *io << ": content is not an ELF image");

    for (off = elfHeader.e_phoff, i = 0; i < elfHeader.e_phnum; i++) {
        auto hdr = io->readObj<Elf_Phdr>(off);
        programHeaders[hdr.p_type].push_back(hdr);
        off += elfHeader.e_phentsize;
    }

    if (elfHeader.e_shnum == 0) {
        sectionHeaders.push_back(ElfSection());
    } else {
        sectionHeaders.resize(elfHeader.e_shnum);
        for (off = elfHeader.e_shoff, i = 0; i < elfHeader.e_shnum; i++) {
            sectionHeaders[i].open(io, off);
            off += elfHeader.e_shentsize;
        }
    }

    if (elfHeader.e_shstrndx != SHN_UNDEF) {
        auto &sshdr = sectionHeaders[elfHeader.e_shstrndx];
        for (auto &h : sectionHeaders) {
            auto name = sshdr.io->readString(h.shdr.sh_name);
            namedSection[name] = &h;
            // .gnu_debugdata is a separate LZMA-compressed ELF image with just
            // a symbol table.
            if (name == ".gnu_debugdata") {
#ifdef WITH_LZMA
                debugData = make_shared<ElfObject>(imageCache,
                      make_shared<const LzmaReader>(h.io));
#else
                static bool warned = false;
                if (!warned) {
                    std::clog << "warning: no compiled support for LZMA - "
                          "can't decode debug data in " << *io << "\n";
                    warned = true;
                }
#endif
            }
        }
        auto &tab = getSection(".hash", SHT_HASH);
        auto &syms = getLinkedSection(tab);
        auto &strings = getLinkedSection(syms);
        if (tab && syms && strings)
            hash = make_unique<ElfSymHash>(tab.io, syms.io, strings.io);
    } else {
        hash = nullptr;
    }
}

const Elf_Phdr *
ElfObject::getSegmentForAddress(Elf_Off a) const
{
    for (const auto &hdr : getSegments(PT_LOAD))
        if (hdr.p_vaddr <= a && hdr.p_vaddr + hdr.p_memsz > a)
            return &hdr;
    return nullptr;
}

const ElfObject::ProgramHeaders &
ElfObject::getSegments(Elf_Word type) const
{
    auto it = programHeaders.find(type);
    if (it == programHeaders.end()) {
        static const ProgramHeaders empty;
        return empty;
    }
    return it->second;
}

string
ElfObject::getInterpreter() const
{
    for (auto &seg : getSegments(PT_INTERP))
        return io->readString(seg.p_offset);
    return "";
}

/*
 * Find the symbol that represents a particular address.
 */
bool
ElfObject::findSymbolByAddress(Elf_Addr addr, int type, Elf_Sym &sym, string &name)
{
    /* Try to find symbols in these sections */
    for (auto secname : { ".symtab", ".dynsym" }) {
        const auto &symSection = getSection(secname, SHT_NULL);
        if (symSection.shdr.sh_type == SHT_NOBITS)
            continue;
        SymbolSection syms(symSection.io, getLinkedSection(symSection).io);
        for (auto syminfo : syms) {
            auto &candidate = syminfo.first;
            if (candidate.st_shndx >= sectionHeaders.size())
                continue;
            if (type != STT_NOTYPE && ELF_ST_TYPE(candidate.st_info) != type)
                continue;
            if (candidate.st_value > addr)
                continue;
            if (candidate.st_size + candidate.st_value < addr) // allow up to and just past the symbol
                continue;
            auto &sec = sectionHeaders[candidate.st_shndx];
            if ((sec.shdr.sh_flags & SHF_ALLOC) == 0)
                continue;
            sym = candidate;
            name = syminfo.second;
            return true;
        }
    }
    if (debugData)
        return debugData->findSymbolByAddress(addr, type, sym, name);
    return false;;
}

const ElfSection &
ElfObject::getSection(const string &name, Elf_Word type) const
{
    auto s = namedSection.find(name);
    if (s == namedSection.end() || (s->second->shdr.sh_type != type && type != SHT_NULL)) {
        ElfObject *debug = getDebug();
        if (debug)
            return debug->getSection(name, type);
        return sectionHeaders[0];
    }
    return *s->second;
}

const ElfSection &
ElfObject::getSection(Elf_Word idx) const
{
    if (sectionHeaders[idx].shdr.sh_type != SHT_NULL)
        return sectionHeaders[idx];
    auto debug = getDebug();
    if (debug) {
        return debug->sectionHeaders[idx];
    }
    return sectionHeaders[0];
}

const ElfSection &
ElfObject::getLinkedSection(const ElfSection &from) const
{
    if (&from >= &sectionHeaders[0] && &from <= &sectionHeaders[sectionHeaders.size() - 1])
        return sectionHeaders[from.shdr.sh_link];
    return getDebug()->sectionHeaders[from.shdr.sh_link];
}

SymbolSection
ElfObject::getSymbols(const string &tableName)
{
    auto &table = getSection(tableName, SHT_NULL);
    string n = stringify(*io);
    if (table.shdr.sh_type == SHT_NOBITS || table.shdr.sh_type == SHT_NULL)
        return SymbolSection(sectionHeaders[0].io, sectionHeaders[0].io);
    auto &strings = getLinkedSection(table);
    return SymbolSection(table.io, strings.io);
}

/*
 * Locate a named symbol in an ELF image.
 */
bool
ElfObject::findSymbolByName(const string &name, Elf_Sym &sym)
{
    auto &syment = cachedSymbols[name];
    auto findUncached  = [&](Elf_Sym &sym) {
        if (hash && hash->findSymbol(sym, name))
            return CachedSymbol::SYM_FOUND;
        for (const char *sec : { ".dynsym", ".symtab" }) {
            SymbolSection sect = getSymbols(sec);
            if (sect.linearSearch(name, sym))
                return CachedSymbol::SYM_FOUND;
        }
        return CachedSymbol::SYM_NOTFOUND;
    };
    if (syment.disposition == CachedSymbol::SYM_NEW)
        syment.disposition = findUncached(syment.sym);
    sym = syment.sym;
    return syment.disposition == CachedSymbol::SYM_FOUND;
}

ElfObject::~ElfObject() = default;

ElfObject *
ElfObject::getDebug() const
{
    if (!debugLoaded) {
        debugLoaded = true;
        auto &hdr = getSection(".gnu_debuglink", SHT_PROGBITS);
        if (!hdr)
            return 0;
        auto link = hdr.io->readString(0);
        auto dir = dirname(stringify(*io));
        debugObject = imageCache.getDebugImage(dir + "/" + link);
        if (!debugObject) {
            for (auto note : notes) {
                if (note.name() == "GNU" && note.type() == GNU_BUILD_ID) {
                    std::ostringstream dir;
                    dir << ".build-id/";
                    size_t i;
                    auto io = note.data();
                    std::vector<unsigned char> data(io->size());
                    io->readObj(0, &data[0], io->size());
                    dir << std::hex << std::setw(2) << std::setfill('0') << int(data[0]);
                    dir << "/";
                    for (i = 1; i < note.size(); ++i)
                        dir << std::setw(2) << int(data[i]);
                    dir << ".debug";
                    debugObject = imageCache.getDebugImage(dir.str());
                    break;
                }
            }
        }
        if (debugObject && verbose >= 2)
            *debug << "found debug object " << *debugObject->io << " for " << *io << "\n";
    }
    return debugObject.get();
}

bool
SymbolSection::linearSearch(const string &name, Elf_Sym &sym)
{
    for (const auto &info : *this) {
        if (name == info.second) {
            sym = info.first;
            return true;
        }
    }
    return false;
}

ElfSymHash::ElfSymHash(Reader::csptr hash_,
      Reader::csptr syms_, Reader::csptr strings_)
    : hash(std::move(hash_))
    , syms(std::move(syms_))
    , strings(std::move(strings_))
{
    // read the hash table into local memory.
    size_t words = hash->size() / sizeof (Elf_Word);
    data.resize(words);
    hash->readObj(0, &data[0], words);
    nbucket = data[0];
    nchain = data[1];
    buckets = &data[0] + 2;
    chains = buckets + nbucket;
}

bool
ElfSymHash::findSymbol(Elf_Sym &sym, const string &name)
{
    uint32_t bucket = elf_hash(name) % nbucket;
    for (Elf_Word i = buckets[bucket]; i != STN_UNDEF; i = chains[i]) {
        auto candidate = syms->readObj<Elf_Sym>(i * sizeof (Elf_Sym));
        auto candidateName = strings->readString(candidate.st_name);
        if (candidateName == name) {
            sym = candidate;
            return true;
        }
    }
    return false;
}

/*
 * Culled from System V Application Binary Interface
 */
static uint32_t
elf_hash(const string &text)
{
    uint32_t h = 0, g;
    for (auto c : text) {
        h = (h << 4) + c;
        if ((g = h & 0xf0000000) != 0)
            h ^= g >> 24;
        h &= ~g;
    }
    return (h);
}

void
ElfSection::open(const Reader::csptr &image, off_t off)
{
    image->readObj(off, &shdr);
    // Null sections get null readers.
    if (shdr.sh_type == SHT_NULL) {
        io = make_shared<NullReader>();
        return;
    }
    auto rawIo = make_shared<OffsetReader>(image, shdr.sh_offset, shdr.sh_size);
    if ((shdr.sh_flags & SHF_COMPRESSED) == 0) {
        io = rawIo;
    } else {
#ifdef WITH_ZLIB
        auto chdr = rawIo->readObj<Elf_Chdr>(0);
        io = make_shared<InflateReader>(chdr.ch_size, OffsetReader(rawIo,
                 sizeof chdr, shdr.sh_size - sizeof chdr));
#else
        static bool warned = false;
        if (!warned) {
            warned = true;
            std::clog <<"warning: no support configured for compressed debug info in "
               << *image << std::endl;
        }
        io = make_shared<NullReader>();
#endif
    }
}

ElfObject::sptr
ImageCache::getImageForName(const string &name) {
    bool found;
    auto res = getImageIfLoaded(name, found);
    if (found) {
        if (res != nullptr)
            return res;
        // Don't return null to keep it consistent with a previous failure to load.
        throw (Exception() << "previously failed to load " << name);
    }
    auto &item = cache[name];
    item = make_shared<ElfObject>(*this, loadFile(name));
    return item;
}

ImageCache::ImageCache() : elfHits(0), elfLookups(0) {}
ImageCache::~ImageCache() {
    if (verbose >= 2) {
        *debug << "ELF image cache: lookups: " << elfLookups << ", hits=" << elfHits << std::endl;
        for (auto &items : cache) {
            if (items.second)
                *debug << "\t" << *items.second->io << std::endl;
            else
                *debug << "\t" << "NEGATIVE: " << items.first << std::endl;
        }
    }
}

ElfObject::sptr
ImageCache::getImageIfLoaded(const string &name, bool &found)
{
    elfLookups++;
    auto it = cache.find(name);
    if (it != cache.end()) {
        elfHits++;
        found = true;
        return it->second;
    }
    found = false;
    return ElfObject::sptr();
}

ElfObject::sptr
ImageCache::getDebugImage(const string &name) {
    // XXX: verify checksum.
    for (const auto &dir : globalDebugDirectories.dirs) {
        bool found;
        auto img = getImageIfLoaded(stringify(dir, "/", name), found);
        if (found)
            return img;
    }
    for (const auto &dir : globalDebugDirectories.dirs) {
        try {
           return getImageForName(stringify(dir, "/", name));
        }
        catch (const std::exception &ex) {
            continue;
        }
    }
    return ElfObject::sptr();
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2010 Matthew Iselin, Heights College
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <network/IpAddress.h>

String IpAddress::toString()
{
    if(m_Type == IPv4)
    {
        NormalStaticString str;
        str.clear();
        str.append(m_Ipv4 & 0xff);
        str += ".";
        str.append((m_Ipv4 >> 8) & 0xff);
        str += ".";
        str.append((m_Ipv4 >> 16) & 0xff);
        str += ".";
        str.append((m_Ipv4 >> 24) & 0xff);
        return String(static_cast<const char*>(str));
    }
    else
    {
        NormalStaticString str;
        str.clear();
        bool bZeroComp = false;
        bool alreadyZeroComp = false; // Compression can only come once.
                                      // Naive algorithm, compresses first zeroes
                                      // but not the largest set.
        for(size_t i = 0; i < 16; i++)
        {
            if(i && ((i % 2) == 0))
            {
                if(!bZeroComp)
                    str += ":";

                if(alreadyZeroComp)
                    continue;

                // Zero-compression
                if(!m_Ipv6[i] && !m_Ipv6[i+1])
                {
                    i++;
                    bZeroComp = true;
                    continue;
                }
                else if(bZeroComp)
                {
                    str += ":";
                    bZeroComp = false;
                    alreadyZeroComp = true;
                }
            }
            str.append(m_Ipv6[i], 16, 2);
        }
        return String(static_cast<const char*>(str));
    }
    return String("");
}

bool IpAddress::isMulticast()
{
    if(m_Type == IPv4)
    {
        // Leading bits of the IP address = 1110? The old class D is all multicast now.
        if(((m_Ipv4 & 0xFF) & 0xE0) == 0xE0)
            return true;
    }
    else if(m_Type == IPv6)
    {
        // FF00::/8 is the multicast range for IPv6.
        if(m_Ipv6[0] == 0xFF)
            return true;
    }

    return false;
}
<commit_msg>Network: fixed IpAddress::toString to avoid padding with zeroes. ::1 used to become "0000::0001", now becomes "::1".<commit_after>/*
 * Copyright (c) 2010 Matthew Iselin, Heights College
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <network/IpAddress.h>

String IpAddress::toString()
{
    if(m_Type == IPv4)
    {
        NormalStaticString str;
        str.clear();
        str.append(m_Ipv4 & 0xff);
        str += ".";
        str.append((m_Ipv4 >> 8) & 0xff);
        str += ".";
        str.append((m_Ipv4 >> 16) & 0xff);
        str += ".";
        str.append((m_Ipv4 >> 24) & 0xff);
        return String(static_cast<const char*>(str));
    }
    else
    {
        NormalStaticString str;
        str.clear();
        bool bZeroComp = false;
        bool alreadyZeroComp = false; // Compression can only come once.
                                      // Naive algorithm, compresses first zeroes
                                      // but not the largest set.
        for(size_t i = 0; i < 16; i++)
        {
            if(i && ((i % 2) == 0))
            {
                if(!bZeroComp)
                    str += ":";

                if(alreadyZeroComp)
                {
                    if(m_Ipv6[i])
                        str.append(m_Ipv6[i], 16);
                    continue;
                }

                // Zero-compression
                if(!m_Ipv6[i] && !m_Ipv6[i+1])
                {
                    i++;
                    bZeroComp = true;
                    continue;
                }
                else if(bZeroComp)
                {
                    str += ":";
                    bZeroComp = false;
                    alreadyZeroComp = true;
                }
            }

            if(m_Ipv6[i])
                str.append(m_Ipv6[i], 16);
        }
        return String(static_cast<const char*>(str));
    }
    return String("");
}

bool IpAddress::isMulticast()
{
    if(m_Type == IPv4)
    {
        // Leading bits of the IP address = 1110? The old class D is all multicast now.
        if(((m_Ipv4 & 0xFF) & 0xE0) == 0xE0)
            return true;
    }
    else if(m_Type == IPv6)
    {
        // FF00::/8 is the multicast range for IPv6.
        if(m_Ipv6[0] == 0xFF)
            return true;
    }

    return false;
}
<|endoftext|>