[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-18 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov abandoned this revision.
george.karpenkov added a comment.

Abandoned in favor of https://reviews.llvm.org/D41378


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-15 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov added a comment.

@alexshap That's a good question, and honestly I am not sure.
It is probably a good idea to have the tests which run the counterexample 
dumper and check that it does not crash.
As for the contents, I'm not sure: I would like to switch to generating HTML, 
and testing HTML output is IMO close to useless, because it has all the 
presentation stuff in it.
(it could have been possible to e.g. make the output mode which generates JSON, 
test that, and then use javascript templating to convert it to HTML, but that 
would not allow reusing existing code for outputting HTML formatted code with 
macros expanded properly)


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-15 Thread Alexander Shaposhnikov via Phabricator via cfe-commits
alexshap added a comment.

Are there any plans to add tests for this ?


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-15 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov added a comment.

@dcoughlin @NoQ I think this version is reasonable enough to get committed. 
Another easy iteration would be to change visitor to simply add the diagnostic 
to path, and move the actual printing to `CounterexampleDiagnostics`.


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-15 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov updated this revision to Diff 127197.
george.karpenkov edited the summary of this revision.
george.karpenkov added a comment.
Herald added a subscriber: mgorny.

Fixed formatting, moved output to diagnostic consumer.


https://reviews.llvm.org/D40809

Files:
  docs/ClangCommandLineReference.rst
  include/clang/Driver/Options.td
  include/clang/StaticAnalyzer/Core/Analyses.def
  include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
  include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
  lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  lib/StaticAnalyzer/Core/BugReporter.cpp
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  lib/StaticAnalyzer/Core/CMakeLists.txt
  lib/StaticAnalyzer/Core/CounterexampleDiagnostics.cpp

Index: lib/StaticAnalyzer/Core/CounterexampleDiagnostics.cpp
===
--- /dev/null
+++ lib/StaticAnalyzer/Core/CounterexampleDiagnostics.cpp
@@ -0,0 +1,40 @@
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+/// Dump the counterexample to stderr.
+class CounterexampleDiagnostics : public PathDiagnosticConsumer {
+
+public:
+  CounterexampleDiagnostics() {}
+
+  bool supportsCrossFileDiagnostics() const override { return false; }
+
+  ~CounterexampleDiagnostics() override { FlushDiagnostics(nullptr); }
+
+  StringRef getName() const override { return "CounterexampleDiagnostics"; }
+
+  /// Force usage of the counterexample visitor.
+  PathGenerationScheme getGenerationScheme() const override {
+return CounterexampleDump;
+  }
+
+  void FlushDiagnosticsImpl(std::vector ,
+FilesMade *filesMade) override {
+// FIXME move counterexample dumping logic here.
+  }
+};
+
+} // end anonymous namespace
+
+void ento::createCounterexampleDiagnosticsConsumer(
+AnalyzerOptions , PathDiagnosticConsumers ,
+const std::string , const Preprocessor ) {
+  C.push_back(new CounterexampleDiagnostics());
+}
Index: lib/StaticAnalyzer/Core/CMakeLists.txt
===
--- lib/StaticAnalyzer/Core/CMakeLists.txt
+++ lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -25,6 +25,7 @@
   CommonBugCategories.cpp
   ConstraintManager.cpp
   CoreEngine.cpp
+  CounterexampleDiagnostics.cpp
   DynamicTypeMap.cpp
   Environment.cpp
   ExplodedGraph.cpp
Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
===
--- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -22,9 +22,12 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/raw_ostream.h"
+#include 
+#include 
 
 using namespace clang;
 using namespace ento;
@@ -1878,3 +1881,144 @@
 
   return std::move(Piece);
 }
