class CoreEngine {
friend struct NodeBuilderContext;
friend class NodeBuilder;
+ friend class ExprEngine;
friend class CommonNodeBuilder;
friend class IndirectGotoNodeBuilder;
friend class SwitchNodeBuilder;
/// steps. Returns true if there is still simulation state on the worklist.
bool ExecuteWorkList(const LocationContext *L, unsigned Steps,
ProgramStateRef InitState);
- void ExecuteWorkListWithInitialState(const LocationContext *L,
+ /// Returns true if there is still simulation state on the worklist.
+ bool ExecuteWorkListWithInitialState(const LocationContext *L,
unsigned Steps,
ProgramStateRef InitState,
ExplodedNodeSet &Dst);
~ExprEngine();
- void ExecuteWorkList(const LocationContext *L, unsigned Steps = 150000) {
- Engine.ExecuteWorkList(L, Steps, 0);
+ /// Returns true if there is still simulation state on the worklist.
+ bool ExecuteWorkList(const LocationContext *L, unsigned Steps = 150000) {
+ return Engine.ExecuteWorkList(L, Steps, 0);
}
/// Execute the work list with an initial state. Nodes that reaches the exit
/// of the function are added into the Dst set, which represent the exit
- /// state of the function call.
- void ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps,
+ /// state of the function call. Returns true if there is still simulation
+ /// state on the worklist.
+ bool ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps,
ProgramStateRef InitState,
ExplodedNodeSet &Dst) {
- Engine.ExecuteWorkListWithInitialState(L, Steps, InitState, Dst);
+ return Engine.ExecuteWorkListWithInitialState(L, Steps, InitState, Dst);
}
/// getContext - Return the ASTContext associated with this analysis.
ExplodedNode *Pred, ExplodedNodeSet &Dst);
/// Called by CoreEngine when processing the entrance of a CFGBlock.
- virtual void processCFGBlockEntrance(NodeBuilderWithSinks &nodeBuilder);
+ virtual void processCFGBlockEntrance(const BlockEdge &L,
+ NodeBuilderWithSinks &nodeBuilder);
/// ProcessBranch - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a branch condition.
/// Called by CoreEngine when it starts processing a CFGBlock. The
/// SubEngine is expected to populate dstNodes with new nodes representing
/// updated analysis state, or generate no nodes at all if it doesn't.
- virtual void processCFGBlockEntrance(NodeBuilderWithSinks &nodeBuilder) = 0;
+ virtual void processCFGBlockEntrance(const BlockEdge &L,
+ NodeBuilderWithSinks &nodeBuilder) = 0;
/// Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a branch condition.
}
}
-void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L,
+bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L,
unsigned Steps,
ProgramStateRef InitState,
ExplodedNodeSet &Dst) {
- ExecuteWorkList(L, Steps, InitState);
+ bool DidNotFinish = ExecuteWorkList(L, Steps, InitState);
for (ExplodedGraph::eop_iterator I = G->eop_begin(),
E = G->eop_end(); I != E; ++I) {
Dst.Add(*I);
}
+ return DidNotFinish;
}
void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
ExplodedNodeSet dstNodes;
BlockEntrance BE(Blk, Pred->getLocationContext());
NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE);
- SubEng.processCFGBlockEntrance(nodeBuilder);
+ SubEng.processCFGBlockEntrance(L, nodeBuilder);
// Auto-generate a node.
if (!nodeBuilder.hasGeneratedNodes()) {
// Enqueue nodes onto the worklist.
enqueue(dstNodes);
-
- // Make sink nodes as exhausted.
- const SmallVectorImpl<ExplodedNode*> &Sinks = nodeBuilder.getSinks();
- for (SmallVectorImpl<ExplodedNode*>::const_iterator
- I =Sinks.begin(), E = Sinks.end(); I != E; ++I) {
- blocksExhausted.push_back(std::make_pair(L, *I));
- }
}
void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
STATISTIC(NumTimesRetriedWithoutInlining,
"The # of times we re-evaluated a call without inlining");
-STATISTIC(NumNotNew,
- "Cached out");
-STATISTIC(NumNull,
- "Null node");
-
//===----------------------------------------------------------------------===//
// Utility functions.
//===----------------------------------------------------------------------===//
break;
}
- if (!BeforeProcessingCall) {
- NumNull++;
+ if (!BeforeProcessingCall)
return false;
- }
// TODO: Clean up the unneeded nodes.
// Make the new node a successor of BeforeProcessingCall.
bool IsNew = false;
ExplodedNode *NewNode = G.getNode(NewNodeLoc, NewNodeState, false, &IsNew);
- if (!IsNew) {
- NumNotNew++;
- return false;
- }
+ // We cached out at this point. Caching out is common due to us backtracking
+ // from the inlined function, which might spawn several paths.
+ if (!IsNew)
+ return true;
+
NewNode->addPredecessor(BeforeProcessingCall, G);
// Add the new node to the work list.
}
/// Block entrance. (Update counters).
-void ExprEngine::processCFGBlockEntrance(NodeBuilderWithSinks &nodeBuilder) {
+void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
+ NodeBuilderWithSinks &nodeBuilder) {
// FIXME: Refactor this into a checker.
ExplodedNode *pred = nodeBuilder.getContext().getPred();
if (nodeBuilder.getContext().getCurrentBlockCount() >= AMgr.getMaxVisit()) {
static SimpleProgramPointTag tag("ExprEngine : Block count exceeded");
- nodeBuilder.generateNode(pred->getState(), pred, &tag, true);
+ const ExplodedNode *Sink =
+ nodeBuilder.generateNode(pred->getState(), pred, &tag, true);
// Check if we stopped at the top level function or not.
// Root node should have the location context of the top most function.
// no-inlining policy in the state and enqueuing the new work item on
// the list. Replay should almost never fail. Use the stats to catch it
// if it does.
- if (!(AMgr.RetryExhausted && replayWithoutInlining(pred, CalleeLC)))
- NumMaxBlockCountReachedInInlined++;
+ if ((AMgr.RetryExhausted && replayWithoutInlining(pred, CalleeLC)))
+ return;
+ NumMaxBlockCountReachedInInlined++;
} else
NumMaxBlockCountReached++;
+
+ // Make sink nodes as exhausted(for stats) only if retry failed.
+ Engine.blocksExhausted.push_back(std::make_pair(L, Sink));
}
}