mirror of https://github.com/microsoft/clang.git
[analyzer] Self-debug: Dump environment frame-by-frame.
It makes it easier to discriminate between values of similar expressions in different stack frames. It also makes the separate backtrace section in ExplodedGraph dumps redundant. Differential Revision: https://reviews.llvm.org/D42552 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@324660 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
880486c0ff
commit
bf0d414987
|
@ -265,7 +265,11 @@ public:
|
|||
|
||||
virtual void Profile(llvm::FoldingSetNodeID &ID) = 0;
|
||||
|
||||
void dumpStack(raw_ostream &OS, StringRef Indent = "") const;
|
||||
void dumpStack(
|
||||
raw_ostream &OS, StringRef Indent = "", const char *NL = "\n",
|
||||
const char *Sep = "",
|
||||
std::function<void(const LocationContext *)> printMoreInfoPerContext =
|
||||
[](const LocationContext *) {}) const;
|
||||
void dumpStack() const;
|
||||
|
||||
public:
|
||||
|
|
|
@ -92,12 +92,9 @@ public:
|
|||
bool operator==(const Environment& RHS) const {
|
||||
return ExprBindings == RHS.ExprBindings;
|
||||
}
|
||||
|
||||
void print(raw_ostream &Out, const char *NL, const char *Sep) const;
|
||||
|
||||
private:
|
||||
void printAux(raw_ostream &Out, bool printLocations,
|
||||
const char *NL, const char *Sep) const;
|
||||
|
||||
void print(raw_ostream &Out, const char *NL, const char *Sep,
|
||||
const LocationContext *WithLC = nullptr) const;
|
||||
};
|
||||
|
||||
class EnvironmentManager {
|
||||
|
|
|
@ -435,9 +435,10 @@ public:
|
|||
}
|
||||
|
||||
// Pretty-printing.
|
||||
void print(raw_ostream &Out, const char *nl = "\n",
|
||||
const char *sep = "") const;
|
||||
void printDOT(raw_ostream &Out) const;
|
||||
void print(raw_ostream &Out, const char *nl = "\n", const char *sep = "",
|
||||
const LocationContext *CurrentLC = nullptr) const;
|
||||
void printDOT(raw_ostream &Out,
|
||||
const LocationContext *CurrentLC = nullptr) const;
|
||||
void printTaint(raw_ostream &Out, const char *nl = "\n",
|
||||
const char *sep = "") const;
|
||||
|
||||
|
|
|
@ -463,28 +463,54 @@ bool LocationContext::isParentOf(const LocationContext *LC) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void LocationContext::dumpStack(raw_ostream &OS, StringRef Indent) const {
|
||||
static void printLocation(raw_ostream &OS, const SourceManager &SM,
|
||||
SourceLocation SLoc) {
|
||||
if (SLoc.isFileID() && SM.isInMainFile(SLoc))
|
||||
OS << "line " << SM.getExpansionLineNumber(SLoc);
|
||||
else
|
||||
SLoc.print(OS, SM);
|
||||
}
|
||||
|
||||
void LocationContext::dumpStack(
|
||||
raw_ostream &OS, StringRef Indent, const char *NL, const char *Sep,
|
||||
std::function<void(const LocationContext *)> printMoreInfoPerContext) const {
|
||||
ASTContext &Ctx = getAnalysisDeclContext()->getASTContext();
|
||||
PrintingPolicy PP(Ctx.getLangOpts());
|
||||
PP.TerseOutput = 1;
|
||||
|
||||
const SourceManager &SM =
|
||||
getAnalysisDeclContext()->getASTContext().getSourceManager();
|
||||
|
||||
unsigned Frame = 0;
|
||||
for (const LocationContext *LCtx = this; LCtx; LCtx = LCtx->getParent()) {
|
||||
|
||||
switch (LCtx->getKind()) {
|
||||
case StackFrame:
|
||||
OS << Indent << '#' << Frame++ << ' ';
|
||||
cast<StackFrameContext>(LCtx)->getDecl()->print(OS, PP);
|
||||
OS << '\n';
|
||||
OS << Indent << '#' << Frame << ' ';
|
||||
++Frame;
|
||||
if (const NamedDecl *D = dyn_cast<NamedDecl>(LCtx->getDecl()))
|
||||
OS << "Calling " << D->getQualifiedNameAsString();
|
||||
else
|
||||
OS << "Calling anonymous code";
|
||||
if (const Stmt *S = cast<StackFrameContext>(LCtx)->getCallSite()) {
|
||||
OS << " at ";
|
||||
printLocation(OS, SM, S->getLocStart());
|
||||
}
|
||||
break;
|
||||
case Scope:
|
||||
OS << Indent << " (scope)\n";
|
||||
OS << "Entering scope";
|
||||
break;
|
||||
case Block:
|
||||
OS << Indent << " (block context: "
|
||||
<< cast<BlockInvocationContext>(LCtx)->getContextData()
|
||||
<< ")\n";
|
||||
OS << "Invoking block";
|
||||
if (const Decl *D = cast<BlockInvocationContext>(LCtx)->getDecl()) {
|
||||
OS << " defined at ";
|
||||
printLocation(OS, SM, D->getLocStart());
|
||||
}
|
||||
break;
|
||||
}
|
||||
OS << NL;
|
||||
|
||||
printMoreInfoPerContext(LCtx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -186,28 +186,41 @@ EnvironmentManager::removeDeadBindings(Environment Env,
|
|||
}
|
||||
|
||||
void Environment::print(raw_ostream &Out, const char *NL,
|
||||
const char *Sep) const {
|
||||
bool isFirst = true;
|
||||
const char *Sep, const LocationContext *WithLC) const {
|
||||
if (ExprBindings.isEmpty())
|
||||
return;
|
||||
|
||||
for (Environment::iterator I = begin(), E = end(); I != E; ++I) {
|
||||
const EnvironmentEntry &En = I.getKey();
|
||||
|
||||
if (isFirst) {
|
||||
Out << NL << NL
|
||||
<< "Expressions:"
|
||||
<< NL;
|
||||
isFirst = false;
|
||||
} else {
|
||||
Out << NL;
|
||||
if (!WithLC) {
|
||||
// Find the freshest location context.
|
||||
llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts;
|
||||
for (auto I : *this) {
|
||||
const LocationContext *LC = I.first.getLocationContext();
|
||||
if (FoundContexts.count(LC) == 0) {
|
||||
// This context is fresher than all other contexts so far.
|
||||
WithLC = LC;
|
||||
for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent())
|
||||
FoundContexts.insert(LCI);
|
||||
}
|
||||
}
|
||||
|
||||
const Stmt *S = En.getStmt();
|
||||
assert(S != nullptr && "Expected non-null Stmt");
|
||||
|
||||
Out << " (" << (const void*) En.getLocationContext() << ','
|
||||
<< (const void*) S << ") ";
|
||||
LangOptions LO; // FIXME.
|
||||
S->printPretty(Out, nullptr, PrintingPolicy(LO));
|
||||
Out << " : " << I.getData();
|
||||
}
|
||||
|
||||
assert(WithLC);
|
||||
|
||||
LangOptions LO; // FIXME.
|
||||
PrintingPolicy PP(LO);
|
||||
|
||||
Out << NL << NL << "Expressions by stack frame:" << NL;
|
||||
WithLC->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) {
|
||||
for (auto I : ExprBindings) {
|
||||
if (I.first.getLocationContext() != LC)
|
||||
continue;
|
||||
|
||||
const Stmt *S = I.first.getStmt();
|
||||
assert(S != nullptr && "Expected non-null Stmt");
|
||||
|
||||
Out << "(" << (const void *)LC << ',' << (const void *)S << ") ";
|
||||
S->printPretty(Out, nullptr, PP);
|
||||
Out << " : " << I.second << NL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2768,12 +2768,6 @@ struct DOTGraphTraits<ExplodedNode*> :
|
|||
<< "\\l";
|
||||
}
|
||||
}
|
||||
static void printLocation2(raw_ostream &Out, SourceLocation SLoc) {
|
||||
if (SLoc.isFileID() && GraphPrintSourceManager->isInMainFile(SLoc))
|
||||
Out << "line " << GraphPrintSourceManager->getExpansionLineNumber(SLoc);
|
||||
else
|
||||
SLoc.print(Out, *GraphPrintSourceManager);
|
||||
}
|
||||
|
||||
static std::string getNodeLabel(const ExplodedNode *N, void*){
|
||||
|
||||
|
@ -2948,40 +2942,7 @@ struct DOTGraphTraits<ExplodedNode*> :
|
|||
Out << "\\|StateID: " << (const void*) state.get()
|
||||
<< " NodeID: " << (const void*) N << "\\|";
|
||||
|
||||
// Analysis stack backtrace.
|
||||
Out << "Location context stack (from current to outer):\\l";
|
||||
const LocationContext *LC = Loc.getLocationContext();
|
||||
unsigned Idx = 0;
|
||||
for (; LC; LC = LC->getParent(), ++Idx) {
|
||||
Out << Idx << ". (" << (const void *)LC << ") ";
|
||||
switch (LC->getKind()) {
|
||||
case LocationContext::StackFrame:
|
||||
if (const NamedDecl *D = dyn_cast<NamedDecl>(LC->getDecl()))
|
||||
Out << "Calling " << D->getQualifiedNameAsString();
|
||||
else
|
||||
Out << "Calling anonymous code";
|
||||
if (const Stmt *S = cast<StackFrameContext>(LC)->getCallSite()) {
|
||||
Out << " at ";
|
||||
printLocation2(Out, S->getLocStart());
|
||||
}
|
||||
break;
|
||||
case LocationContext::Block:
|
||||
Out << "Invoking block";
|
||||
if (const Decl *D = cast<BlockInvocationContext>(LC)->getBlockDecl()) {
|
||||
Out << " defined at ";
|
||||
printLocation2(Out, D->getLocStart());
|
||||
}
|
||||
break;
|
||||
case LocationContext::Scope:
|
||||
Out << "Entering scope";
|
||||
// FIXME: Add more info once ScopeContext is activated.
|
||||
break;
|
||||
}
|
||||
Out << "\\l";
|
||||
}
|
||||
Out << "\\l";
|
||||
|
||||
state->printDOT(Out);
|
||||
state->printDOT(Out, N->getLocationContext());
|
||||
|
||||
Out << "\\l";
|
||||
|
||||
|
|
|
@ -437,14 +437,14 @@ void ProgramState::setStore(const StoreRef &newStore) {
|
|||
// State pretty-printing.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void ProgramState::print(raw_ostream &Out,
|
||||
const char *NL, const char *Sep) const {
|
||||
void ProgramState::print(raw_ostream &Out, const char *NL, const char *Sep,
|
||||
const LocationContext *LC) const {
|
||||
// Print the store.
|
||||
ProgramStateManager &Mgr = getStateManager();
|
||||
Mgr.getStoreManager().print(getStore(), Out, NL, Sep);
|
||||
|
||||
// Print out the environment.
|
||||
Env.print(Out, NL, Sep);
|
||||
Env.print(Out, NL, Sep, LC);
|
||||
|
||||
// Print out the constraints.
|
||||
Mgr.getConstraintManager().print(this, Out, NL, Sep);
|
||||
|
@ -453,8 +453,8 @@ void ProgramState::print(raw_ostream &Out,
|
|||
Mgr.getOwningEngine()->printState(Out, this, NL, Sep);
|
||||
}
|
||||
|
||||
void ProgramState::printDOT(raw_ostream &Out) const {
|
||||
print(Out, "\\l", "\\|");
|
||||
void ProgramState::printDOT(raw_ostream &Out, const LocationContext *LC) const {
|
||||
print(Out, "\\l", "\\|", LC);
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD void ProgramState::dump() const {
|
||||
|
|
|
@ -18,6 +18,6 @@ void test() {
|
|||
// CHECK: 0. Program arguments: {{.*}}clang
|
||||
// CHECK-NEXT: 1. <eof> parser at end of file
|
||||
// CHECK-NEXT: 2. While analyzing stack:
|
||||
// CHECK-NEXT: #0 void inlined()
|
||||
// CHECK-NEXT: #1 void test()
|
||||
// CHECK-NEXT: #0 Calling inlined at line 15
|
||||
// CHECK-NEXT: #1 Calling test
|
||||
// CHECK-NEXT: 3. {{.*}}crash-trace.c:{{[0-9]+}}:3: Error evaluating statement
|
||||
|
|
|
@ -18,6 +18,8 @@ void foo(int x) {
|
|||
// CHECK: Store (direct and default bindings)
|
||||
// CHECK-NEXT: (y,0,direct) : 1 S32b
|
||||
|
||||
// CHECK: Expressions:
|
||||
// CHECK: Expressions by stack frame:
|
||||
// CHECK-NEXT: #0 Calling foo
|
||||
// CHECK-NEXT: clang_analyzer_printState : &code{clang_analyzer_printState}
|
||||
// CHECK-NEXT: {{(Ranges are empty.)|(Constraints:[[:space:]]*$)}}
|
||||
|
||||
// CHECK: {{(Ranges are empty.)|(Constraints:[[:space:]]*$)}}
|
||||
|
|
Loading…
Reference in New Issue