diff options
author | Artem Dergachev <artem.dergachev@gmail.com> | 2019-08-13 23:04:53 +0000 |
---|---|---|
committer | Artem Dergachev <artem.dergachev@gmail.com> | 2019-08-13 23:04:53 +0000 |
commit | 9289681ea3d000e7d46b2c69bac26b45f960c4dd (patch) | |
tree | dd7e646d999966deb91899ba605652b66fe604c6 /clang/utils/analyzer/exploded-graph-rewriter.py | |
parent | 0b26891f3f2b75dcc8f1a0f3a34342ce27570a2c (diff) | |
download | bcm5719-llvm-9289681ea3d000e7d46b2c69bac26b45f960c4dd.tar.gz bcm5719-llvm-9289681ea3d000e7d46b2c69bac26b45f960c4dd.zip |
[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
llvm-svn: 368768
Diffstat (limited to 'clang/utils/analyzer/exploded-graph-rewriter.py')
-rwxr-xr-x | clang/utils/analyzer/exploded-graph-rewriter.py | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/clang/utils/analyzer/exploded-graph-rewriter.py b/clang/utils/analyzer/exploded-graph-rewriter.py index 3fa8ba0c9a2..d612cfc3ba7 100755 --- a/clang/utils/analyzer/exploded-graph-rewriter.py +++ b/clang/utils/analyzer/exploded-graph-rewriter.py @@ -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()) |