+
+/// Export the counterexample to C.
+class PrinterVisitor final : public BugReporterVisitorImpl {
+
+  /// File ID followed by a line number.
+  llvm::SetVector> VisitedLines;
+
+  /// Output stream to write into.
+  raw_ostream 
+
+  /// State machine variables.
+  mutable bool DonePrinting = false;
+
+  /// Max number of chars used to display the filename.
+  static const unsigned MAX_FILENAME_MARGIN = 80;
+  static const unsigned MARGIN_SEP_LEN = 1;
+  static constexpr const char *const REPORT_SEPARATOR =
+  "/* == */\n";
+
+public:
+  PrinterVisitor(raw_ostream ) : Ostream(Ostream) {}
+
+  PrinterVisitor() = delete;
+
+  void Profile(llvm::FoldingSetNodeID ) const override {
+static int Tag = 0;
+ID.AddPointer();
+  }
+
+  std::shared_ptr VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext ,
+ BugReport ) override {
+SourceManager  = 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 << REPORT_SEPARATOR;
+  DonePrinting = true;
+  return nullptr;
+} else if (const Stmt *S = 

[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-14 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov added a comment.

@dcoughlin my current iteration creates a `PathDiagnosticConsumer` which 
outputs HTML with this report. I think that makes much more sense (as 
essentially this is a way of visualizing the error path).


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-14 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

In https://reviews.llvm.org/D40809#955929, @dcoughlin wrote:

> As it stands, this is a debugging tool not a way to communicate error paths 
> to end users. (I think that, too, would be very helpful but I'd like to see 
> this as a debugging aid first.) The workflow for debugging would be more like 
> viewing the exploded graph than (say) emitting html diagnostics.
>
> My point is this: this is valuable as a debugging tool, we should get it 
> committed as such. We can work on making it user facing separately.


Per-report dumps are still much more useful for a debugging tool than 
whole-graph dumps. I guess such tool would be used on live code, not on reduced 
examples, and in this case there would be a *lot* of noise we'd want to avoid.


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-14 Thread Devin Coughlin via Phabricator via cfe-commits
dcoughlin added a comment.

In https://reviews.llvm.org/D40809#954890, @NoQ wrote:

> In https://reviews.llvm.org/D40809#954858, @dcoughlin wrote:
>
> > One possibility is to turn this into a debug checker similar to 
> > debug.ViewExplodedGraph. That checker registers for a checkEndAnalysis() 
> > callback and traverses the node graph (see DebugCheckers.cpp). Can you do 
> > the same here? It doesn't look like you really need this to be a 
> > BugReporterVisitor -- and making it a debug checker would avoid outputting 
> > multiple copies for each diagnostic consumer.
>
>
> These prints are only for actual bugs, not for the whole graph. Even if we 
> identify error nodes in the final graph, we're unlikely to identify which of 
> them are suppressed by visitors.


As it stands, this is a debugging tool not a way to communicate error paths to 
end users. (I think that, too, would be very helpful but I'd like to see this 
as a debugging aid first.) The workflow for debugging would be more like 
viewing the exploded graph than (say) emitting html diagnostics.

My point is this: this is valuable as a debugging tool, we should get it 
committed as such. We can work on making it user facing separately.


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-13 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

In https://reviews.llvm.org/D40809#954858, @dcoughlin wrote:

> One possibility is to turn this into a debug checker similar to 
> debug.ViewExplodedGraph. That checker registers for a checkEndAnalysis() 
> callback and traverses the node graph (see DebugCheckers.cpp). Can you do the 
> same here? It doesn't look like you really need this to be a 
> BugReporterVisitor -- and making it a debug checker would avoid outputting 
> multiple copies for each diagnostic consumer.


These prints are only for actual bugs, not for the whole graph


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-13 Thread Alexander Shaposhnikov via Phabricator via cfe-commits
alexshap added inline comments.



Comment at: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp:1976
+unsigned FileEndOffset = SM.getFileOffset(SM.getLocForEndOfFile(FID));
+for (unsigned i=Offset; BufferStart[i] != '\n' && i < FileEndOffset; ++i)
+  Ostream << BufferStart[i];

spaces around '=' (maybe clang-format this diff ?)


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-13 Thread Devin Coughlin via Phabricator via cfe-commits
dcoughlin added a comment.

This is seems like a very useful visualization, *especially* for loops. Can we 
this patch get it into a state where it can be committed and used for debugging 
purposes?

One possibility is to turn this into a debug checker similar to 
debug.ViewExplodedGraph. That checker registers for a checkEndAnalysis() 
callback and traverses the node graph (see DebugCheckers.cpp). Can you do the 
same here? It doesn't look like you really need this to be a BugReporterVisitor 
-- and making it a debug checker would avoid outputting multiple copies for 
each diagnostic consumer.

You could also control file output with an analyzer-config option that takes an 
optional path.


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-13 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov updated this revision to Diff 126853.
george.karpenkov edited the summary of this revision.

https://reviews.llvm.org/D40809

Files:
  include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
  lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  lib/StaticAnalyzer/Core/BugReporter.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 
+#include 
 #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,155 @@
 
   return std::move(Piece);
 }
+
+/// Export the counterexample to C.
+class PrinterVisitor final : public BugReporterVisitorImpl {
+
+  /// File ID followed by a line number.
+  llvm::SetVector> VisitedLines;
+
+  /// Output stream to write into.
+  raw_ostream 
+
+  /// State machine variables.
+  mutable bool DonePrinting = false;
+
+  /// Max number of chars used to display the filename.
+  static const unsigned MAX_FILENAME_MARGIN = 80;
+  static const unsigned MARGIN_SEP_LEN = 1; 
+  static constexpr const char * const REPORT_SEPARATOR =
+  "/* == */\n";
+
+public:
+  PrinterVisitor(raw_ostream ) : Ostream(Ostream) {}
+
+  PrinterVisitor() = delete;
+
+  void Profile(llvm::FoldingSetNodeID ) const override {
+static int Tag = 0;
+ID.AddPointer();
+  }
+
+  std::shared_ptr VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext ,
+ BugReport ) override {
+SourceManager  = 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 << REPORT_SEPARATOR;
+  DonePrinting = true;
+  return nullptr;
+} else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) {
+
+  // Statement location with an associated SourceLocation object.
+  SourceLocation Loc = S->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 ) {
+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 , 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, ).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 , 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

