diff options
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r-- | lib/StaticAnalyzer/Core/CallEvent.cpp | 40 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 123 |
2 files changed, 124 insertions, 39 deletions
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 006ca1043a..2d96a1ea76 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -382,14 +382,14 @@ static const CXXMethodDecl *devirtualize(const CXXMethodDecl *MD, SVal ThisVal){ } -const Decl *CXXInstanceCall::getRuntimeDefinition() const { - const Decl *D = SimpleCall::getRuntimeDefinition(); +RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { + const Decl *D = SimpleCall::getRuntimeDefinition().Decl; if (!D) - return 0; + return RuntimeDefinition(); const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); if (!MD->isVirtual()) - return MD; + return RuntimeDefinition(MD, 0); // If the method is virtual, see if we can find the actual implementation // based on context-sensitivity. @@ -398,9 +398,9 @@ const Decl *CXXInstanceCall::getRuntimeDefinition() const { // because a /partially/ constructed object can be referred to through a // base pointer. We'll eventually want to use DynamicTypeInfo here. if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal())) - return Devirtualized; + return RuntimeDefinition(Devirtualized, 0); - return 0; + return RuntimeDefinition(); } void CXXInstanceCall::getInitialStackFrameContents( @@ -512,14 +512,14 @@ void CXXDestructorCall::getExtraInvalidatedRegions(RegionList &Regions) const { Regions.push_back(static_cast<const MemRegion *>(Data)); } -const Decl *CXXDestructorCall::getRuntimeDefinition() const { - const Decl *D = AnyFunctionCall::getRuntimeDefinition(); +RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const { + const Decl *D = AnyFunctionCall::getRuntimeDefinition().Decl; if (!D) - return 0; + return RuntimeDefinition(); const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); if (!MD->isVirtual()) - return MD; + return RuntimeDefinition(MD, 0); // If the method is virtual, see if we can find the actual implementation // based on context-sensitivity. @@ -528,9 +528,9 @@ const Decl *CXXDestructorCall::getRuntimeDefinition() const { // because a /partially/ constructed object can be referred to through a // base pointer. We'll eventually want to use DynamicTypeInfo here. if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal())) - return Devirtualized; + return RuntimeDefinition(Devirtualized, 0); - return 0; + return RuntimeDefinition(); } void CXXDestructorCall::getInitialStackFrameContents( @@ -659,7 +659,7 @@ ObjCMessageKind ObjCMethodCall::getMessageKind() const { return static_cast<ObjCMessageKind>(Info.getInt()); } -const Decl *ObjCMethodCall::getRuntimeDefinition() const { +RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { const ObjCMessageExpr *E = getOriginExpr(); assert(E); Selector Sel = E->getSelector(); @@ -669,12 +669,16 @@ const Decl *ObjCMethodCall::getRuntimeDefinition() const { // Find the the receiver type. const ObjCObjectPointerType *ReceiverT = 0; QualType SupersType = E->getSuperType(); + const MemRegion *Receiver = 0; + if (!SupersType.isNull()) { + // Super always means the type of immediate predecessor to the method + // where the call occurs. ReceiverT = cast<ObjCObjectPointerType>(SupersType); } else { - const MemRegion *Receiver = getReceiverSVal().getAsRegion(); + Receiver = getReceiverSVal().getAsRegion(); if (!Receiver) - return 0; + return RuntimeDefinition(); QualType DynType = getState()->getDynamicTypeInfo(Receiver).getType(); ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType); @@ -683,7 +687,7 @@ const Decl *ObjCMethodCall::getRuntimeDefinition() const { // Lookup the method implementation. if (ReceiverT) if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) - return IDecl->lookupPrivateMethod(Sel); + return RuntimeDefinition(IDecl->lookupPrivateMethod(Sel), Receiver); } else { // This is a class method. @@ -691,11 +695,11 @@ const Decl *ObjCMethodCall::getRuntimeDefinition() const { // class name. if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) { // Find/Return the method implementation. - return IDecl->lookupPrivateClassMethod(Sel); + return RuntimeDefinition(IDecl->lookupPrivateClassMethod(Sel), 0); } } - return 0; + return RuntimeDefinition(); } void ObjCMethodCall::getInitialStackFrameContents( diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index c1e010073c..9dd100ad77 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -11,12 +11,15 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "ExprEngine" + #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/AST/DeclCXX.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/Statistic.h" #include "llvm/Support/SaveAndRestore.h" #define CXX_INLINING_ENABLED 1 @@ -24,6 +27,9 @@ using namespace clang; using namespace ento; +STATISTIC(NumOfDynamicDispatchPathSplits, + "The # of times we split the path due to imprecise dynamic dispatch info"); + void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Get the entry block in the CFG of the callee. const StackFrameContext *calleeCtx = CE.getCalleeContext(); @@ -273,14 +279,27 @@ bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { return true; } -bool ExprEngine::inlineCall(const CallEvent &Call, - ExplodedNode *Pred) { - if (!getAnalysisManager().shouldInlineCall()) - return false; - - const Decl *D = Call.getRuntimeDefinition(); - if (!D) - return false; +/// The GDM component containing the dynamic dispatch bifurcation info. When +/// the exact type of the receiver is not known, we want to explore both paths - +/// one on which we do inline it and the other one on which we don't. This is +/// done to ensure we do not drop coverage. +/// This is the map from the receiver region to a bool, specifying either we +/// consider this region's information precise or not along the given path. +namespace clang { +namespace ento { +struct DynamicDispatchBifurcationMap {}; +typedef llvm::ImmutableMap<const MemRegion*, + int> DynamicDispatchBifur; +template<> struct ProgramStateTrait<DynamicDispatchBifurcationMap> + : public ProgramStatePartialTrait<DynamicDispatchBifur> { + static void *GDMIndex() { static int index; return &index; } +}; +}} + +bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, + NodeBuilder &Bldr, ExplodedNode *Pred, + ProgramStateRef State) { + assert(D); const LocationContext *CurLC = Pred->getLocationContext(); const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); @@ -359,7 +378,8 @@ bool ExprEngine::inlineCall(const CallEvent &Call, break; } case CE_ObjCMessage: - if (getAnalysisManager().IPAMode != DynamicDispatch) + if (!(getAnalysisManager().IPAMode == DynamicDispatch || + getAnalysisManager().IPAMode == DynamicDispatchBifurcate)) return false; break; } @@ -384,7 +404,7 @@ bool ExprEngine::inlineCall(const CallEvent &Call, // Construct a new state which contains the mapping from actual to // formal arguments. - ProgramStateRef State = Pred->getState()->enterStackFrame(Call, CalleeSFC); + State = State->enterStackFrame(Call, CalleeSFC); bool isNew; if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) { @@ -392,6 +412,11 @@ bool ExprEngine::inlineCall(const CallEvent &Call, if (isNew) Engine.getWorkList()->enqueue(N); } + + // If we decided to inline the call, the successor has been manually + // added onto the work list so remove it from the node builder. + Bldr.takeNodes(Pred); + return true; } @@ -491,6 +516,18 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, return State->BindExpr(E, LCtx, R); } +// Conservatively evaluate call by invalidating regions and binding +// a conjured return value. +void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, + ExplodedNode *Pred, ProgramStateRef State) { + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + State = Call.invalidateRegions(Count, State); + State = bindReturnValue(Call, Pred->getLocationContext(), State); + + // And make the result node. + Bldr.generateNode(Call.getProgramPoint(), State, Pred); +} + void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, const CallEvent &CallTemplate) { // Make sure we have the most recent state attached to the call. @@ -506,23 +543,67 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, if (InlinedFailedState) { // If we already tried once and failed, make sure we don't retry later. State = InlinedFailedState; - } else if (inlineCall(*Call, Pred)) { - // If we decided to inline the call, the successor has been manually - // added onto the work list and we should not perform our generic - // call-handling steps. - Bldr.takeNodes(Pred); - return; + } else if (getAnalysisManager().shouldInlineCall()) { + RuntimeDefinition RD = Call->getRuntimeDefinition(); + const Decl *D = RD.Decl; + if (D) { + // Explore with and without inlining the call. + const MemRegion *BifurReg = RD.Reg; + if (BifurReg && + getAnalysisManager().IPAMode == DynamicDispatchBifurcate) { + BifurcateCall(BifurReg, *Call, D, Bldr, Pred); + return; + } else { + // We are not bifurcating and we do have a Decl, so just inline. + if (inlineCall(*Call, D, Bldr, Pred, State)) + return; + } + } } // If we can't inline it, handle the return value and invalidate the regions. - unsigned Count = currentBuilderContext->getCurrentBlockCount(); - State = Call->invalidateRegions(Count, State); - State = bindReturnValue(*Call, Pred->getLocationContext(), State); + conservativeEvalCall(*Call, Bldr, Pred, State); +} - // And make the result node. - Bldr.generateNode(Call->getProgramPoint(), State, Pred); +void ExprEngine::BifurcateCall(const MemRegion *BifurReg, + const CallEvent &Call, const Decl *D, + NodeBuilder &Bldr, ExplodedNode *Pred) { + assert(BifurReg); + + // Check if we've performed the split already - note, we only want + // to split the path once per memory region. + ProgramStateRef State = Pred->getState(); + DynamicDispatchBifur BM = State->get<DynamicDispatchBifurcationMap>(); + for (DynamicDispatchBifur::iterator I = BM.begin(), + E = BM.end(); I != E; ++I) { + if (I->first == BifurReg) { + // If we are on "inline path", keep inlining if possible. + if (I->second == true) + if (inlineCall(Call, D, Bldr, Pred, State)) + return; + // If inline failed, or we are on the path where we assume we + // don't have enough info about the receiver to inline, conjure the + // return value and invalidate the regions. + conservativeEvalCall(Call, Bldr, Pred, State); + return; + } + } + + // If we got here, this is the first time we process a message to this + // region, so split the path. + ProgramStateRef IState = + State->set<DynamicDispatchBifurcationMap>(BifurReg, true); + inlineCall(Call, D, Bldr, Pred, IState); + + ProgramStateRef NoIState = + State->set<DynamicDispatchBifurcationMap>(BifurReg, false); + conservativeEvalCall(Call, Bldr, Pred, NoIState); + + NumOfDynamicDispatchPathSplits++; + return; } + void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { |