This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] -fsave-optimization-record: add contrib/optrecord.py
- From: David Malcolm <dmalcolm at redhat dot com>
- To: Richard Biener <richard dot guenther at gmail dot com>, gcc-patches at gcc dot gnu dot org
- Cc: David Malcolm <dmalcolm at redhat dot com>
- Date: Fri, 20 Jul 2018 13:12:19 -0400
- Subject: [PATCH] -fsave-optimization-record: add contrib/optrecord.py
This patch adds a Python 3 module to "contrib" for reading the output of
-fsave-optimization-record.
It can be imported from other Python code, or run standalone as a script,
in which case it prints the saved messages in a form resembling GCC
diagnostics.
OK for trunk?
contrib/ChangeLog:
* optrecord.py: New file.
---
contrib/optrecord.py | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 295 insertions(+)
create mode 100755 contrib/optrecord.py
diff --git a/contrib/optrecord.py b/contrib/optrecord.py
new file mode 100755
index 0000000..b07488e
--- /dev/null
+++ b/contrib/optrecord.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python3
+#
+# Python module for working with the result of -fsave-optimization-record
+# Contributed by David Malcolm <dmalcolm@redhat.com>.
+#
+# Copyright (C) 2018 Free Software Foundation, Inc.
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 3, or (at your option) any later
+# version.
+#
+# GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>. */
+
+import argparse
+import json
+import os
+import sys
+
+class TranslationUnit:
+ """Top-level class for containing optimization records"""
+ @staticmethod
+ def from_filename(filename):
+ with open(filename) as f:
+ root_obj = json.load(f)
+ return TranslationUnit(filename, root_obj)
+
+ def __init__(self, filename, json_obj):
+ self.filename = filename
+ self.pass_by_id = {}
+
+ # Expect a 3-tuple
+ metadata, passes, records = json_obj
+
+ self.format = metadata['format']
+ self.generator = metadata['generator']
+ self.passes = [Pass(obj, self) for obj in passes]
+ self.records = [Record(obj, self) for obj in records]
+
+ def __repr__(self):
+ return ('TranslationUnit(%r, %r, %r, %r)'
+ % (self.filename, self.generator, self.passes, self.records))
+
+class Pass:
+ """An optimization pass"""
+ def __init__(self, json_obj, tu):
+ self.id_ = json_obj['id']
+ self.name = json_obj['name']
+ self.num = json_obj['num']
+ self.optgroups = set(json_obj['optgroups']) # list of strings
+ self.type = json_obj['type']
+ tu.pass_by_id[self.id_] = self
+ self.children = [Pass(child, tu)
+ for child in json_obj.get('children', [])]
+
+ def __repr__(self):
+ return ('Pass(%r, %r, %r, %r)'
+ % (self.name, self.num, self.optgroups, self.type))
+
+def from_optional_json_field(cls, jsonobj, field):
+ if field not in jsonobj:
+ return None
+ return cls(jsonobj[field])
+
+class ImplLocation:
+ """An implementation location (within the compiler itself)"""
+ def __init__(self, json_obj):
+ self.file = json_obj['file']
+ self.line = json_obj['line']
+ self.function = json_obj['function']
+
+ def __repr__(self):
+ return ('ImplLocation(%r, %r, %r)'
+ % (self.file, self.line, self.function))
+
+class Location:
+ """A source location"""
+ def __init__(self, json_obj):
+ self.file = json_obj['file']
+ self.line = json_obj['line']
+ self.column = json_obj['column']
+
+ def __str__(self):
+ return '%s:%i:%i' % (self.file, self.line, self.column)
+
+ def __repr__(self):
+ return ('Location(%r, %r, %r)'
+ % (self.file, self.line, self.column))
+
+class Count:
+ """An execution count"""
+ def __init__(self, json_obj):
+ self.quality = json_obj['quality']
+ self.value = json_obj['value']
+
+ def __repr__(self):
+ return ('Count(%r, %r)'
+ % (self.quality, self.value))
+
+ def is_precise(self):
+ return self.quality in ('precise', 'adjusted')
+
+class Record:
+ """A optimization record: success/failure/note"""
+ def __init__(self, json_obj, tu):
+ self.kind = json_obj['kind']
+ if 'pass' in json_obj:
+ self.pass_ = tu.pass_by_id[json_obj['pass']]
+ else:
+ self.pass_ = None
+ self.function = json_obj.get('function', None)
+ self.impl_location = from_optional_json_field(ImplLocation, json_obj,
+ 'impl_location')
+ self.message = [Item.from_json(obj) for obj in json_obj['message']]
+ self.count = from_optional_json_field(Count, json_obj, 'count')
+ self.location = from_optional_json_field(Location, json_obj, 'location')
+ if 'inlining_chain' in json_obj:
+ self.inlining_chain = [InliningNode(obj)
+ for obj in json_obj['inlining_chain']]
+ else:
+ self.inlining_chain = None
+ self.children = [Record(child, tu)
+ for child in json_obj.get('children', [])]
+
+ def __repr__(self):
+ return ('Record(%r, %r, %r, %r, %r)'
+ % (self.kind, self.message, self.pass_, self.function,
+ self.children))
+
+class InliningNode:
+ """A node within an inlining chain"""
+ def __init__(self, json_obj):
+ self.fndecl = json_obj['fndecl']
+ self.site = from_optional_json_field(Location, json_obj, 'site')
+
+class Item:
+ """Base class for non-string items within a message"""
+ @staticmethod
+ def from_json(json_obj):
+ if isinstance(json_obj, str):
+ return json_obj
+ if 'expr' in json_obj:
+ return Expr(json_obj)
+ elif 'stmt' in json_obj:
+ return Stmt(json_obj)
+ elif 'symtab_node' in json_obj:
+ return SymtabNode(json_obj)
+ else:
+ raise ValueError('unrecognized item: %r' % json_obj)
+
+class Expr(Item):
+ """An expression within a message"""
+ def __init__(self, json_obj):
+ self.expr = json_obj['expr']
+ self.location = from_optional_json_field(Location, json_obj, 'location')
+
+ def __str__(self):
+ return self.expr
+
+ def __repr__(self):
+ return 'Expr(%r)' % self.expr
+
+class Stmt(Item):
+ """A statement within a message"""
+ def __init__(self, json_obj):
+ self.stmt = json_obj['stmt']
+ self.location = from_optional_json_field(Location, json_obj, 'location')
+
+ def __str__(self):
+ return self.stmt
+
+ def __repr__(self):
+ return 'Stmt(%r)' % self.stmt
+
+class SymtabNode(Item):
+ """A symbol table node within a message"""
+ def __init__(self, json_obj):
+ self.node = json_obj['symtab_node']
+ self.location = from_optional_json_field(Location, json_obj, 'location')
+
+ def __str__(self):
+ return self.node
+
+ def __repr__(self):
+ return 'SymtabNode(%r)' % self.node
+
+############################################################################
+
+SGR_START = "\33["
+SGR_END = "m\33[K"
+def SGR_SEQ(text):
+ return SGR_START + text + SGR_END
+SGR_RESET = SGR_SEQ("")
+
+COLOR_SEPARATOR = ";"
+COLOR_BOLD = "01"
+COLOR_FG_RED = "31"
+COLOR_FG_GREEN = "32"
+COLOR_FG_CYAN = "36"
+
+class Printer:
+ def __init__(self, colorize):
+ self.colorize = colorize
+
+ def print_to_str(self, record, indent=0):
+ msg = ''
+ loc = record.location
+ if loc:
+ msg += self.bold('%s: ' % loc)
+ msg += self.color_for_kind('%s: ' % record.kind, record.kind)
+ msg += ' ' * indent
+ for item in record.message:
+ if isinstance(item, str):
+ msg += item
+ elif isinstance(item, (Expr, Stmt, SymtabNode)):
+ msg += "'" + self.bold(str(item)) + "'"
+ else:
+ raise TypeError('unknown message item: %r' % item)
+ # Strip trailing whitespace (including newlines)
+ msg = msg.rstrip()
+ if record.pass_:
+ msg += ' [%s]' % self.bold('pass=%s' % record.pass_.name)
+ if record.count:
+ msg += (' [%s]'
+ % self.bold('count(%s)=%i'
+ % (record.count.quality,
+ record.count.value)))
+ return msg
+
+ def print_record(self, out, record, indent=0):
+ msg = self.print_to_str(record, indent)
+ out.write('%s\n' % msg)
+ for child in record.children:
+ self.print_record(out, child, indent + 1)
+
+ def with_color(self, color, text):
+ if self.colorize:
+ return SGR_SEQ(color) + text + SGR_RESET
+ else:
+ return text
+
+ def bold(self, text):
+ return self.with_color(COLOR_BOLD, text)
+
+ def bold_green(self, text):
+ return self.with_color(COLOR_FG_GREEN + COLOR_SEPARATOR + COLOR_BOLD,
+ text)
+
+ def bold_red(self, text):
+ return self.with_color(COLOR_FG_RED + COLOR_SEPARATOR + COLOR_BOLD,
+ text)
+
+ def bold_cyan(self, text):
+ return self.with_color(COLOR_FG_CYAN + COLOR_SEPARATOR + COLOR_BOLD,
+ text)
+
+ def color_for_kind(self, text, kind):
+ if kind == 'success':
+ return self.bold_green(text)
+ elif kind == 'failure':
+ return self.bold_red(text)
+ else:
+ return self.bold_cyan(text)
+
+def should_colorize(stream):
+ return os.environ['TERM'] != 'dumb' and os.isatty(stream.fileno())
+
+############################################################################
+
+def main():
+ """
+ If run as a script, read one or more files, and print them to stdout in
+ a format similar to GCC diagnostics.
+ """
+ parser = argparse.ArgumentParser(
+ description="Print the results of GCC's -fsave-optimization-record.")
+ parser.add_argument('filenames', metavar='FILENAME', type=str, nargs='+',
+ help='the name of the file(s) to be printed')
+ args = parser.parse_args()
+ p = Printer(should_colorize(sys.stdout))
+ for filename in args.filenames:
+ tu = TranslationUnit.from_filename(filename)
+ for r in tu.records:
+ p.print_record(sys.stdout, r)
+
+if __name__ == '__main__':
+ main()
--
1.8.5.3