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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to