r368768 - [analyzer] exploded-graph-rewriter: Implement manual graph trimming.

Artem Dergachev via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 13 16:04:53 PDT 2019


Author: dergachev
Date: Tue Aug 13 16:04:53 2019
New Revision: 368768

URL: http://llvm.org/viewvc/llvm-project?rev=368768&view=rev
Log:
[analyzer] exploded-graph-rewriter: Implement manual graph trimming.

When -trim-egraph is unavailable (say, when you're debugging a crash on
a real-world code that takes too long to reduce), it makes sense to view
the untrimmed graph up to the crashing node's predecessor, then dump the ID
(or a pointer) of the node in the attached debugger, and then trim
the dumped graph in order to keep only paths from the root to the node.

The newly added --to flag does exactly that:

$ exploded-graph-rewriter.py ExprEngine.dot --to 0x12229acd0

Multiple nodes can be specified. Stable IDs of nodes can be used
instead of pointers.

Differential Revision: https://reviews.llvm.org/D65345

Modified:
    cfe/trunk/test/Analysis/exploded-graph-rewriter/trimmers.dot
    cfe/trunk/utils/analyzer/exploded-graph-rewriter.py

Modified: cfe/trunk/test/Analysis/exploded-graph-rewriter/trimmers.dot
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/exploded-graph-rewriter/trimmers.dot?rev=368768&r1=368767&r2=368768&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/exploded-graph-rewriter/trimmers.dot (original)
+++ cfe/trunk/test/Analysis/exploded-graph-rewriter/trimmers.dot Tue Aug 13 16:04:53 2019
@@ -1,7 +1,17 @@
 // RUN: %exploded_graph_rewriter %s \
-// RUN:     | FileCheck %s -check-prefixes=CHECK,BASIC
+// RUN:     | FileCheck %s -check-prefixes=ONE,TWO,THREE,FOUR
 // RUN: %exploded_graph_rewriter -s %s \
-// RUN:     | FileCheck %s -check-prefixes=CHECK,SINGLE
+// RUN:     | FileCheck %s -check-prefixes=ONE,TWO,NOTHREE,FOUR
+// RUN: %exploded_graph_rewriter --to=0x2 %s \
+// RUN:     | FileCheck %s -check-prefixes=ONE,TWO,NOTHREE,NOFOUR
+// RUN: %exploded_graph_rewriter --to 2 %s \
+// RUN:     | FileCheck %s -check-prefixes=ONE,TWO,NOTHREE,NOFOUR
+// RUN: %exploded_graph_rewriter --to 2,3 %s \
+// RUN:     | FileCheck %s -check-prefixes=ONE,TWO,THREE,NOFOUR
+// RUN: %exploded_graph_rewriter --to 4 %s \
+// RUN:     | FileCheck %s -check-prefixes=ONE,TWO,THREE,FOUR
+// RUN: %exploded_graph_rewriter --to 4 -s %s \
+// RUN:     | FileCheck %s -check-prefixes=ONE,TWO,NOTHREE,FOUR
 
 // FIXME: Substitution doesn't seem to work on Windows.
 // UNSUPPORTED: system-windows
@@ -22,16 +32,16 @@ Node0x4 [shape=record,label=
  "{{ "node_id": 4, "pointer": "0x4", "has_report": false, "is_sink": false,
       "program_state": null, "program_points": []}\l}"];
 
-// CHECK: Node0x1 -> Node0x2;
 Node0x1 -> Node0x2;
-
-// BASIC: Node0x1 -> Node0x3;
-// SINGLE-NOT: Node0x1 -> Node0x3;
 Node0x1 -> Node0x3;
-
-// CHECK: Node0x2 -> Node0x4;
 Node0x2 -> Node0x4;
-
-// BASIC: Node0x3 -> Node0x4;
-// SINGLE-NOT: Node0x3 -> Node0x4;
 Node0x3 -> Node0x4;
+
+// ONE: Node0x1
+// NOTONE-NOT: Node0x1
+// TWO: Node0x2
+// NOTTWO-NOT: Node0x2
+// THREE: Node0x3
+// NOTTHREE-NOT: Node0x3
+// FOUR: Node0x4
+// NOTFOUR-NOT: Node0x4

Modified: cfe/trunk/utils/analyzer/exploded-graph-rewriter.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/analyzer/exploded-graph-rewriter.py?rev=368768&r1=368767&r2=368768&view=diff
==============================================================================
--- cfe/trunk/utils/analyzer/exploded-graph-rewriter.py (original)
+++ cfe/trunk/utils/analyzer/exploded-graph-rewriter.py Tue Aug 13 16:04:53 2019
@@ -914,6 +914,52 @@ class SinglePathTrimmer(object):
                        for node_id in visited_nodes}
 
 
+# TargetedTrimmer keeps paths that lead to specific nodes and discards all
+# other paths. Useful when you cannot use -trim-egraph (e.g. when debugging
+# a crash).
+class TargetedTrimmer(object):
+    def __init__(self, target_nodes):
+        super(TargetedTrimmer, self).__init__()
+        self._target_nodes = target_nodes
+
+    @staticmethod
+    def parse_target_node(node, graph):
+        if node.startswith('0x'):
+            ret = 'Node' + node
+            assert ret in graph.nodes
+            return ret
+        else:
+            for other_id in graph.nodes:
+                other = graph.nodes[other_id]
+                if other.node_id == int(node):
+                    return other_id
+
+    @staticmethod
+    def parse_target_nodes(target_nodes, graph):
+        return [TargetedTrimmer.parse_target_node(node, graph)
+                for node in target_nodes.split(',')]
+
+    def trim(self, graph):
+        queue = self._target_nodes
+        visited_nodes = set()
+
+        while len(queue) > 0:
+            node_id = queue.pop()
+            visited_nodes.add(node_id)
+            node = graph.nodes[node_id]
+            for pred_id in node.predecessors:
+                if pred_id not in visited_nodes:
+                    queue.append(pred_id)
+        graph.nodes = {node_id: graph.nodes[node_id]
+                       for node_id in visited_nodes}
+        for node_id in graph.nodes:
+            node = graph.nodes[node_id]
+            node.successors = [succ_id for succ_id in node.successors
+                               if succ_id in visited_nodes]
+            node.predecessors = [succ_id for succ_id in node.predecessors
+                                 if succ_id in visited_nodes]
+
+
 #===-----------------------------------------------------------------------===#
 # The entry point to the script.
 #===-----------------------------------------------------------------------===#
@@ -939,6 +985,11 @@ def main():
                         help='only display the leftmost path in the graph '
                              '(useful for trimmed graphs that still '
                              'branch too much)')
+    parser.add_argument('--to', type=str, default=None,
+                        help='only display execution paths from the root '
+                             'to the given comma-separated list of nodes '
+                             'identified by a pointer or a stable ID; '
+                             'compatible with --single-path')
     parser.add_argument('--dark', action='store_const', dest='dark',
                         const=True, default=False,
                         help='dark mode')
@@ -960,6 +1011,9 @@ def main():
             graph.add_raw_line(raw_line)
 
     trimmers = []
+    if args.to is not None:
+        trimmers.append(TargetedTrimmer(
+            TargetedTrimmer.parse_target_nodes(args.to, graph)))
     if args.single_path:
         trimmers.append(SinglePathTrimmer())
 




More information about the cfe-commits mailing list