]> granicus.if.org Git - clang/commitdiff
[analyzer] exploded-graph-rewriter: Open the converted graph immediately.
authorArtem Dergachev <artem.dergachev@gmail.com>
Tue, 13 Aug 2019 23:04:47 +0000 (23:04 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Tue, 13 Aug 2019 23:04:47 +0000 (23:04 +0000)
Change the default behavior: the tool no longer dumps the rewritten .dot file
to stdout, but instead it automatically converts it into an .html file
(which essentially wraps an .svg file) and immediately opens it with
the default web browser.

This means that the tool should now be fairly easy to use:

  $ exploded-graph-rewriter.py /tmp/ExprEngine.dot

The benefits of wrapping the .svg file into an .html file are:

    - It'll open in a web browser, which is the intended behavior.
      An .svg file would be open with an image viewer/editor instead.
    - It avoids the white background around the otherwise dark svg area
      in dark mode.

The feature can be turned off by passing a flag '--rewrite-only'.
The LIT substitution is updated to enforce the old mode because
we don't want web browsers opening on our buildbots.

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@368766 91177308-0d34-0410-b5e6-96231b3b80d8

test/Analysis/exploded-graph-rewriter/lit.local.cfg
utils/analyzer/exploded-graph-rewriter.py

index dfeb0a86c4f3c1357c81ce39201d4c35c0616d5b..87ce52cc53e0d1878917635262e562e9613d6d74 100644 (file)
@@ -8,7 +8,7 @@ use_lit_shell = os.environ.get("LIT_USE_INTERNAL_SHELL")
 config.test_format = lit.formats.ShTest(use_lit_shell == "0")
 
 config.substitutions.append(('%exploded_graph_rewriter',
-                             '\'%s\' %s' % (
+                             '\'%s\' %s --dump-dot-only' % (
                                  config.python_executable,
                                  lit.util.which('exploded-graph-rewriter.py',
                                                 os.path.join(
index 5ce56d61c0dcff1b44b267686c2a216b1a7e037e..eee09f37bd285d03b20773088f02fba1f13d6a4a 100755 (executable)
@@ -394,16 +394,25 @@ class ExplodedGraph(object):
 # A visitor that dumps the ExplodedGraph into a DOT file with fancy HTML-based
 # syntax highlighing.
 class DotDumpVisitor(object):
-    def __init__(self, do_diffs, dark_mode, gray_mode, topo_mode):
+    def __init__(self, do_diffs, dark_mode, gray_mode,
+                 topo_mode, dump_dot_only):
         super(DotDumpVisitor, self).__init__()
         self._do_diffs = do_diffs
         self._dark_mode = dark_mode
         self._gray_mode = gray_mode
         self._topo_mode = topo_mode
+        self._dump_dot_only = dump_dot_only
+        self._output = []
 
-    @staticmethod
-    def _dump_raw(s):
-        print(s, end='')
+    def _dump_raw(self, s):
+        if self._dump_dot_only:
+            print(s, end='')
+        else:
+            self._output.append(s)
+
+    def output(self):
+        assert not self._dump_dot_only
+        return ''.join(self._output)
 
     def _dump(self, s):
         s = s.replace('&', '&amp;') \
@@ -812,6 +821,44 @@ class DotDumpVisitor(object):
     def visit_end_of_graph(self):
         self._dump_raw('}\n')
 
+        if not self._dump_dot_only:
+            import sys
+            import tempfile
+
+            def write_temp_file(suffix, data):
+                fd, filename = tempfile.mkstemp(suffix=suffix)
+                print('Writing "%s"...' % filename)
+                with os.fdopen(fd, 'w') as fp:
+                    fp.write(data)
+                print('Done! Please remember to remove the file.')
+                return filename
+
+            try:
+                import graphviz
+            except ImportError:
+                # The fallback behavior if graphviz is not installed!
+                print('Python graphviz not found. Please invoke')
+                print('  $ pip install graphviz')
+                print('in order to enable automatic conversion to HTML.')
+                print()
+                print('You may also convert DOT to SVG manually via')
+                print('  $ dot -Tsvg input.dot -o output.svg')
+                print()
+                write_temp_file('.dot', self.output())
+                return
+
+            svg = graphviz.pipe('dot', 'svg', self.output())
+
+            filename = write_temp_file(
+                '.html', '<html><body bgcolor="%s">%s</body></html>' % (
+                             '#1a1a1a' if self._dark_mode else 'white', svg))
+            if sys.platform == 'win32':
+                os.startfile(filename)
+            elif sys.platform == 'darwin':
+                os.system('open "%s"' % filename)
+            else:
+                os.system('xdg-open "%s"' % filename)
+
 
 #===-----------------------------------------------------------------------===#
 # Explorers know how to traverse the ExplodedGraph in a certain order.
@@ -874,8 +921,10 @@ class SinglePathExplorer(object):
 
 
 def main():
-    parser = argparse.ArgumentParser()
-    parser.add_argument('filename', type=str)
+    parser = argparse.ArgumentParser(
+        description='Display and manipulate Exploded Graph dumps.')
+    parser.add_argument('filename', type=str,
+                        help='the .dot file produced by the Static Analyzer')
     parser.add_argument('-v', '--verbose', action='store_const',
                         dest='loglevel', const=logging.DEBUG,
                         default=logging.WARNING,
@@ -897,6 +946,11 @@ def main():
     parser.add_argument('--gray', action='store_const', dest='gray',
                         const=True, default=False,
                         help='black-and-white mode')
+    parser.add_argument('--dump-dot-only', action='store_const',
+                        dest='dump_dot_only', const=True, default=False,
+                        help='instead of writing an HTML file and immediately '
+                             'displaying it, dump the rewritten dot file '
+                             'to stdout')
     args = parser.parse_args()
     logging.basicConfig(level=args.loglevel)
 
@@ -907,7 +961,8 @@ def main():
             graph.add_raw_line(raw_line)
 
     explorer = SinglePathExplorer() if args.single_path else BasicExplorer()
-    visitor = DotDumpVisitor(args.diff, args.dark, args.gray, args.topology)
+    visitor = DotDumpVisitor(args.diff, args.dark, args.gray, args.topology,
+                             args.dump_dot_only)
 
     explorer.explore(graph, visitor)