[analyzer] Assign truly stable identifiers to exploded nodes.
authorArtem Dergachev <artem.dergachev@gmail.com>
Thu, 17 Oct 2019 23:10:09 +0000 (23:10 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Thu, 17 Oct 2019 23:10:09 +0000 (23:10 +0000)
ExplodedGraph nodes will now have a numeric identifier stored in them
which will keep track of the order in which the nodes were created
and it will be fully deterministic both accross runs and across machines.

This is extremely useful for debugging as it allows reliably setting
conditional breakpoints by node IDs.

llvm-svn: 375186

19 files changed:
clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h
clang/lib/StaticAnalyzer/Core/BugReporter.cpp
clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
clang/test/Analysis/dump_egraph.c
clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot
clang/test/Analysis/exploded-graph-rewriter/checker_messages_diff.dot
clang/test/Analysis/exploded-graph-rewriter/constraints.dot
clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot
clang/test/Analysis/exploded-graph-rewriter/edge.dot
clang/test/Analysis/exploded-graph-rewriter/environment.dot
clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot
clang/test/Analysis/exploded-graph-rewriter/node_labels.dot
clang/test/Analysis/exploded-graph-rewriter/program_points.dot
clang/test/Analysis/exploded-graph-rewriter/store.dot
clang/test/Analysis/exploded-graph-rewriter/store_diff.dot
clang/test/Analysis/exploded-graph-rewriter/topology.dot
clang/test/Analysis/exploded-graph-rewriter/trimmers.dot
clang/utils/analyzer/exploded-graph-rewriter.py

index 9773a55..e87772c 100644 (file)
@@ -131,10 +131,12 @@ class ExplodedNode : public llvm::FoldingSetNode {
   /// Succs - The successors of this node.
   NodeGroup Succs;
 
+  int64_t Id;
+
 public:
   explicit ExplodedNode(const ProgramPoint &loc, ProgramStateRef state,
-                        bool IsSink)
-      : Location(loc), State(std::move(state)), Succs(IsSink) {
+                        int64_t Id, bool IsSink)
+      : Location(loc), State(std::move(state)), Succs(IsSink), Id(Id) {
     assert(isSink() == IsSink);
   }
 
@@ -258,7 +260,7 @@ public:
   }
   const_succ_range succs() const { return {Succs.begin(), Succs.end()}; }
 
-  int64_t getID(ExplodedGraph *G) const;
+  int64_t getID() const { return Id; }
 
   /// The node is trivial if it has only one successor, only one predecessor,
   /// it's predecessor has only one successor,
@@ -324,7 +326,7 @@ protected:
   BumpVectorContext BVC;
 
   /// NumNodes - The number of nodes in the graph.
-  unsigned NumNodes = 0;
+  int64_t NumNodes = 0;
 
   /// A list of recently allocated nodes that can potentially be recycled.
   NodeVector ChangedNodes;
@@ -358,6 +360,7 @@ public:
   ///  ExplodedGraph for further processing.
   ExplodedNode *createUncachedNode(const ProgramPoint &L,
     ProgramStateRef State,
+    int64_t Id,
     bool IsSink = false);
 
   std::unique_ptr<ExplodedGraph> MakeEmptyGraph() const {
index f689d8e..1864bce 100644 (file)
@@ -2566,7 +2566,8 @@ BugPathInfo *BugPathGetter::getNextBugPath() {
     // Create the equivalent node in the new graph with the same state
     // and location.
     ExplodedNode *NewN = GNew->createUncachedNode(
-        OrigN->getLocation(), OrigN->getState(), OrigN->isSink());
+        OrigN->getLocation(), OrigN->getState(),
+        OrigN->getID(), OrigN->isSink());
 
     // Link up the new node with the previous node.
     if (Succ)
index a8fa7ad..c483849 100644 (file)
@@ -283,10 +283,6 @@ ExplodedNode * const *ExplodedNode::NodeGroup::end() const {
   return Storage.getAddrOfPtr1() + 1;
 }
 
-int64_t ExplodedNode::getID(ExplodedGraph *G) const {
-  return G->getAllocator().identifyKnownAlignedObject<ExplodedNode>(this);
-}
-
 bool ExplodedNode::isTrivial() const {
   return pred_size() == 1 && succ_size() == 1 &&
          getFirstPred()->getState()->getID() == getState()->getID() &&
@@ -417,14 +413,14 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
       V = (NodeTy*) getAllocator().Allocate<NodeTy>();
     }
 
-    new (V) NodeTy(L, State, IsSink);
+    ++NumNodes;
+    new (V) NodeTy(L, State, NumNodes, IsSink);
 
     if (ReclaimNodeInterval)
       ChangedNodes.push_back(V);
 
     // Insert the node into the node set and return it.
     Nodes.InsertNode(V, InsertPos);
-    ++NumNodes;
 
     if (IsNew) *IsNew = true;
   }
@@ -436,9 +432,10 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
 
 ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L,
                                                 ProgramStateRef State,
+                                                int64_t Id,
                                                 bool IsSink) {
   NodeTy *V = (NodeTy *) getAllocator().Allocate<NodeTy>();
-  new (V) NodeTy(L, State, IsSink);
+  new (V) NodeTy(L, State, Id, IsSink);
   return V;
 }
 
