blob: f81d19d8b22d1d8d2e3e4d868d9502b7e7800681 [file] [log] [blame]
//===-- StatsTracker.cpp --------------------------------------------------===//
//
// The KLEE Symbolic Virtual Machine
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Common.h"
#include "StatsTracker.h"
#include "klee/ExecutionState.h"
#include "klee/Statistics.h"
#include "klee/Config/Version.h"
#include "klee/Internal/Module/InstructionInfoTable.h"
#include "klee/Internal/Module/KModule.h"
#include "klee/Internal/Module/KInstruction.h"
#include "klee/Internal/Support/ModuleUtil.h"
#include "klee/Internal/System/Time.h"
#include "CallPathManager.h"
#include "CoreStats.h"
#include "Executor.h"
#include "MemoryManager.h"
#include "UserSearcher.h"
#include "../Solver/SolverStats.h"
#include "llvm/BasicBlock.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/InlineAsm.h"
#include "llvm/Module.h"
#include "llvm/Type.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/CFG.h"
#if LLVM_VERSION_CODE < LLVM_VERSION(2, 9)
#include "llvm/System/Process.h"
#else
#include "llvm/Support/Process.h"
#endif
#if LLVM_VERSION_CODE < LLVM_VERSION(2, 9)
#include "llvm/System/Path.h"
#else
#include "llvm/Support/Path.h"
#endif
#include <iostream>
#include <fstream>
using namespace klee;
using namespace llvm;
///
namespace {
cl::opt<bool>
TrackInstructionTime("track-instruction-time",
cl::desc("Enable tracking of time for individual instructions"),
cl::init(false));
cl::opt<bool>
OutputStats("output-stats",
cl::desc("Write running stats trace file"),
cl::init(true));
cl::opt<bool>
OutputIStats("output-istats",
cl::desc("Write instruction level statistics (in callgrind format)"),
cl::init(true));
cl::opt<double>
StatsWriteInterval("stats-write-interval",
cl::desc("Approximate number of seconds between stats writes (default: 1.0)"),
cl::init(1.));
cl::opt<double>
IStatsWriteInterval("istats-write-interval",
cl::desc("Approximate number of seconds between istats writes (default: 10.0)"),
cl::init(10.));
/*
cl::opt<double>
BranchCovCountsWriteInterval("branch-cov-counts-write-interval",
cl::desc("Approximate number of seconds between run.branches writes (default: 5.0)"),
cl::init(5.));
*/
// XXX I really would like to have dynamic rate control for something like this.
cl::opt<double>
UncoveredUpdateInterval("uncovered-update-interval",
cl::init(30.));
cl::opt<bool>
UseCallPaths("use-call-paths",
cl::desc("Enable calltree tracking for instruction level statistics"),
cl::init(true));
}
///
bool StatsTracker::useStatistics() {
return OutputStats || OutputIStats;
}
namespace klee {
class WriteIStatsTimer : public Executor::Timer {
StatsTracker *statsTracker;
public:
WriteIStatsTimer(StatsTracker *_statsTracker) : statsTracker(_statsTracker) {}
~WriteIStatsTimer() {}
void run() { statsTracker->writeIStats(); }
};
class WriteStatsTimer : public Executor::Timer {
StatsTracker *statsTracker;
public:
WriteStatsTimer(StatsTracker *_statsTracker) : statsTracker(_statsTracker) {}
~WriteStatsTimer() {}
void run() { statsTracker->writeStatsLine(); }
};
class UpdateReachableTimer : public Executor::Timer {
StatsTracker *statsTracker;
public:
UpdateReachableTimer(StatsTracker *_statsTracker) : statsTracker(_statsTracker) {}
void run() { statsTracker->computeReachableUncovered(); }
};
}
//
/// Check for special cases where we statically know an instruction is
/// uncoverable. Currently the case is an unreachable instruction
/// following a noreturn call; the instruction is really only there to
/// satisfy LLVM's termination requirement.
static bool instructionIsCoverable(Instruction *i) {
if (i->getOpcode() == Instruction::Unreachable) {
BasicBlock *bb = i->getParent();
BasicBlock::iterator it(i);
if (it==bb->begin()) {
return true;
} else {
Instruction *prev = --it;
if (isa<CallInst>(prev) || isa<InvokeInst>(prev)) {
Function *target = getDirectCallTarget(prev);
if (target && target->doesNotReturn())
return false;
}
}
}
return true;
}
StatsTracker::StatsTracker(Executor &_executor, std::string _objectFilename,
bool _updateMinDistToUncovered)
: executor(_executor),
objectFilename(_objectFilename),
statsFile(0),
istatsFile(0),
startWallTime(util::getWallTime()),
numBranches(0),
fullBranches(0),
partialBranches(0),
updateMinDistToUncovered(_updateMinDistToUncovered) {
KModule *km = executor.kmodule;
sys::Path module(objectFilename);
if (!sys::Path(objectFilename).isAbsolute()) {
sys::Path current = sys::Path::GetCurrentDirectory();
current.appendComponent(objectFilename);
if (current.exists())
objectFilename = current.c_str();
}
if (OutputIStats)
theStatisticManager->useIndexedStats(km->infos->getMaxID());
for (std::vector<KFunction*>::iterator it = km->functions.begin(),
ie = km->functions.end(); it != ie; ++it) {
KFunction *kf = *it;
kf->trackCoverage = 1;
for (unsigned i=0; i<kf->numInstructions; ++i) {
KInstruction *ki = kf->instructions[i];
if (OutputIStats) {
unsigned id = ki->info->id;
theStatisticManager->setIndex(id);
if (kf->trackCoverage && instructionIsCoverable(ki->inst))
++stats::uncoveredInstructions;
}
if (kf->trackCoverage) {
if (BranchInst *bi = dyn_cast<BranchInst>(ki->inst))
if (!bi->isUnconditional())
numBranches++;
}
}
}
if (OutputStats) {
statsFile = executor.interpreterHandler->openOutputFile("run.stats");
assert(statsFile && "unable to open statistics trace file");
writeStatsHeader();
writeStatsLine();
executor.addTimer(new WriteStatsTimer(this), StatsWriteInterval);
if (updateMinDistToUncovered) {
computeReachableUncovered();
executor.addTimer(new UpdateReachableTimer(this), UncoveredUpdateInterval);
}
}
if (OutputIStats) {
istatsFile = executor.interpreterHandler->openOutputFile("run.istats");
assert(istatsFile && "unable to open istats file");
executor.addTimer(new WriteIStatsTimer(this), IStatsWriteInterval);
}
}
StatsTracker::~StatsTracker() {
if (statsFile)
delete statsFile;
if (istatsFile)
delete istatsFile;
}
void StatsTracker::done() {
if (statsFile)
writeStatsLine();
if (OutputIStats)
writeIStats();
}
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 (sf.kf->trackCoverage && instructionIsCoverable(inst)) {
if (!theStatisticManager->getIndexedValue(stats::coveredInstructions, ii.id)) {
// Checking for actual stoppoints avoids inconsistencies due
// to line number propogation.
//
// FIXME: This trick no longer works, we should fix this in the line
// number propogation.
#if LLVM_VERSION_CODE < LLVM_VERSION(2, 7)
if (isa<DbgStopPointInst>(inst))
#endif
es.coveredLines[&ii.file].insert(ii.line);
es.coveredNew = true;
es.instsSinceCovNew = 1;
++stats::coveredInstructions;
stats::uncoveredInstructions += (uint64_t)-1;
}
}
}
}
///
/* Should be called _after_ the es->pushFrame() */
void StatsTracker::framePushed(ExecutionState &es, StackFrame *parentFrame) {
if (OutputIStats) {
StackFrame &sf = es.stack.back();
if (UseCallPaths) {
CallPathNode *parent = parentFrame ? parentFrame->callPathNode : 0;
CallPathNode *cp = callPathManager.getCallPath(parent,
sf.caller ? sf.caller->inst : 0,
sf.kf->function);
sf.callPathNode = cp;
cp->count++;
}
if (updateMinDistToUncovered) {
uint64_t minDistAtRA = 0;
if (parentFrame)
minDistAtRA = parentFrame->minDistToUncoveredOnReturn;
sf.minDistToUncoveredOnReturn = sf.caller ?
computeMinDistToUncovered(sf.caller, minDistAtRA) : 0;
}
}
}
/* Should be called _after_ the es->popFrame() */
void StatsTracker::framePopped(ExecutionState &es) {
// XXX remove me?
}
void StatsTracker::markBranchVisited(ExecutionState *visitedTrue,
ExecutionState *visitedFalse) {
if (OutputIStats) {
unsigned id = theStatisticManager->getIndex();
uint64_t hasTrue = theStatisticManager->getIndexedValue(stats::trueBranches, id);
uint64_t hasFalse = theStatisticManager->getIndexedValue(stats::falseBranches, id);
if (visitedTrue && !hasTrue) {
visitedTrue->coveredNew = true;
visitedTrue->instsSinceCovNew = 1;
++stats::trueBranches;
if (hasFalse) { ++fullBranches; --partialBranches; }
else ++partialBranches;
hasTrue = 1;
}
if (visitedFalse && !hasFalse) {
visitedFalse->coveredNew = true;
visitedFalse->instsSinceCovNew = 1;
++stats::falseBranches;
if (hasTrue) { ++fullBranches; --partialBranches; }
else ++partialBranches;
}
}
}
void StatsTracker::writeStatsHeader() {
*statsFile << "('Instructions',"
<< "'FullBranches',"
<< "'PartialBranches',"
<< "'NumBranches',"
<< "'UserTime',"
<< "'NumStates',"
<< "'MallocUsage',"
<< "'NumQueries',"
<< "'NumQueryConstructs',"
<< "'NumObjects',"
<< "'WallTime',"
<< "'CoveredInstructions',"
<< "'UncoveredInstructions',"
<< "'QueryTime',"
<< "'SolverTime',"
<< "'CexCacheTime',"
<< "'ForkTime',"
<< "'ResolveTime',"
#ifdef DEBUG
<< "'ArrayHashTime',"
#endif
<< ")\n";
statsFile->flush();
}
double StatsTracker::elapsed() {
return util::getWallTime() - startWallTime;
}
void StatsTracker::writeStatsLine() {
*statsFile << "(" << stats::instructions
<< "," << fullBranches
<< "," << partialBranches
<< "," << numBranches
<< "," << util::getUserTime()
<< "," << executor.states.size()
<< "," << sys::Process::GetTotalMemoryUsage()
<< "," << stats::queries
<< "," << stats::queryConstructs
<< "," << 0 // was numObjects
<< "," << elapsed()
<< "," << stats::coveredInstructions
<< "," << stats::uncoveredInstructions
<< "," << stats::queryTime / 1000000.
<< "," << stats::solverTime / 1000000.
<< "," << stats::cexCacheTime / 1000000.
<< "," << stats::forkTime / 1000000.
<< "," << stats::resolveTime / 1000000.
#ifdef DEBUG
<< "," << stats::arrayHashTime / 1000000.
#endif
<< ")\n";
statsFile->flush();
}
void StatsTracker::updateStateStatistics(uint64_t addend) {
for (std::set<ExecutionState*>::iterator it = executor.states.begin(),
ie = executor.states.end(); it != ie; ++it) {
ExecutionState &state = **it;
const InstructionInfo &ii = *state.pc->info;
theStatisticManager->incrementIndexedValue(stats::states, ii.id, addend);
if (UseCallPaths)
state.stack.back().callPathNode->statistics.incrementValue(stats::states, addend);
}
}
void StatsTracker::writeIStats() {
Module *m = executor.kmodule->module;
uint64_t istatsMask = 0;
std::ostream &of = *istatsFile;
of.seekp(0, std::ios::end);
unsigned istatsSize = of.tellp();
of.seekp(0);
of << "version: 1\n";
of << "creator: klee\n";
of << "pid: " << sys::Process::GetCurrentUserId() << "\n";
of << "cmd: " << m->getModuleIdentifier() << "\n\n";
of << "\n";
StatisticManager &sm = *theStatisticManager;
unsigned nStats = sm.getNumStatistics();
// Max is 13, sadly
istatsMask |= 1<<sm.getStatisticID("Queries");
istatsMask |= 1<<sm.getStatisticID("QueriesValid");
istatsMask |= 1<<sm.getStatisticID("QueriesInvalid");
istatsMask |= 1<<sm.getStatisticID("QueryTime");
istatsMask |= 1<<sm.getStatisticID("ResolveTime");
istatsMask |= 1<<sm.getStatisticID("Instructions");
istatsMask |= 1<<sm.getStatisticID("InstructionTimes");
istatsMask |= 1<<sm.getStatisticID("InstructionRealTimes");
istatsMask |= 1<<sm.getStatisticID("Forks");
istatsMask |= 1<<sm.getStatisticID("CoveredInstructions");
istatsMask |= 1<<sm.getStatisticID("UncoveredInstructions");
istatsMask |= 1<<sm.getStatisticID("States");
istatsMask |= 1<<sm.getStatisticID("MinDistToUncovered");
of << "positions: instr line\n";
for (unsigned i=0; i<nStats; i++) {
if (istatsMask & (1<<i)) {
Statistic &s = sm.getStatistic(i);
of << "event: " << s.getShortName() << " : "
<< s.getName() << "\n";
}
}
of << "events: ";
for (unsigned i=0; i<nStats; i++) {
if (istatsMask & (1<<i))
of << sm.getStatistic(i).getShortName() << " ";
}
of << "\n";
// set state counts, decremented after we process so that we don't
// have to zero all records each time.
if (istatsMask & (1<<stats::states.getID()))
updateStateStatistics(1);
std::string sourceFile = "";
CallSiteSummaryTable callSiteStats;
if (UseCallPaths)
callPathManager.getSummaryStatistics(callSiteStats);
of << "ob=" << objectFilename << "\n";
for (Module::iterator fnIt = m->begin(), fn_ie = m->end();
fnIt != fn_ie; ++fnIt) {
if (!fnIt->isDeclaration()) {
of << "fn=" << fnIt->getName().str() << "\n";
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) {
Instruction *instr = &*it;
const InstructionInfo &ii = executor.kmodule->infos->getInfo(instr);
unsigned index = ii.id;
if (ii.file!=sourceFile) {
of << "fl=" << ii.file << "\n";
sourceFile = ii.file;
}
of << ii.assemblyLine << " ";
of << ii.line << " ";
for (unsigned i=0; i<nStats; i++)
if (istatsMask&(1<<i))
of << sm.getIndexedValue(sm.getStatistic(i), index) << " ";
of << "\n";
if (UseCallPaths &&
(isa<CallInst>(instr) || isa<InvokeInst>(instr))) {
CallSiteSummaryTable::iterator it = callSiteStats.find(instr);
if (it!=callSiteStats.end()) {
for (std::map<llvm::Function*, CallSiteInfo>::iterator
fit = it->second.begin(), fie = it->second.end();
fit != fie; ++fit) {
Function *f = fit->first;
CallSiteInfo &csi = fit->second;
const InstructionInfo &fii =
executor.kmodule->infos->getFunctionInfo(f);
if (fii.file!="" && fii.file!=sourceFile)
of << "cfl=" << fii.file << "\n";
of << "cfn=" << f->getName().str() << "\n";
of << "calls=" << csi.count << " ";
of << fii.assemblyLine << " ";
of << fii.line << "\n";
of << ii.assemblyLine << " ";
of << ii.line << " ";
for (unsigned i=0; i<nStats; i++) {
if (istatsMask&(1<<i)) {
Statistic &s = sm.getStatistic(i);
uint64_t value;
// Hack, ignore things that don't make sense on
// call paths.
if (&s == &stats::uncoveredInstructions) {
value = 0;
} else {
value = csi.statistics.getValue(s);
}
of << value << " ";
}
}
of << "\n";
}
}
}
}
}
}
}
if (istatsMask & (1<<stats::states.getID()))
updateStateStatistics((uint64_t)-1);
// Clear then end of the file if necessary (no truncate op?).
unsigned pos = of.tellp();
for (unsigned i=pos; i<istatsSize; ++i)
of << '\n';
of.flush();
}
///
typedef std::map<Instruction*, std::vector<Function*> > calltargets_ty;
static calltargets_ty callTargets;
static std::map<Function*, std::vector<Instruction*> > functionCallers;
static std::map<Function*, unsigned> functionShortestPath;
static std::vector<Instruction*> getSuccs(Instruction *i) {
BasicBlock *bb = i->getParent();
std::vector<Instruction*> res;
if (i==bb->getTerminator()) {
for (succ_iterator it = succ_begin(bb), ie = succ_end(bb); it != ie; ++it)
res.push_back(it->begin());
} else {
res.push_back(++BasicBlock::iterator(i));
}
return res;
}
uint64_t klee::computeMinDistToUncovered(const KInstruction *ki,
uint64_t minDistAtRA) {
StatisticManager &sm = *theStatisticManager;
if (minDistAtRA==0) { // unreachable on return, best is local
return sm.getIndexedValue(stats::minDistToUncovered,
ki->info->id);
} else {
uint64_t minDistLocal = sm.getIndexedValue(stats::minDistToUncovered,
ki->info->id);
uint64_t distToReturn = sm.getIndexedValue(stats::minDistToReturn,
ki->info->id);
if (distToReturn==0) { // return unreachable, best is local
return minDistLocal;
} else if (!minDistLocal) { // no local reachable
return distToReturn + minDistAtRA;
} else {
return std::min(minDistLocal, distToReturn + minDistAtRA);
}
}
}
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;
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);
}
}
}