--- /dev/null
+// RUN: %exploded_graph_rewriter %s \
+// RUN: | FileCheck %s -check-prefixes=CHECK,BASIC
+// RUN: %exploded_graph_rewriter -s %s \
+// RUN: | FileCheck %s -check-prefixes=CHECK,SINGLE
+
+// FIXME: Substitution doesn't seem to work on Windows.
+// UNSUPPORTED: system-windows
+
+Node0x1 [shape=record,label=
+ "{{ "node_id": 1, "pointer": "0x1", "has_report": false, "is_sink": false,
+ "program_state": null, "program_points": []}\l}"];
+
+Node0x2 [shape=record,label=
+ "{{ "node_id": 2, "pointer": "0x2", "has_report": false, "is_sink": false,
+ "program_state": null, "program_points": []}\l}"];
+
+Node0x3 [shape=record,label=
+ "{{ "node_id": 3, "pointer": "0x3", "has_report": false, "is_sink": false,
+ "program_state": null, "program_points": []}\l}"];
+
+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;
#===-----------------------------------------------------------------------===#
-# A class that encapsulates traversal of the ExplodedGraph. Different explorer
-# kinds could potentially traverse specific sub-graphs.
+# BasicExplorer explores the whole graph in no particular order.
class BasicExplorer(object):
def __init__(self):
super(BasicExplorer, self).__init__()
visitor.visit_end_of_graph()
+# SinglePathExplorer traverses only a single path - the leftmost path
+# from the root. Useful when the trimmed graph is still too large
+# due to a large amount of equivalent reports.
+class SinglePathExplorer(object):
+ def __init__(self):
+ super(SinglePathExplorer, self).__init__()
+
+ def explore(self, graph, visitor):
+ visitor.visit_begin_graph(graph)
+
+ # Keep track of visited nodes in order to avoid loops.
+ visited = set()
+ node_id = graph.root_id
+ while True:
+ visited.add(node_id)
+ node = graph.nodes[node_id]
+ logging.debug('Visiting ' + node_id)
+ visitor.visit_node(node)
+ if len(node.successors) == 0:
+ break
+
+ succ_id = node.successors[0]
+ succ = graph.nodes[succ_id]
+ logging.debug('Visiting edge: %s -> %s ' % (node_id, succ_id))
+ visitor.visit_edge(node, succ)
+ if succ_id in visited:
+ break
+
+ node_id = succ_id
+
+ visitor.visit_end_of_graph()
+
+
#===-----------------------------------------------------------------------===#
# The entry point to the script.
#===-----------------------------------------------------------------------===#
parser.add_argument('-d', '--diff', action='store_const', dest='diff',
const=True, default=False,
help='display differences between states')
+ parser.add_argument('-s', '--single-path', action='store_const',
+ dest='single_path', const=True, default=False,
+ help='only display the leftmost path in the graph '
+ '(useful for trimmed graphs that still '
+ 'branch too much)')
parser.add_argument('--dark', action='store_const', dest='dark',
const=True, default=False,
help='dark mode')
raw_line = raw_line.strip()
graph.add_raw_line(raw_line)
- explorer = BasicExplorer()
+ explorer = SinglePathExplorer() if args.single_path else BasicExplorer()
visitor = DotDumpVisitor(args.diff, args.dark, args.gray)
+
explorer.explore(graph, visitor)