@@ -498,7 +495,8 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
 
     // Create the corresponding node in the new graph and record the mapping
     // from the old node to the new node.
-    ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, N->isSink());
+    ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State,
+                                               N->getID(), N->isSink());
     Pass2[N] = NewN;
 
     // Also record the reverse mapping from the new node to the old node.
index 8629fd9..ea1df1b 100644 (file)
@@ -3061,16 +3061,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
     const unsigned int Space = 1;
     ProgramStateRef State = N->getState();
 
-    auto Noop = [](const ExplodedNode*){};
-    bool HasReport = traverseHiddenNodes(
-        N, Noop, Noop, &nodeHasBugReport);
-    bool IsSink = traverseHiddenNodes(
-        N, Noop, Noop, [](const ExplodedNode *N) { return N->isSink(); });
-
-    Out << "{ \"node_id\": " << N->getID(G) << ", \"pointer\": \""
-        << (const void *)N << "\", \"state_id\": " << State->getID()
-        << ", \"has_report\": " << (HasReport ? "true" : "false")
-        << ", \"is_sink\": " << (IsSink ? "true" : "false")
+    Out << "{ \"state_id\": " << State->getID()
         << ",\\l";
 
     Indent(Out, Space, IsDot) << "\"program_points\": [\\l";
@@ -3083,9 +3074,12 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
           OtherNode->getLocation().printJson(Out, /*NL=*/"\\l");
           Out << ", \"tag\": ";
           if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag())
-            Out << '\"' << Tag->getTagDescription() << "\" }";
+            Out << '\"' << Tag->getTagDescription() << "\"";
           else
-            Out << "null }";
+            Out << "null";
+          Out << ", \"node_id\": " << OtherNode->getID() <<
+                 ", \"is_sink\": " << OtherNode->isSink() <<
+                 ", \"has_report\": " << nodeHasBugReport(OtherNode) << " }";
         },
         // Adds a comma and a new-line between each program point.
         [&](const ExplodedNode *) { Out << ",\\l"; },
index 99463da..4ad0400 100644 (file)
@@ -18,7 +18,7 @@ int foo() {
   return *x + *y;
 }
 
-// CHECK: \"program_points\": [\l&nbsp;&nbsp;&nbsp;&nbsp;\{ \"kind\": \"Edge\", \"src_id\": 2, \"dst_id\": 1, \"terminator\": null, \"term_kind\": null, \"tag\": null \}\l&nbsp;&nbsp;],\l&nbsp;&nbsp;\"program_state\": null
+// CHECK: \"program_points\": [\l&nbsp;&nbsp;&nbsp;&nbsp;\{ \"kind\": \"Edge\", \"src_id\": 2, \"dst_id\": 1, \"terminator\": null, \"term_kind\": null, \"tag\": null, \"node_id\": 1, \"is_sink\":0, \"has_report\": 0 \}\l&nbsp;&nbsp;],\l&nbsp;&nbsp;\"program_state\": null
 
 // CHECK: \"program_points\": [\l&nbsp;&nbsp;&nbsp;&nbsp;\{ \"kind\": \"BlockEntrance\", \"block_id\": 1
 