[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-05 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

So we have an `ExplodedGraph`, in that there's an `ExplodedNode` against which 
the report is being thrown, we have a bunch of `BugReporterVisitor`s that walk 
from that node to the root of the graph and mark places they find interesting 
with `PathDiagnosticPiece`s of different kinds, and `PathDiagnosticConsumer`s 
(such as Plist or Html) that look at the vector of pieces and display it to the 
user.

It sounds solid (even if a bit over-engineered) to implement your 
counterexample generator as a set of:

1. new class of `PathDiagnosticPiece`s that holds information that you need 
about a single line of code. Like, a while ago i was adding blue bubbles to 
scan-build (https://reviews.llvm.org/D24278), you might use this as an example 
on how to add a new class of diagnostic pieces.
2. a new `BugReporterVisitor` attached to all reports that puts such piece on 
every line of code. You already have it, but now it'd return the piece instead 
of printing stuff to console. Now that i think of it, the best place to add a 
visitor to all reports would be `GRBugReporter::generatePathDiagnostic()` - 
this is where other non-checker-specific visitors are added.
3. a new `PathDiagnosticConsumer` that would transform your new pieces into 
report files or however you want to present the results. I guess you could 
follow the examples of the existing consumers here; i didn't hack much on 
those, but there doesn't seem to be much boilerplate here. Most of the 
complicated processing, if any, goes here, the visitor can at best provide a 
set of pieces in chronological order, he cannot rearrange them anyhow but the 
consumer can.


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-05 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

In https://reviews.llvm.org/D40809#945551, @NoQ wrote:

> If you want to turn this into a form of user-facing analyzer output variant, 
> you may want to implement it as a `PathDiagnosticConsumer` rather than a 
> visitor.


Or maybe no, actually it's totally wrong, never mind. `PathDiagnosticConsumer` 
only operates on diagnostics, and it no longer knows anything about nodes, so 
the info is lost by that time. We could probably still go this way by making a 
visitor emit special per-line path diagnostics that are all ignored by all 
consumers except yours. This may actually be the best way to go, but such patch 
would be very intrusive because there are many switches by diagnostic kinds all 
over the place and they all would need to be updated.


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-05 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

This looks great for understanding what exactly is going on in a large 
real-world report.

If you want to turn this into a form of user-facing analyzer output variant, 
you may want to implement it as a `PathDiagnosticConsumer` rather than a 
visitor. If we only want a neat debugging mechanism, then you might want to 
stick it directly into `BugReporter::emitReport()` or maybe 
`BugReporter::FlushReport()` (not sure how both of these interact with report 
deduplication).


https://reviews.llvm.org/D40809



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-04 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov updated this revision to Diff 125429.
george.karpenkov edited the summary of this revision.

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 
+#include 
 #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,151 @@
 
   return std::move(Piece);
 }
+
+/// Export the counterexample to C.
+class PrinterVisitor final : public BugReporterVisitorImpl {
+
+  /// File ID followed by a line number.
+  llvm::SetVector> VisitedLines;
+  mutable bool DonePrinting = false;
+
+  /// Output stream to write into.
+  raw_ostream 
+
+  /// Max number of chars used to display the filename.
+  static const unsigned MAX_FILENAME_MARGIN = 80;
+  static const unsigned MARGIN_SEP_LEN = 1; 
+  static constexpr const char * const REPORT_SEPARATOR =
+  "/* == */\n";
+
+public:
+  PrinterVisitor(raw_ostream ) : Ostream(Ostream) {}
+
+  void Profile(llvm::FoldingSetNodeID ) const override {
+static int Tag = 0;
+ID.AddPointer();
+  }
+
+  std::shared_ptr VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext ,
+ BugReport ) override {
+SourceManager  = 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 << REPORT_SEPARATOR;
+  DonePrinting = true;
+  return nullptr;
+} else if (Optional PS = N->getLocation().getAs()) {
+
+  // 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 ) {
+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 , 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, ).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 , 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

[PATCH] D40809: [WIP] [analyzer] Dump counterexample traces as C programs

2017-12-04 Thread George Karpenkov via Phabricator via cfe-commits
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 
+#include 
 #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 {
+
+  /// File ID followed by a line number.
+  llvm::SetVector> VisitedLines;
+  mutable bool DonePrinting = false;
+
+  /// Output stream to write into.
+  raw_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) {}
+
+  void Profile(llvm::FoldingSetNodeID ) const override {
+static int Tag = 0;
+ID.AddPointer();
+  }
+
+  std::shared_ptr VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext ,
+ BugReport ) override {
+SourceManager  = 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 PS = N->getLocation().getAs()) {
+
+  // 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 ) {
+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.
+