void StatsTracker::stepInstruction(ExecutionState &es) { if (OutputIStats) { if (TrackInstructionTime) { static sys::TimeValue lastNowTime(0, 0), lastUserTime(0, 0); if (lastUserTime.seconds() == 0 && lastUserTime.nanoseconds() == 0) { sys::TimeValue sys(0, 0); sys::Process::GetTimeUsage(lastNowTime, lastUserTime, sys); } else { sys::TimeValue now(0, 0), user(0, 0), sys(0, 0); sys::Process::GetTimeUsage(now, user, sys); sys::TimeValue delta = user - lastUserTime; sys::TimeValue deltaNow = now - lastNowTime; stats::instructionTime += delta.usec(); stats::instructionRealTime += deltaNow.usec(); lastUserTime = user; lastNowTime = now; } } Instruction *inst = es.pc()->inst; const InstructionInfo &ii = *es.pc()->info; StackFrame &sf = es.stack().back(); theStatisticManager->setIndex(ii.id); if (UseCallPaths) theStatisticManager->setContext(&sf.callPathNode->statistics); if (es.instsSinceCovNew) ++es.instsSinceCovNew; if (instructionIsCoverable(inst)) { if (!theStatisticManager->getIndexedValue( stats::locallyCoveredInstructions, ii.id)) { // Checking for actual stoppoints avoids inconsistencies due // to line number propogation. es.coveredLines[&ii.file].insert(ii.line); es.setCoveredNew(); es.instsSinceCovNew = 1; ++stats::locallyCoveredInstructions; stats::locallyUncoveredInstructions += (uint64_t) -1; if (!theStatisticManager->getIndexedValue(stats::globallyCoveredInstructions, ii.id)) { ++stats::globallyCoveredInstructions; stats::globallyUncoveredInstructions += (uint64_t) -1; } } } } }
ExecutionState &BumpMergingSearcher::selectState() { entry: // out of base states, pick one to pop if (baseSearcher->empty()) { std::map<llvm::Instruction*, ExecutionState*>::iterator it = statesAtMerge.begin(); ExecutionState *es = it->second; statesAtMerge.erase(it); ++es->pc(); baseSearcher->addState(es); } ExecutionState &es = baseSearcher->selectState(); if (Instruction *mp = getMergePoint(es)) { std::map<llvm::Instruction*, ExecutionState*>::iterator it = statesAtMerge.find(mp); baseSearcher->removeState(&es); if (it==statesAtMerge.end()) { statesAtMerge.insert(std::make_pair(mp, &es)); } else { ExecutionState *mergeWith = it->second; if (mergeWith->merge(es)) { // hack, because we are terminating the state we need to let // the baseSearcher know about it again baseSearcher->addState(&es); executor.terminateState(es, true); } else { it->second = &es; // the bump ++mergeWith->pc(); baseSearcher->addState(mergeWith); } } goto entry; } else { return es; } }
void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, ExecutionState &state) { // Note that in general phi nodes can reuse phi values from the same // block but the incoming value is the eval() result *before* the // execution of any phi nodes. this is pathological and doesn't // really seem to occur, but just in case we run the PhiCleanerPass // which makes sure this cannot happen and so it is safe to just // eval things in order. The PhiCleanerPass also makes sure that all // incoming blocks have the same order for each PHINode so we only // have to compute the index once. // // With that done we simply set an index in the state so that PHI // instructions know which argument to eval, set the pc, and continue. // XXX this lookup has to go ? KFunction *kf = state.stack().back().kf; unsigned entry = kf->basicBlockEntry[dst]; state.pc() = &kf->instructions[entry]; if (state.pc()->inst->getOpcode() == Instruction::PHI) { PHINode *first = static_cast<PHINode*>(state.pc()->inst); state.crtThread().incomingBBIndex = first->getBasicBlockIndex(src); } }
void StatsTracker::computeReachableUncovered() { KModule *km = executor.kmodule; Module *m = km->module; static bool init = true; const InstructionInfoTable &infos = *km->infos; StatisticManager &sm = *theStatisticManager; if (init) { init = false; // Compute call targets. It would be nice to use alias information // instead of assuming all indirect calls hit all escaping // functions, eh? for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); fnIt != fn_ie; ++fnIt) { for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); bbIt != bb_ie; ++bbIt) { for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); it != ie; ++it) { if (isa<CallInst>(it) || isa<InvokeInst>(it)) { if (isa<InlineAsm>(it->getOperand(0))) { // We can never call through here so assume no targets // (which should be correct anyhow). callTargets.insert(std::make_pair(it, std::vector<Function*>())); } else if (Function *target = getDirectCallTarget(it)) { callTargets[it].push_back(target); } else { callTargets[it] = std::vector<Function*>(km->escapingFunctions.begin(), km->escapingFunctions.end()); } } } } } // Compute function callers as reflexion of callTargets. for (calltargets_ty::iterator it = callTargets.begin(), ie = callTargets.end(); it != ie; ++it) for (std::vector<Function*>::iterator fit = it->second.begin(), fie = it->second.end(); fit != fie; ++fit) functionCallers[*fit].push_back(it->first); // Initialize minDistToReturn to shortest paths through // functions. 0 is unreachable. std::vector<Instruction *> instructions; for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); fnIt != fn_ie; ++fnIt) { if (fnIt->isDeclaration()) { if (fnIt->doesNotReturn()) { functionShortestPath[fnIt] = 0; } else { functionShortestPath[fnIt] = 1; // whatever } continue; } else { functionShortestPath[fnIt] = 0; } KFunction *kf = km->functionMap[fnIt]; for (unsigned i = 0; i < kf->numInstructions; ++i) { Instruction *inst = kf->instrPostOrder[i]->inst; instructions.push_back(inst); sm.setIndexedValue(stats::minDistToReturn, kf->instrPostOrder[i]->info->id, isa<ReturnInst>(inst)); } } // I'm so lazy it's not even worklisted. bool changed; do { changed = false; for (std::vector<Instruction*>::iterator it = instructions.begin(), ie = instructions.end(); it != ie; ++it) { Instruction *inst = *it; unsigned bestThrough = 0; if (isa<CallInst>(inst) || isa<InvokeInst>(inst)) { std::vector<Function*> &targets = callTargets[inst]; for (std::vector<Function*>::iterator fnIt = targets.begin(), ie = targets.end(); fnIt != ie; ++fnIt) { uint64_t dist = functionShortestPath[*fnIt]; if (dist) { dist = 1+dist; // count instruction itself if (bestThrough==0 || dist<bestThrough) bestThrough = dist; } } } else { bestThrough = 1; } if (bestThrough) { unsigned id = infos.getInfo(*it).id; uint64_t best, cur = best = sm.getIndexedValue(stats::minDistToReturn, id); std::vector<Instruction*> succs = getSuccs(*it); for (std::vector<Instruction*>::iterator it2 = succs.begin(), ie = succs.end(); it2 != ie; ++it2) { uint64_t dist = sm.getIndexedValue(stats::minDistToReturn, infos.getInfo(*it2).id); if (dist) { uint64_t val = bestThrough + dist; if (best==0 || val<best) best = val; } } if (best != cur) { sm.setIndexedValue(stats::minDistToReturn, id, best); changed = true; // Update shortest path if this is the entry point. Function *f = inst->getParent()->getParent(); if (inst==f->begin()->begin()) functionShortestPath[f] = best; } } } } while (changed); } // compute minDistToUncovered, 0 is unreachable std::vector<Instruction *> instructions; std::vector<unsigned> ids; for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); fnIt != fn_ie; ++fnIt) { if (fnIt->isDeclaration()) continue; KFunction *kf = km->functionMap[fnIt]; for (unsigned i = 0; i < kf->numInstructions; ++i) { Instruction *inst = kf->instrPostOrder[i]->inst; unsigned id = kf->instrPostOrder[i]->info->id; instructions.push_back(inst); ids.push_back(id); sm.setIndexedValue(stats::minDistToGloballyUncovered, id, sm.getIndexedValue(stats::globallyUncoveredInstructions, id)); } } // I'm so lazy it's not even worklisted. bool changed; do { changed = false; for (unsigned i = 0; i < instructions.size(); ++i) { Instruction *inst = instructions[i]; unsigned id = ids[i]; uint64_t best, cur = best = sm.getIndexedValue(stats::minDistToGloballyUncovered, id); unsigned bestThrough = 0; if (isa<CallInst>(inst) || isa<InvokeInst>(inst)) { std::vector<Function*> &targets = callTargets[inst]; for (std::vector<Function*>::iterator fnIt = targets.begin(), ie = targets.end(); fnIt != ie; ++fnIt) { uint64_t dist = functionShortestPath[*fnIt]; if (dist) { dist = 1+dist; // count instruction itself if (bestThrough==0 || dist<bestThrough) bestThrough = dist; } if (!(*fnIt)->isDeclaration()) { uint64_t calleeDist = sm.getIndexedValue(stats::minDistToGloballyUncovered, infos.getFunctionInfo(*fnIt).id); if (calleeDist) { calleeDist = 1+calleeDist; // count instruction itself if (best==0 || calleeDist<best) best = calleeDist; } } } } else { bestThrough = 1; } if (bestThrough) { std::vector<Instruction*> succs = getSuccs(inst); for (std::vector<Instruction*>::iterator it2 = succs.begin(), ie = succs.end(); it2 != ie; ++it2) { uint64_t dist = sm.getIndexedValue(stats::minDistToGloballyUncovered, infos.getInfo(*it2).id); if (dist) { uint64_t val = bestThrough + dist; if (best==0 || val<best) best = val; } } } if (best != cur) { sm.setIndexedValue(stats::minDistToGloballyUncovered, infos.getInfo(inst).id, best); changed = true; } } } while (changed); for (std::set<ExecutionState*>::iterator it = executor.states.begin(), ie = executor.states.end(); it != ie; ++it) { ExecutionState *es = *it; uint64_t currentFrameMinDist = 0; for (ExecutionState::stack_ty::iterator sfIt = es->stack().begin(), sf_ie = es->stack().end(); sfIt != sf_ie; ++sfIt) { ExecutionState::stack_ty::iterator next = sfIt + 1; KInstIterator kii; if (next==es->stack().end()) { kii = es->pc(); } else { kii = next->caller; ++kii; } sfIt->minDistToUncoveredOnReturn = currentFrameMinDist; currentFrameMinDist = computeMinDistToUncovered(kii, currentFrameMinDist); } } LOG(INFO) << "Processed " << instructions.size() << " instructions in static analysis"; }
void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f, std::vector< ref<Expr> > &arguments) { fireControlFlowEvent(&state, ::cloud9::worker::CALL); if (f && DebugCallHistory) { unsigned depth = state.stack().size(); LOG(INFO) << "Call[" << &state << "]: " << std::string(depth, ' ') << f->getName().str(); } Instruction *i = NULL; if (ki) i = ki->inst; if (ki && f && f->isDeclaration()) { switch(f->getIntrinsicID()) { case Intrinsic::not_intrinsic: // state may be destroyed by this call, cannot touch callExternalFunction(state, ki, f, arguments); break; // va_arg is handled by caller and intrinsic lowering, see comment for // ExecutionState::varargs case Intrinsic::vastart: { StackFrame &sf = state.stack().back(); assert(sf.varargs && "vastart called in function with no vararg object"); // FIXME: This is really specific to the architecture, not the pointer // size. This happens to work fir x86-32 and x86-64, however. Expr::Width WordSize = Context::get().getPointerWidth(); if (WordSize == Expr::Int32) { executeMemoryOperation(state, true, arguments[0], sf.varargs->getBaseExpr(), 0); } else { assert(WordSize == Expr::Int64 && "Unknown word size!"); // X86-64 has quite complicated calling convention. However, // instead of implementing it, we can do a simple hack: just // make a function believe that all varargs are on stack. executeMemoryOperation(state, true, arguments[0], ConstantExpr::create(48, 32), 0); // gp_offset executeMemoryOperation(state, true, AddExpr::create(arguments[0], ConstantExpr::create(4, 64)), ConstantExpr::create(304, 32), 0); // fp_offset executeMemoryOperation(state, true, AddExpr::create(arguments[0], ConstantExpr::create(8, 64)), sf.varargs->getBaseExpr(), 0); // overflow_arg_area executeMemoryOperation(state, true, AddExpr::create(arguments[0], ConstantExpr::create(16, 64)), ConstantExpr::create(0, 64), 0); // reg_save_area } break; } case Intrinsic::vaend: // va_end is a noop for the interpreter. // // FIXME: We should validate that the target didn't do something bad // with vaeend, however (like call it twice). break; case Intrinsic::vacopy: // va_copy should have been lowered. // // FIXME: It would be nice to check for errors in the usage of this as // well. default: LOG(FATAL) << "Unknown intrinsic: " << f->getName().data(); } if (InvokeInst *ii = dyn_cast<InvokeInst>(i)) transferToBasicBlock(ii->getNormalDest(), i->getParent(), state); } else { // FIXME: I'm not really happy about this reliance on prevPC but it is ok, I // guess. This just done to avoid having to pass KInstIterator everywhere // instead of the actual instruction, since we can't make a KInstIterator // from just an instruction (unlike LLVM). KFunction *kf = kmodule->functionMap[f]; state.pushFrame(state.prevPC(), kf); state.pc() = kf->instructions; if (statsTracker) statsTracker->framePushed(state, &state.stack()[state.stack().size()-2]); //XXX TODO fix this ugly stuff // TODO: support "byval" parameter attribute // TODO: support zeroext, signext, sret attributes unsigned callingArgs = arguments.size(); unsigned funcArgs = f->arg_size(); if (!f->isVarArg()) { if (callingArgs > funcArgs) { LOG(WARNING) << "Calling " << f->getName().data() << " with extra arguments."; } else if (callingArgs < funcArgs) { terminateStateOnError(state, "calling function with too few arguments", "user.err"); return; } } else { if (callingArgs < funcArgs) { terminateStateOnError(state, "calling function with too few arguments", "user.err"); return; } StackFrame &sf = state.stack().back(); unsigned size = 0; for (unsigned i = funcArgs; i < callingArgs; i++) { // FIXME: This is really specific to the architecture, not the pointer // size. This happens to work fir x86-32 and x86-64, however. Expr::Width WordSize = Context::get().getPointerWidth(); if (WordSize == Expr::Int32) { size += Expr::getMinBytesForWidth(arguments[i]->getWidth()); } else { size += llvm::RoundUpToAlignment(arguments[i]->getWidth(), WordSize) / 8; } } MemoryObject *mo = sf.varargs = memory->allocate(&state, size, true, false, state.prevPC()->inst); if (!mo) { terminateStateOnExecError(state, "out of memory (varargs)"); return; } ObjectState *os = bindObjectInState(state, mo, true); unsigned offset = 0; for (unsigned i = funcArgs; i < callingArgs; i++) { // FIXME: This is really specific to the architecture, not the pointer // size. This happens to work fir x86-32 and x86-64, however. Expr::Width WordSize = Context::get().getPointerWidth(); if (WordSize == Expr::Int32) { os->write(offset, arguments[i]); offset += Expr::getMinBytesForWidth(arguments[i]->getWidth()); } else { assert(WordSize == Expr::Int64 && "Unknown word size!"); os->write(offset, arguments[i]); offset += llvm::RoundUpToAlignment(arguments[i]->getWidth(), WordSize) / 8; } } } unsigned numFormals = f->arg_size(); for (unsigned i=0; i<numFormals; ++i) bindArgument(kf, i, state, arguments[i]); } }
void StatsTracker::computeReachableUncovered() { KModule *km = executor.kmodule; Module *m = km->module; static bool init = true; const InstructionInfoTable &infos = *km->infos; StatisticManager &sm = *theStatisticManager; if (init) { init = false; // Compute call targets. It would be nice to use alias information // instead of assuming all indirect calls hit all escaping // functions, eh? for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); fnIt != fn_ie; ++fnIt) { for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); bbIt != bb_ie; ++bbIt) { for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); it != ie; ++it) { if (isa<CallInst>(it) || isa<InvokeInst>(it)) { CallSite cs(it); if (isa<InlineAsm>(cs.getCalledValue())) { // We can never call through here so assume no targets // (which should be correct anyhow). callTargets.insert(std::make_pair(it, std::vector<Function*>())); } else if (Function *target = getDirectCallTarget(cs)) { callTargets[it].push_back(target); } else { callTargets[it] = std::vector<Function*>(km->escapingFunctions.begin(), km->escapingFunctions.end()); } } } } } // Compute function callers as reflexion of callTargets. for (calltargets_ty::iterator it = callTargets.begin(), ie = callTargets.end(); it != ie; ++it) for (std::vector<Function*>::iterator fit = it->second.begin(), fie = it->second.end(); fit != fie; ++fit) functionCallers[*fit].push_back(it->first); // Initialize minDistToReturn to shortest paths through // functions. 0 is unreachable. std::vector<Instruction *> instructions; for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); fnIt != fn_ie; ++fnIt) { if (fnIt->isDeclaration()) { if (fnIt->doesNotReturn()) { functionShortestPath[fnIt] = 0; } else { functionShortestPath[fnIt] = 1; // whatever } } else { functionShortestPath[fnIt] = 0; } // Not sure if I should bother to preorder here. XXX I should. for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); bbIt != bb_ie; ++bbIt) { for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); it != ie; ++it) { instructions.push_back(it); unsigned id = infos.getInfo(it).id; sm.setIndexedValue(stats::minDistToReturn, id, isa<ReturnInst>(it) #if LLVM_VERSION_CODE < LLVM_VERSION(3, 1) || isa<UnwindInst>(it) #endif ); } } } std::reverse(instructions.begin(), instructions.end()); // I'm so lazy it's not even worklisted. bool changed; do { changed = false; for (std::vector<Instruction*>::iterator it = instructions.begin(), ie = instructions.end(); it != ie; ++it) { Instruction *inst = *it; unsigned bestThrough = 0; if (isa<CallInst>(inst) || isa<InvokeInst>(inst)) { std::vector<Function*> &targets = callTargets[inst]; for (std::vector<Function*>::iterator fnIt = targets.begin(), ie = targets.end(); fnIt != ie; ++fnIt) { uint64_t dist = functionShortestPath[*fnIt]; if (dist) { dist = 1+dist; // count instruction itself if (bestThrough==0 || dist<bestThrough) bestThrough = dist; } } } else { bestThrough = 1; } if (bestThrough) { unsigned id = infos.getInfo(*it).id; uint64_t best, cur = best = sm.getIndexedValue(stats::minDistToReturn, id); std::vector<Instruction*> succs = getSuccs(*it); for (std::vector<Instruction*>::iterator it2 = succs.begin(), ie = succs.end(); it2 != ie; ++it2) { uint64_t dist = sm.getIndexedValue(stats::minDistToReturn, infos.getInfo(*it2).id); if (dist) { uint64_t val = bestThrough + dist; if (best==0 || val<best) best = val; } } // there's a corner case here when a function only includes a single // instruction (a ret). in that case, we MUST update // functionShortestPath, or it will remain 0 (erroneously indicating // that no return instructions are reachable) Function *f = inst->getParent()->getParent(); if (best != cur || (inst == f->begin()->begin() && functionShortestPath[f] != best)) { sm.setIndexedValue(stats::minDistToReturn, id, best); changed = true; // Update shortest path if this is the entry point. if (inst==f->begin()->begin()) functionShortestPath[f] = best; } } } } while (changed); } // compute minDistToUncovered, 0 is unreachable std::vector<Instruction *> instructions; for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); fnIt != fn_ie; ++fnIt) { // Not sure if I should bother to preorder here. for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); bbIt != bb_ie; ++bbIt) { for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); it != ie; ++it) { unsigned id = infos.getInfo(it).id; instructions.push_back(&*it); sm.setIndexedValue(stats::minDistToUncovered, id, sm.getIndexedValue(stats::uncoveredInstructions, id)); } } } std::reverse(instructions.begin(), instructions.end()); // I'm so lazy it's not even worklisted. bool changed; do { changed = false; for (std::vector<Instruction*>::iterator it = instructions.begin(), ie = instructions.end(); it != ie; ++it) { Instruction *inst = *it; uint64_t best, cur = best = sm.getIndexedValue(stats::minDistToUncovered, infos.getInfo(inst).id); unsigned bestThrough = 0; if (isa<CallInst>(inst) || isa<InvokeInst>(inst)) { std::vector<Function*> &targets = callTargets[inst]; for (std::vector<Function*>::iterator fnIt = targets.begin(), ie = targets.end(); fnIt != ie; ++fnIt) { uint64_t dist = functionShortestPath[*fnIt]; if (dist) { dist = 1+dist; // count instruction itself if (bestThrough==0 || dist<bestThrough) bestThrough = dist; } if (!(*fnIt)->isDeclaration()) { uint64_t calleeDist = sm.getIndexedValue(stats::minDistToUncovered, infos.getFunctionInfo(*fnIt).id); if (calleeDist) { calleeDist = 1+calleeDist; // count instruction itself if (best==0 || calleeDist<best) best = calleeDist; } } } } else { bestThrough = 1; } if (bestThrough) { std::vector<Instruction*> succs = getSuccs(inst); for (std::vector<Instruction*>::iterator it2 = succs.begin(), ie = succs.end(); it2 != ie; ++it2) { uint64_t dist = sm.getIndexedValue(stats::minDistToUncovered, infos.getInfo(*it2).id); if (dist) { uint64_t val = bestThrough + dist; if (best==0 || val<best) best = val; } } } if (best != cur) { sm.setIndexedValue(stats::minDistToUncovered, infos.getInfo(inst).id, best); changed = true; } } } while (changed); for (std::set<ExecutionState*>::iterator it = executor.states.begin(), ie = executor.states.end(); it != ie; ++it) { ExecutionState *es = *it; uint64_t currentFrameMinDist = 0; #if MULTITHREAD for (Thread::stack_ty::iterator sfIt = es->stack().begin(), sf_ie = es->stack().end(); sfIt != sf_ie; ++sfIt) { Thread::stack_ty::iterator next = sfIt + 1; KInstIterator kii; if (next==es->stack().end()) { kii = es->pc(); #else for (ExecutionState::stack_ty::iterator sfIt = es->stack.begin(), sf_ie = es->stack.end(); sfIt != sf_ie; ++sfIt) { ExecutionState::stack_ty::iterator next = sfIt + 1; KInstIterator kii; if (next==es->stack.end()) { kii = es->pc; #endif } else { kii = next->caller; ++kii; } sfIt->minDistToUncoveredOnReturn = currentFrameMinDist; currentFrameMinDist = computeMinDistToUncovered(kii, currentFrameMinDist); } } }
ExecutionState &MergingSearcher::selectState() { while (!baseSearcher->empty()) { ExecutionState &es = baseSearcher->selectState(); if (getMergePoint(es)) { baseSearcher->removeState(&es, &es); statesAtMerge.insert(&es); } else { return es; } } // build map of merge point -> state list std::map<Instruction*, std::vector<ExecutionState*> > merges; for (std::set<ExecutionState*>::const_iterator it = statesAtMerge.begin(), ie = statesAtMerge.end(); it != ie; ++it) { ExecutionState &state = **it; Instruction *mp = getMergePoint(state); merges[mp].push_back(&state); } if (DebugLogMerge) std::cerr << "-- all at merge --\n"; for (std::map<Instruction*, std::vector<ExecutionState*> >::iterator it = merges.begin(), ie = merges.end(); it != ie; ++it) { if (DebugLogMerge) { std::cerr << "\tmerge: " << it->first << " ["; for (std::vector<ExecutionState*>::iterator it2 = it->second.begin(), ie2 = it->second.end(); it2 != ie2; ++it2) { ExecutionState *state = *it2; std::cerr << state << ", "; } std::cerr << "]\n"; } // merge states std::set<ExecutionState*> toMerge(it->second.begin(), it->second.end()); while (!toMerge.empty()) { ExecutionState *base = *toMerge.begin(); toMerge.erase(toMerge.begin()); std::set<ExecutionState*> toErase; for (std::set<ExecutionState*>::iterator it = toMerge.begin(), ie = toMerge.end(); it != ie; ++it) { ExecutionState *mergeWith = *it; if (base->merge(*mergeWith)) { toErase.insert(mergeWith); } } if (DebugLogMerge && !toErase.empty()) { std::cerr << "\t\tmerged: " << base << " with ["; for (std::set<ExecutionState*>::iterator it = toErase.begin(), ie = toErase.end(); it != ie; ++it) { if (it!=toErase.begin()) std::cerr << ", "; std::cerr << *it; } std::cerr << "]\n"; } for (std::set<ExecutionState*>::iterator it = toErase.begin(), ie = toErase.end(); it != ie; ++it) { std::set<ExecutionState*>::iterator it2 = toMerge.find(*it); assert(it2!=toMerge.end()); executor.terminateState(**it, true); toMerge.erase(it2); } // step past merge and toss base back in pool statesAtMerge.erase(statesAtMerge.find(base)); ++base->pc(); baseSearcher->addState(base); } } if (DebugLogMerge) std::cerr << "-- merge complete, continuing --\n"; return selectState(); }