@@ -27,4 +27,4 @@ int foo() {
 
 // CHECK: \"pretty\": \"'\\\\x13'\"
 
-// CHECK: \"has_report\": true
+// CHECK: \"has_report\": 1
index 84185db..2d054a8 100644 (file)
@@ -14,7 +14,14 @@ Node0x1 [shape=record,label=
       "has_report": false,
       "is_sink": false,
       "state_id": 2,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "store": null,
         "constraints": null,
index 2f0bcbd..898f796 100644 (file)
@@ -5,12 +5,15 @@
 
 Node0x1 [shape=record,label=
  "{
-    { "node_id": 1,
-      "pointer": "0x1",
-      "has_report": false,
-      "is_sink": false,
-      "state_id": 2,
-      "program_points": [],
+    { "state_id": 2,
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "environment": null,
         "store": null,
@@ -59,12 +62,16 @@ Node0x1 -> Node0x4;
 // CHECK-SAME: </tr>
 Node0x4 [shape=record,label=
  "{
-    { "node_id": 4,
-      "pointer": "0x4",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 5,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "environment": null,
         "store": null,
@@ -88,12 +95,15 @@ Node0x4 -> Node0x6;
 
 Node0x6 [shape=record,label=
  "{
-    { "node_id": 6,
-      "pointer": "0x6",
-      "has_report": false,
-      "is_sink": false,
-      "state_id": 7,
-      "program_points": [],
+    { "state_id": 7,
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": null
     }
 \l}"];
index 075df98..f5ebcf1 100644 (file)
 // CHECK-SAME: </table></td></tr>
 Node0x1 [shape=record,label=
  "{
-    { "node_id": 1,
-      "pointer": "0x1",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 2,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "store": null,
         "environment": null,
index 00b2f14..53a87aa 100644 (file)
@@ -5,12 +5,16 @@
 
 Node0x1 [shape=record,label=
  "{
-    { "node_id": 1,
-      "pointer": "0x1",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 2,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "store": null,
         "environment": null,
@@ -39,12 +43,16 @@ Node0x1 -> Node0x3;
 // CHECK-SAME: </tr>
 Node0x3 [shape=record,label=
  "{
-    { "node_id": 3,
-      "pointer": "0x3",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 4,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "store": null,
         "environment": null,
@@ -62,12 +70,16 @@ Node0x3 -> Node0x5;
 
 Node0x5 [shape=record,label=
  "{
-    { "node_id": 5,
-      "pointer": "0x5",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 6,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "store": null,
         "environment": null,
index 15e5561..3923f1f 100644 (file)
@@ -5,13 +5,25 @@
 // UNSUPPORTED: system-windows
 
 Node0x1 [shape=record,label=
- "{{ "node_id": 1, "pointer": "0x1", "has_report": false, "is_sink": false,
-     "program_state": null, "program_points": []}\l}"];
+ "{{ "program_state": null, "program_points": [
+    {
+      "kind": "BlockEntrance", "block_id": 1,
+      "terminator": null, "term_kind": null,
+      "tag": null, "node_id": 1,
+      "has_report": 0, "is_sink": 0
+    }
+  ]}\l}"];
 
 // LIGHT: Node0x1 -> Node0x2;
 // DARK:  Node0x1 -> Node0x2 [color="white"];
 Node0x1 -> Node0x2;
 
 Node0x2 [shape=record,label=
- "{{ "node_id": 2, "pointer": "0x2", "has_report": false, "is_sink": false,
-     "program_state": null, "program_points": []}\l}"];
+ "{{ "program_state": null, "program_points": [
+    {
+      "kind": "BlockEntrance", "block_id": 1,
+      "terminator": null, "term_kind": null,
+      "tag": null, "node_id": 1,
+      "has_report": 0, "is_sink": 0
+    }
+ ]}\l}"];
index 399484a..733aae3 100644 (file)
@@ -11,7 +11,7 @@
 // CHECK-SAME:     </td>
 // CHECK-SAME:     <td align="left" colspan="2">
 // CHECK-SAME:       <font color="gray60">foo </font>
-// CHECK-SAME:       (environment.cpp:<b>4</b>:<b>6</b> 
+// CHECK-SAME:       (environment.cpp:<b>4</b>:<b>6</b>
 // CHECK-SAME:       <font color="royalblue1">
 // CHECK-SAME:         (<i>spelling at </i> environment.h:<b>7</b>:<b>8</b>)
 // CHECK-SAME:       </font>)
@@ -36,7 +36,14 @@ Node0x1 [shape=record,label=
       "has_report": false,
       "is_sink": false,
       "state_id": 2,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "store": null,
         "constraints": null,
index 475247b..05e8d4e 100644 (file)
@@ -6,12 +6,16 @@
 // No diffs on the first node, nothing to check.
 Node0x1 [shape=record,label=
  "{
-    { "node_id": 1,
-      "pointer": "0x1",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 2,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "store": null,
         "constraints": null,
@@ -57,12 +61,16 @@ Node0x1 -> Node0x6;
 // CHECK-SAME: </tr>
 Node0x6 [shape=record,label=
  "{
-    { "node_id": 6,
-      "pointer": "0x6",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 7,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "store": null,
         "constraints": null,
@@ -102,12 +110,16 @@ Node0x6 -> Node0x9;
 // CHECK-SAME: </tr>
 Node0x9 [shape=record,label=
  "{
-    { "node_id": 9,
-      "pointer": "0x9",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 7,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "store": null,
         "constraints": null,
index a434cd2..a3d7420 100644 (file)
 // CHECK-SAME:   <tr>
 // LIGHT-SAME:     <td bgcolor="gray70">
 // DARK-SAME:      <td bgcolor="gray20">
-// CHECK-SAME:       <b>Node 1 (0x1) - State Unspecified</b>
+// CHECK-SAME:       <b>State Unspecified</b>
 // CHECK-SAME:     </td>
 // CHECK-SAME:   </tr>
 Node0x1 [shape=record,label=
  "{
     { "node_id": 1, "pointer": "0x1", "has_report": false, "is_sink": false,
       "program_state": null,
-      "program_points": []
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ]
     }
 \l}"];
 
 // CHECK: Node0x2 [
-// CHECK-SAME: <tr><td>
-// COLOR-SAME:   <font color="red"><b>Bug Report Attached</b></font>
-// GRAY-SAME:    <b>Bug Report Attached</b>
-// CHECK-SAME: </td></tr>
-// CHECK-SAME: <tr><td>
-// COLOR-SAME:   <font color="cornflowerblue"><b>Sink Node</b></font>
-// GRAY-SAME:    <b>Sink Node</b>
-// CHECK-SAME: </td></tr>
+// CHECK-SAME: <tr>
+// CHECK-SAME:   <td colspan="3" align="left">
+// COLOR-SAME:     <font color="red"><b>Bug Report Attached</b></font>
+// GRAY-SAME:      <b>Bug Report Attached</b>
+// CHECK-SAME:   </td>
+// CHECK-SAME: </tr>
+// CHECK-SAME: <tr>
+// CHECK-SAME:   <td colspan="3" align="left">
+// COLOR-SAME:     <font color="cornflowerblue"><b>Sink Node</b></font>
+// GRAY-SAME:      <b>Sink Node</b>
+// CHECK-SAME:   </td>
+// CHECK-SAME: </tr>
 Node0x2 [shape=record,label=
  "{
-    { "node_id": 2, "pointer": "0x2", "has_report": true, "is_sink": true,
-      "program_state": null,
-      "program_points": []
+    { "program_state": null,
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 2,
+          "has_report": 1, "is_sink": 1
+        }
+      ]
     }
 \l}"];
index c27c230..c949275 100644 (file)
@@ -28,7 +28,7 @@
 // CHECK-SAME: </table>
 Node0x1 [shape=record,label=
  "{
-    { "node_id": 1, "pointer": "0x1", "has_report": false, "is_sink": false,
+    {
       "program_state": null, "program_points": [
       {
         "kind": "Edge",
@@ -36,14 +36,20 @@ Node0x1 [shape=record,label=
         "dst_id": 1,
         "terminator": null,
         "term_kind": null,
-        "tag": null
+        "tag": null,
+        "node_id": 1,
+        "has_report": 0,
+        "is_sink": 0
       },
       {
         "kind": "BlockEntrance",
         "block_id": 1,
         "terminator": null,
         "term_kind": null,
-        "tag": null
+        "tag": null,
+        "node_id": 2,
+        "has_report": 0,
+        "is_sink": 0
       }
     ]}
 \l}"];
@@ -72,10 +78,9 @@ Node0x1 [shape=record,label=
 // CHECK-SAME:     </td>
 // CHECK-SAME:   </tr>
 // CHECK-SAME: </table>
-Node0x2 [shape=record,label=
+Node0x3 [shape=record,label=
  "{
-    { "node_id": 2, "pointer": "0x2", "has_report": false, "is_sink": false,
-      "program_state": null, "program_points": [
+    { "program_state": null, "program_points": [
       {
         "kind": "Statement",
         "stmt_kind": "DeclRefExpr",
@@ -88,7 +93,11 @@ Node0x2 [shape=record,label=
           "line": 4,
           "column": 5
         },
-        "tag": "ExprEngine : Clean Node"
+        "tag": "ExprEngine : Clean Node",
+        "node_id": 3,
+        "pointer": "0x3",
+        "has_report": 0,
+        "is_sink": 0
       }
     ]}
 \l}"];
@@ -97,9 +106,9 @@ Node0x2 [shape=record,label=
 
 // CHECK-NEXT: <b>Program point:</b>
 // CHECK-SAME: <td align="left">\{ ... \}</td>
-Node0x3 [shape=record,label=
+Node0x4 [shape=record,label=
  "{
-    { "node_id": 3, "pointer": "0x3", "has_report": false, "is_sink": false,
+    {
       "program_state": null, "program_points": [
       {
         "kind": "Statement",
@@ -112,7 +121,10 @@ Node0x3 [shape=record,label=
           "line": 7,
           "column": 8
         },
-        "tag": "ExprEngine : Clean Node"
+        "tag": "ExprEngine : Clean Node",
+        "node_id": 4,
+        "has_report": 0,
+        "is_sink": 0
       }
     ]}
 \l}"];
@@ -143,10 +155,9 @@ Node0x3 [shape=record,label=
 // CHECK-SAME:     </td>
 // CHECK-SAME:   </tr>
 // CHECK-SAME: </table>
-Node0x4 [shape=record,label=
+Node0x5 [shape=record,label=
  "{
-    { "node_id": 4, "pointer": "0x4", "has_report": false, "is_sink": false,
-      "program_state": null, "program_points": [
+    { "program_state": null, "program_points": [
       {
         "kind": "Statement",
         "stmt_kind": "ImplicitCastExpr",
@@ -160,7 +171,10 @@ Node0x4 [shape=record,label=
           "line": 8,
           "column": 9
         },
-        "tag": "ExprEngine : Clean Node"
+        "tag": "ExprEngine : Clean Node",
+        "node_id": 5,
+        "has_report": 0,
+        "is_sink": 0
       }
     ]}
 \l}"];
index 7267dd4..c92901c 100644 (file)
 // CHECK-SAME: </table>
 Node0x1 [shape=record,label=
  "{
-    { "node_id": 1,
-      "pointer": "0x1",
-      "has_report": false,
-      "is_sink": false,
-      "state_id": 2,
-      "program_points": [],
+    { "state_id": 2,
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "environment": null,
         "constraints": null,
index 94d1d8d..8dd5fb4 100644 (file)
@@ -10,7 +10,14 @@ Node0x1 [shape=record,label=
       "has_report": false,
       "is_sink": false,
       "state_id": 2,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "environment": null,
         "constraints": null,
@@ -55,12 +62,16 @@ Node0x1 -> Node0x4;
 // CHECK-SAME: </tr>
 Node0x4 [shape=record,label=
  "{
-    { "node_id": 4,
-      "pointer": "0x4",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 5,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "environment": null,
         "constraints": null,
@@ -91,12 +102,16 @@ Node0x4 -> Node0x6;
 
 Node0x6 [shape=record,label=
  "{
-    { "node_id": 6,
-      "pointer": "0x6",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 7,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": null
     }
 \l}"];
index fa1b10f..b85115e 100644 (file)
 // TOPOLOGY-NOT: Checker State
 Node0x1 [shape=record,label=
  "{
-    { "node_id": 1,
-      "pointer": "0x1",
-      "has_report": false,
-      "is_sink": false,
+    {
       "state_id": 2,
-      "program_points": [],
+      "program_points": [
+        {
+          "kind": "BlockEntrance", "block_id": 1,
+          "terminator": null, "term_kind": null,
+          "tag": null, "node_id": 1,
+          "has_report": 0, "is_sink": 0
+        }
+      ],
       "program_state": {
         "environment": null,
         "constraints": null,
index 8bdef64..2c441e0 100644 (file)
 // UNSUPPORTED: system-windows
 
 Node0x1 [shape=record,label=
- "{{ "node_id": 1, "pointer": "0x1", "has_report": false, "is_sink": false,
-      "program_state": null, "program_points": []}\l}"];
+ "{{ "program_state": null, "program_points": [
+       {
+         "kind": "BlockEntrance", "block_id": 1,
+         "terminator": null, "term_kind": null,
+         "tag": null, "node_id": 1,
+         "has_report": 0, "is_sink": 0
+       }
+     ]}\l}"];
 
 Node0x2 [shape=record,label=
- "{{ "node_id": 2, "pointer": "0x2", "has_report": false, "is_sink": false,
-      "program_state": null, "program_points": []}\l}"];
+ "{{ "program_state": null, "program_points": [
+       {
+         "kind": "BlockEntrance", "block_id": 1,
+         "terminator": null, "term_kind": null,
+         "tag": null, "node_id": 2,
+         "has_report": 0, "is_sink": 0
+       }
+     ]}\l}"];
 
 Node0x3 [shape=record,label=
- "{{ "node_id": 3, "pointer": "0x3", "has_report": false, "is_sink": false,
-      "program_state": null, "program_points": []}\l}"];
+ "{{ "program_state": null, "program_points": [
+       {
+         "kind": "BlockEntrance", "block_id": 1,
+         "terminator": null, "term_kind": null,
+         "tag": null, "node_id": 3,
+         "has_report": 0, "is_sink": 0
+       }
+     ]}\l}"];
 
 Node0x4 [shape=record,label=
- "{{ "node_id": 4, "pointer": "0x4", "has_report": false, "is_sink": false,
-      "program_state": null, "program_points": []}\l}"];
+ "{{ "program_state": null, "program_points": [
+       {
+         "kind": "BlockEntrance", "block_id": 1,
+         "terminator": null, "term_kind": null,
+         "tag": null, "node_id": 4,
+         "has_report": 0, "is_sink": 0
+       }
+     ]}\l}"];
 
 Node0x1 -> Node0x2;
 Node0x1 -> Node0x3;
index 77da739..79222bd 100755 (executable)
@@ -67,6 +67,9 @@ class ProgramPoint(object):
         super(ProgramPoint, self).__init__()
         self.kind = json_pp['kind']
         self.tag = json_pp['tag']
+        self.node_id = json_pp['node_id']
+        self.is_sink = bool(json_pp['is_sink'])
+        self.has_report = bool(json_pp['has_report'])
         if self.kind == 'Edge':
             self.src_id = json_pp['src_id']
             self.dst_id = json_pp['dst_id']
@@ -309,11 +312,9 @@ class ExplodedNode(object):
 
     def construct(self, node_id, json_node):
         logging.debug('Adding ' + node_id)
-        self.node_id = json_node['node_id']
-        self.ptr = json_node['pointer']
-        self.has_report = json_node['has_report']
-        self.is_sink = json_node['is_sink']
+        self.ptr = node_id[4:]
         self.points = [ProgramPoint(p) for p in json_node['program_points']]
+        self.node_id = self.points[-1].node_id
         self.state = ProgramState(json_node['state_id'],
                                   json_node['program_state']) \
             if json_node['program_state'] is not None else None
@@ -488,12 +489,14 @@ class DotDumpVisitor(object):
         else:
             color = 'forestgreen'
 
+        self._dump('<tr><td align="left">%s.</td>' % p.node_id)
+
         if p.kind == 'Statement':
             # This avoids pretty-printing huge statements such as CompoundStmt.
             # Such statements show up only at [Pre|Post]StmtPurgeDeadSymbols
             skip_pretty = 'PurgeDeadSymbols' in p.stmt_point_kind
             stmt_color = 'cyan3'
-            self._dump('<tr><td align="left" width="0">%s:</td>'
+            self._dump('<td align="left" width="0">%s:</td>'
                        '<td align="left" width="0"><font color="%s">'
                        '%s</font> </td>'
                        '<td align="left"><i>S%s</i></td>'
@@ -506,30 +509,41 @@ class DotDumpVisitor(object):
                           self._short_pretty(p.pretty)
                           if not skip_pretty else ''))
         elif p.kind == 'Edge':
-            self._dump('<tr><td width="0"></td>'
+            self._dump('<td width="0"></td>'
                        '<td align="left" width="0">'
                        '<font color="%s">%s</font></td><td align="left">'
                        '[B%d] -\\> [B%d]</td></tr>'
                        % (color, 'BlockEdge', p.src_id, p.dst_id))
         elif p.kind == 'BlockEntrance':
-            self._dump('<tr><td width="0"></td>'
+            self._dump('<td width="0"></td>'
                        '<td align="left" width="0">'
                        '<font color="%s">%s</font></td>'
                        '<td align="left">[B%d]</td></tr>'
                        % (color, p.kind, p.block_id))
         else:
             # TODO: Print more stuff for other kinds of points.
-            self._dump('<tr><td width="0"></td>'
+            self._dump('<td width="0"></td>'
                        '<td align="left" width="0" colspan="2">'
                        '<font color="%s">%s</font></td></tr>'
                        % (color, p.kind))
 
         if p.tag is not None:
-            self._dump('<tr><td width="0"></td>'
+            self._dump('<tr><td width="0"></td><td width="0"></td>'
                        '<td colspan="3" align="left">'
                        '<b>Tag: </b> <font color="crimson">'
                        '%s</font></td></tr>' % p.tag)
 
+        if p.has_report:
+            self._dump('<tr><td width="0"></td><td width="0"></td>'
+                       '<td colspan="3" align="left">'
+                       '<font color="red"><b>Bug Report Attached'
+                       '</b></font></td></tr>')
+        if p.is_sink:
+            self._dump('<tr><td width="0"></td><td width="0"></td>'
+                       '<td colspan="3" align="left">'
+                       '<font color="cornflowerblue"><b>Sink Node'
+                       '</b></font></td></tr>')
+
     def visit_environment(self, e, prev_e=None):
         self._dump('<table border="0">')
 
@@ -786,17 +800,10 @@ class DotDumpVisitor(object):
             self._dump('color="white",fontcolor="gray80",')
         self._dump('label=<<table border="0">')
 
-        self._dump('<tr><td bgcolor="%s"><b>Node %d (%s) - '
-                   'State %s</b></td></tr>'
+        self._dump('<tr><td bgcolor="%s"><b>State %s</b></td></tr>'
                    % ("gray20" if self._dark_mode else "gray70",
-                      node.node_id, node.ptr, node.state.state_id
+                      node.state.state_id
                       if node.state is not None else 'Unspecified'))
-        if node.has_report:
-            self._dump('<tr><td><font color="red"><b>Bug Report Attached'
-                       '</b></font></td></tr>')
-        if node.is_sink:
-            self._dump('<tr><td><font color="cornflowerblue"><b>Sink Node'
-                       '</b></font></td></tr>')
         if not self._topo_mode:
             self._dump('<tr><td align="left" width="0">')
             if len(node.points) > 1: