george.karpenkov created this revision.
Herald added subscribers: a.sidorin, szepet, xazax.hun.
This patch is work in progress, but has already shown itself to be useful in a
few cases.
For large traces understanding the analyzer output can be painful, especially
when arrows indicating the flow start to loop around.
This patch provides an alternative output method: simply iterate over all
program points in a bug report, and dump them to the associated C statements
(with program-line-based granularity).
In the ideal case (which we currently don't aim to get) the resulting program
is a refinement of an input which goes through the indicated error path.
In the current case, it is a C-like visualization (as some statements are
unexpectedly dropped, and some context is missing) of what the error path is.
Unresolved issues:
- I haven't found a way to run a visitor for all incoming reports, seems that
each visitor has to attach a visitor manually. Maybe that functionality would
have to be added.
- This needs to be behind a flag, still haven't thought of a good name.
- Always dumping to stderr is bad, this should go into a file, which should
somehow get synced to the plist/html/stderr output.
- Stderr output might be confusing in presence of multiple reports, as the
current code will dump all counterexamples first, and then will actually start
showing the warnings.
- In the default driver mode, all counterexamples will be dumped twice
(presumably because visitor is invoked for HTML and for PLIST output)
https://reviews.llvm.org/D40809
Files:
include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
===================================================================
--- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -11,6 +11,8 @@
// enhance the diagnostics reported for a bug.
//
//===----------------------------------------------------------------------===//
+#include <cmath>
+#include <string.h>
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
@@ -24,6 +26,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -1878,3 +1881,150 @@
return std::move(Piece);
}
+
+/// Export the counterexample to C.
+class PrinterVisitor final : public BugReporterVisitorImpl<PrinterVisitor> {
+
+ /// File ID followed by a line number.
+ llvm::SetVector<std::pair<FileID, unsigned>> VisitedLines;
+ mutable bool DonePrinting = false;
+
+ /// Output stream to write into.
+ raw_ostream &Ostream;
+
+ /// Max number of chars used to display the filename.
+ static const unsigned MAX_FILENAME_MARGIN = 80;
+ static const unsigned MARGIN_SEP_LEN = 1;
+
+public:
+ PrinterVisitor(raw_ostream &Ostream) : Ostream(Ostream) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) override {
+ SourceManager &SM = BRC.getSourceManager();
+
+ if (DonePrinting) { // Printing was done, do nothing.
+ return nullptr;
+ } else if (!PrevN->getFirstPred()) { // Finished report, do printing.
+
+ // Went through the entire bug report, now print the lines in reverse
+ // order.
+ bool Status = doPrinting(SM);
+ if (!Status)
+ Ostream << "Error while printing\n";
+ Ostream << "/* ============================================== */\n";
+ DonePrinting = true;
+ return nullptr;
+ } else if (Optional<PostStmt> PS = N->getLocation().getAs<PostStmt>()) {
+
+ // Statement location with an associated SourceLocation object.
+ SourceLocation Loc = PS->getStmt()->getSourceRange().getBegin();
+
+ // FIXME: might be necessary to insert the spelling location as well.
+ insertLoc(SM, SM.getExpansionLoc(Loc));
+ }
+ return nullptr;
+ }
+
+private:
+ /// Print locations serialized to \c VisitedLines.
+ bool doPrinting(SourceManager &SM) {
+ unsigned Margin = calculateMargin(SM);
+ for (auto It = VisitedLines.rbegin(),
+ ItEnd = VisitedLines.rend();
+ It != ItEnd; ++It) {
+ FileID FID = It->first;
+ unsigned LineNo = It->second;
+ bool Status = printLine(SM, FID, LineNo, Margin);
+ if (!Status)
+ return false;
+ }
+ return true;
+ }
+
+ /// Print line \p LineNo from file \p FID, with left margin \p Margin.
+ /// \return \c true on success, \c false on failure.
+ bool printLine(SourceManager &SM, FileID FID,
+ unsigned LineNo, unsigned Margin) {
+ SourceLocation Location = SM.translateLineCol(FID, LineNo, 1);
+ unsigned Offset = SM.getFileOffset(Location);
+
+ // Print location first.
+ int Status = printLocation(SM, Location, Margin);
+ if (!Status)
+ return false;
+
+ bool Invalid = false;
+ const char *BufferStart = SM.getBufferData(FID, &Invalid).data();
+ if (Invalid)
+ return false;
+
+ unsigned FileEndOffset = SM.getFileOffset(SM.getLocForEndOfFile(FID));
+ for (unsigned i=Offset; BufferStart[i] != '\n' && i < FileEndOffset; ++i)
+ Ostream << BufferStart[i];
+
+ Ostream << "\n";
+ return true;
+ }
+
+ /// Print location in left margin.
+ bool printLocation(SourceManager &SM, SourceLocation Loc, unsigned Margin) {
+ Loc = SM.getExpansionLoc(Loc);
+ PresumedLoc PLoc = SM.getPresumedLoc(Loc);
+ if (PLoc.isInvalid())
+ return false;
+
+ uint64_t SavedPos = Ostream.tell();
+ Ostream << PLoc.getFilename() << ":" << PLoc.getLine();
+ uint64_t Delta = Ostream.tell() - SavedPos;
+ assert(Margin >= Delta);
+ for (unsigned i=Delta; i<Margin; i++)
+ Ostream << " ";
+ Ostream << "|";
+
+ return true;
+ }
+
+ /// Serialize source location \p Loc to \c visitedLines.
+ void insertLoc(SourceManager &SM, SourceLocation Loc) {
+ VisitedLines.insert(
+ std::make_pair(SM.getFileID(Loc), SM.getSpellingLineNumber(Loc)));
+ }
+
+ /// \return smallest margin required to fit all filenames present
+ /// in the counterexample.
+ unsigned calculateMargin(SourceManager &SM) {
+ unsigned Out = 0;
+ for (auto Pair : VisitedLines) {
+ FileID FID = Pair.first;
+ unsigned LineNo = Pair.second;
+ SourceLocation Loc = SM.translateLineCol(FID, LineNo, 1);
+ Loc = SM.getExpansionLoc(Loc);
+ PresumedLoc PLoc = SM.getPresumedLoc(Loc);
+ if (PLoc.isInvalid())
+ continue;
+
+ const char *Filename = PLoc.getFilename();
+
+ // Take logarithm to figure out number of chars required.
+ long LineMargin = 1 + std::lround(floor(log10(PLoc.getLine())));
+ assert(LineMargin > 0);
+ unsigned RequiredMargin = strnlen(Filename, MAX_FILENAME_MARGIN)
+ + LineMargin + MARGIN_SEP_LEN;
+ if (RequiredMargin > Out)
+ Out = RequiredMargin;
+ }
+ return Out;
+ }
+};
+
+void bugreporter::printCounterexample(BugReport &report) {
+ report.addVisitor(llvm::make_unique<PrinterVisitor>(llvm::errs()));
+}
Index: lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
+++ lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -95,6 +95,7 @@
if (ex) {
R->addRange(ex->getSourceRange());
bugreporter::trackNullOrUndefValue(N, ex, *R);
+ bugreporter::printCounterexample(*R);
}
C.emitReport(std::move(R));
}
Index: include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
===================================================================
--- include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
+++ include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
@@ -365,6 +365,9 @@
bool IsArg = false,
bool EnableNullFPSuppression = true);
+/// Print the counterexample as a C file.
+void printCounterexample(BugReport &report);
+
const Expr *getDerefExpr(const Stmt *S);
const Stmt *GetDenomExpr(const ExplodedNode *N);
const Stmt *GetRetValExpr(const ExplodedNode *N);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits