forked from OSchip/llvm-project
[analyzer] Introduce a new callback for checkers, printState, to be used for debug-printing the contents of a ProgramState.
Unlike the other callbacks, this one is a simple virtual method, since it is only to be used for debugging. This new callback replaces the old ProgramState::Printer interface, and allows us to move the printing of refcount bindings from CFRefCount to RetainReleaseChecker. llvm-svn: 138728
This commit is contained in:
parent
b1e3e0e59e
commit
58a20d31b7
|
@ -338,6 +338,10 @@ public:
|
|||
class CheckerBase : public ProgramPointTag {
|
||||
public:
|
||||
StringRef getTagDescription() const;
|
||||
|
||||
/// See CheckerManager::runCheckersForPrintState.
|
||||
virtual void printState(raw_ostream &Out, const ProgramState *State,
|
||||
const char *NL, const char *Sep) const { }
|
||||
};
|
||||
|
||||
template <typename CHECK1, typename CHECK2=check::_VoidCheck,
|
||||
|
|
|
@ -267,6 +267,17 @@ public:
|
|||
AnalysisManager &mgr,
|
||||
BugReporter &BR);
|
||||
|
||||
/// \brief Run checkers for debug-printing a ProgramState.
|
||||
///
|
||||
/// Unlike most other callbacks, any checker can simply implement the virtual
|
||||
/// method CheckerBase::printState if it has custom data to print.
|
||||
/// \param Out The output stream
|
||||
/// \param State The state being printed
|
||||
/// \param NL The preferred representation of a newline.
|
||||
/// \param Sep The preferred separator between different kinds of data.
|
||||
void runCheckersForPrintState(raw_ostream &Out, const ProgramState *State,
|
||||
const char *NL, const char *Sep);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Internal registration functions for AST traversing.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -195,6 +195,10 @@ public:
|
|||
ArrayRef<const MemRegion *> ExplicitRegions,
|
||||
ArrayRef<const MemRegion *> Regions);
|
||||
|
||||
/// printState - Called by ProgramStateManager to print checker-specific data.
|
||||
void printState(raw_ostream &Out, const ProgramState *State,
|
||||
const char *NL, const char *Sep);
|
||||
|
||||
virtual ProgramStateManager& getStateManager() { return StateMgr; }
|
||||
|
||||
StoreManager& getStoreManager() { return StateMgr.getStoreManager(); }
|
||||
|
|
|
@ -332,14 +332,6 @@ public:
|
|||
return ProgramStateTrait<T>::Contains(ProgramStateTrait<T>::MakeData(d), key);
|
||||
}
|
||||
|
||||
// State pretty-printing.
|
||||
class Printer {
|
||||
public:
|
||||
virtual ~Printer() {}
|
||||
virtual void Print(raw_ostream &Out, const ProgramState *state,
|
||||
const char* nl, const char* sep) = 0;
|
||||
};
|
||||
|
||||
// Pretty-printing.
|
||||
void print(raw_ostream &Out, CFG &C, const char *nl = "\n",
|
||||
const char *sep = "") const;
|
||||
|
@ -404,7 +396,6 @@ public:
|
|||
|
||||
class ProgramStateManager {
|
||||
friend class ProgramState;
|
||||
friend class ExprEngine; // FIXME: Remove.
|
||||
private:
|
||||
/// Eng - The SubEngine that owns this state manager.
|
||||
SubEngine *Eng; /* Can be null. */
|
||||
|
@ -418,10 +409,6 @@ private:
|
|||
typedef llvm::DenseMap<void*,std::pair<void*,void (*)(void*)> > GDMContextsTy;
|
||||
GDMContextsTy GDMContexts;
|
||||
|
||||
/// Printers - A set of printer objects used for pretty-printing a ProgramState.
|
||||
/// ProgramStateManager owns these objects.
|
||||
std::vector<ProgramState::Printer*> Printers;
|
||||
|
||||
/// StateSet - FoldingSet containing all the states created for analyzing
|
||||
/// a particular function. This is used to unique states.
|
||||
llvm::FoldingSet<ProgramState> StateSet;
|
||||
|
|
|
@ -109,6 +109,10 @@ public:
|
|||
return processRegionChanges(state, 0, MR, MR);
|
||||
}
|
||||
|
||||
/// printState - Called by ProgramStateManager to print checker-specific data.
|
||||
virtual void printState(raw_ostream &Out, const ProgramState *State,
|
||||
const char *NL, const char *Sep) = 0;
|
||||
|
||||
/// Called by CoreEngine when the analysis worklist is either empty or the
|
||||
// maximum number of analysis steps have been reached.
|
||||
virtual void processEndWorklist(bool hasWorkRemaining) = 0;
|
||||
|
|
|
@ -36,7 +36,6 @@ public:
|
|||
TransferFuncs() {}
|
||||
virtual ~TransferFuncs() {}
|
||||
|
||||
virtual void RegisterPrinters(std::vector<ProgramState::Printer*>& Printers) {}
|
||||
virtual void RegisterChecks(ExprEngine& Eng) {}
|
||||
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ public:
|
|||
|
||||
void RefVal::print(raw_ostream &Out) const {
|
||||
if (!T.isNull())
|
||||
Out << "Tracked Type:" << T.getAsString() << '\n';
|
||||
Out << "Tracked " << T.getAsString() << '/';
|
||||
|
||||
switch (getKind()) {
|
||||
default: assert(false);
|
||||
|
@ -1585,14 +1585,6 @@ namespace {
|
|||
|
||||
class CFRefCount : public TransferFuncs {
|
||||
public:
|
||||
class BindingsPrinter : public ProgramState::Printer {
|
||||
public:
|
||||
virtual void Print(raw_ostream &Out,
|
||||
const ProgramState *state,
|
||||
const char* nl,
|
||||
const char* sep);
|
||||
};
|
||||
|
||||
const LangOptions& LOpts;
|
||||
const bool GCEnabled;
|
||||
|
||||
|
@ -1601,60 +1593,12 @@ public:
|
|||
: LOpts(lopts), GCEnabled(gcenabled) {}
|
||||
|
||||
void RegisterChecks(ExprEngine &Eng);
|
||||
|
||||
virtual void RegisterPrinters(std::vector<ProgramState::Printer*>& Printers) {
|
||||
Printers.push_back(new BindingsPrinter());
|
||||
}
|
||||
|
||||
const LangOptions& getLangOptions() const { return LOpts; }
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
static void PrintPool(raw_ostream &Out,
|
||||
SymbolRef Sym,
|
||||
const ProgramState *state) {
|
||||
Out << ' ';
|
||||
if (Sym)
|
||||
Out << Sym->getSymbolID();
|
||||
else
|
||||
Out << "<pool>";
|
||||
Out << ":{";
|
||||
|
||||
// Get the contents of the pool.
|
||||
if (const ARCounts *cnts = state->get<AutoreleasePoolContents>(Sym))
|
||||
for (ARCounts::iterator J=cnts->begin(), EJ=cnts->end(); J != EJ; ++J)
|
||||
Out << '(' << J.getKey() << ',' << J.getData() << ')';
|
||||
|
||||
Out << '}';
|
||||
}
|
||||
|
||||
void CFRefCount::BindingsPrinter::Print(raw_ostream &Out,
|
||||
const ProgramState *state,
|
||||
const char* nl, const char* sep) {
|
||||
|
||||
RefBindings B = state->get<RefBindings>();
|
||||
|
||||
if (!B.isEmpty())
|
||||
Out << sep << nl;
|
||||
|
||||
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
|
||||
Out << (*I).first << " : ";
|
||||
(*I).second.print(Out);
|
||||
Out << nl;
|
||||
}
|
||||
|
||||
// Print the autorelease stack.
|
||||
Out << sep << nl << "AR pool stack:";
|
||||
ARStack stack = state->get<AutoreleaseStack>();
|
||||
|
||||
PrintPool(Out, SymbolRef(), state); // Print the caller's pool.
|
||||
for (ARStack::iterator I=stack.begin(), E=stack.end(); I!=E; ++I)
|
||||
PrintPool(Out, *I, state);
|
||||
|
||||
Out << nl;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Error reporting.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -2543,6 +2487,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void printState(raw_ostream &Out, const ProgramState *State,
|
||||
const char *NL, const char *Sep) const;
|
||||
|
||||
void checkBind(SVal loc, SVal val, CheckerContext &C) const;
|
||||
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
|
||||
void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
|
||||
|
@ -3622,6 +3569,62 @@ void RetainReleaseChecker::checkDeadSymbols(SymbolReaper &SymReaper,
|
|||
C.generateNode(state, Pred);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Debug printing of refcount bindings and autorelease pools.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static void PrintPool(raw_ostream &Out, SymbolRef Sym,
|
||||
const ProgramState *State) {
|
||||
Out << ' ';
|
||||
if (Sym)
|
||||
Out << Sym->getSymbolID();
|
||||
else
|
||||
Out << "<pool>";
|
||||
Out << ":{";
|
||||
|
||||
// Get the contents of the pool.
|
||||
if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym))
|
||||
for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I)
|
||||
Out << '(' << I.getKey() << ',' << I.getData() << ')';
|
||||
|
||||
Out << '}';
|
||||
}
|
||||
|
||||
bool UsesAutorelease(const ProgramState *state) {
|
||||
// A state uses autorelease if it allocated an autorelease pool or if it has
|
||||
// objects in the caller's autorelease pool.
|
||||
return !state->get<AutoreleaseStack>().isEmpty() ||
|
||||
state->get<AutoreleasePoolContents>(SymbolRef());
|
||||
}
|
||||
|
||||
void RetainReleaseChecker::printState(raw_ostream &Out,
|
||||
const ProgramState *State,
|
||||
const char *NL, const char *Sep) const {
|
||||
|
||||
RefBindings B = State->get<RefBindings>();
|
||||
|
||||
if (!B.isEmpty())
|
||||
Out << Sep << NL;
|
||||
|
||||
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
|
||||
Out << I->first << " : ";
|
||||
I->second.print(Out);
|
||||
Out << NL;
|
||||
}
|
||||
|
||||
// Print the autorelease stack.
|
||||
if (UsesAutorelease(State)) {
|
||||
Out << Sep << NL << "AR pool stack:";
|
||||
ARStack Stack = State->get<AutoreleaseStack>();
|
||||
|
||||
PrintPool(Out, SymbolRef(), State); // Print the caller's pool.
|
||||
for (ARStack::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I)
|
||||
PrintPool(Out, *I, State);
|
||||
|
||||
Out << NL;
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Transfer function creation for external clients.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -426,6 +426,14 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit(
|
|||
EndOfTranslationUnitCheckers[i](TU, mgr, BR);
|
||||
}
|
||||
|
||||
void CheckerManager::runCheckersForPrintState(raw_ostream &Out,
|
||||
const ProgramState *State,
|
||||
const char *NL, const char *Sep) {
|
||||
for (llvm::DenseMap<CheckerTag, CheckerRef>::iterator
|
||||
I = CheckerTags.begin(), E = CheckerTags.end(); I != E; ++I)
|
||||
I->second->printState(Out, State, NL, Sep);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Internal registration functions for AST traversing.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -67,7 +67,6 @@ ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf)
|
|||
|
||||
// FIXME: Eventually remove the TF object entirely.
|
||||
TF->RegisterChecks(*this);
|
||||
TF->RegisterPrinters(getStateManager().Printers);
|
||||
|
||||
if (mgr.shouldEagerlyTrimExplodedGraph()) {
|
||||
// Enable eager node reclaimation when constructing the ExplodedGraph.
|
||||
|
@ -189,6 +188,11 @@ ExprEngine::processRegionChanges(const ProgramState *state,
|
|||
Explicits, Regions);
|
||||
}
|
||||
|
||||
void ExprEngine::printState(raw_ostream &Out, const ProgramState *State,
|
||||
const char *NL, const char *Sep) {
|
||||
getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep);
|
||||
}
|
||||
|
||||
void ExprEngine::processEndWorklist(bool hasWorkRemaining) {
|
||||
getCheckerManager().runCheckersForEndAnalysis(G, BR, *this);
|
||||
}
|
||||
|
|
|
@ -51,10 +51,6 @@ ProgramState::~ProgramState() {
|
|||
}
|
||||
|
||||
ProgramStateManager::~ProgramStateManager() {
|
||||
for (std::vector<ProgramState::Printer*>::iterator I=Printers.begin(),
|
||||
E=Printers.end(); I!=E; ++I)
|
||||
delete *I;
|
||||
|
||||
for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end();
|
||||
I!=E; ++I)
|
||||
I->second.second(I->second.first);
|
||||
|
@ -389,11 +385,11 @@ static bool IsEnvLoc(const Stmt *S) {
|
|||
return (bool) (((uintptr_t) S) & 0x1);
|
||||
}
|
||||
|
||||
void ProgramState::print(raw_ostream &Out, CFG &C, const char* nl,
|
||||
const char* sep) const {
|
||||
void ProgramState::print(raw_ostream &Out, CFG &C,
|
||||
const char *NL, const char *Sep) const {
|
||||
// Print the store.
|
||||
ProgramStateManager &Mgr = getStateManager();
|
||||
Mgr.getStoreManager().print(getStore(), Out, nl, sep);
|
||||
Mgr.getStoreManager().print(getStore(), Out, NL, Sep);
|
||||
|
||||
// Print Subexpression bindings.
|
||||
bool isFirst = true;
|
||||
|
@ -404,10 +400,11 @@ void ProgramState::print(raw_ostream &Out, CFG &C, const char* nl,
|
|||
continue;
|
||||
|
||||
if (isFirst) {
|
||||
Out << nl << nl << "Sub-Expressions:" << nl;
|
||||
Out << NL << NL << "Sub-Expressions:" << NL;
|
||||
isFirst = false;
|
||||
} else {
|
||||
Out << NL;
|
||||
}
|
||||
else { Out << nl; }
|
||||
|
||||
Out << " (" << (void*) I.getKey() << ") ";
|
||||
LangOptions LO; // FIXME.
|
||||
|
@ -423,10 +420,11 @@ void ProgramState::print(raw_ostream &Out, CFG &C, const char* nl,
|
|||
continue;
|
||||
|
||||
if (isFirst) {
|
||||
Out << nl << nl << "Block-level Expressions:" << nl;
|
||||
Out << NL << NL << "Block-level Expressions:" << NL;
|
||||
isFirst = false;
|
||||
} else {
|
||||
Out << NL;
|
||||
}
|
||||
else { Out << nl; }
|
||||
|
||||
Out << " (" << (void*) I.getKey() << ") ";
|
||||
LangOptions LO; // FIXME.
|
||||
|
@ -442,10 +440,11 @@ void ProgramState::print(raw_ostream &Out, CFG &C, const char* nl,
|
|||
continue;
|
||||
|
||||
if (isFirst) {
|
||||
Out << nl << nl << "Load/store locations:" << nl;
|
||||
Out << NL << NL << "Load/store locations:" << NL;
|
||||
isFirst = false;
|
||||
} else {
|
||||
Out << NL;
|
||||
}
|
||||
else { Out << nl; }
|
||||
|
||||
const Stmt *S = (Stmt*) (((uintptr_t) I.getKey()) & ((uintptr_t) ~0x1));
|
||||
|
||||
|
@ -455,13 +454,10 @@ void ProgramState::print(raw_ostream &Out, CFG &C, const char* nl,
|
|||
Out << " : " << I.getData();
|
||||
}
|
||||
|
||||
Mgr.getConstraintManager().print(this, Out, nl, sep);
|
||||
Mgr.getConstraintManager().print(this, Out, NL, Sep);
|
||||
|
||||
// Print checker-specific data.
|
||||
for (std::vector<Printer*>::iterator I = Mgr.Printers.begin(),
|
||||
E = Mgr.Printers.end(); I != E; ++I) {
|
||||
(*I)->Print(Out, this, nl, sep);
|
||||
}
|
||||
Mgr.getOwningEngine()->printState(Out, this, NL, Sep);
|
||||
}
|
||||
|
||||
void ProgramState::printDOT(raw_ostream &Out, CFG &C) const {
|
||||
|
|
Loading…
Reference in New Issue