Imported Upstream version 5.18.0.167

Former-commit-id: 289509151e0fee68a1b591a20c9f109c3c789d3a
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-10-20 08:25:10 +00:00
parent e19d552987
commit b084638f15
28489 changed files with 184 additions and 3866856 deletions

View File

@ -1,13 +0,0 @@
set (files
"opt-diff.py"
"opt-stats.py"
"opt-viewer.py"
"optpmap.py"
"optrecord.py"
"style.css")
foreach (file ${files})
install(PROGRAMS ${file}
DESTINATION share/opt-viewer
COMPONENT opt-viewer)
endforeach (file)

View File

@ -1,69 +0,0 @@
#!/usr/bin/env python2.7
from __future__ import print_function
desc = '''Generate the difference of two YAML files into a new YAML file (works on
pair of directories too). A new attribute 'Added' is set to True or False
depending whether the entry is added or removed from the first input to the
next.
The tools requires PyYAML.'''
import yaml
# Try to use the C parser.
try:
from yaml import CLoader as Loader
except ImportError:
from yaml import Loader
import optrecord
import argparse
from collections import defaultdict
from multiprocessing import cpu_count, Pool
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=desc)
parser.add_argument(
'yaml_dir_or_file_1',
help='An optimization record file or a directory searched for optimization '
'record files that are used as the old version for the comparison')
parser.add_argument(
'yaml_dir_or_file_2',
help='An optimization record file or a directory searched for optimization '
'record files that are used as the new version for the comparison')
parser.add_argument(
'--jobs',
'-j',
default=cpu_count(),
type=int,
help='Max job count (defaults to %(default)s, the current CPU count)')
parser.add_argument(
'--no-progress-indicator',
'-n',
action='store_true',
default=False,
help='Do not display any indicator of how many YAML files were read.')
parser.add_argument('--output', '-o', default='diff.opt.yaml')
args = parser.parse_args()
files1 = optrecord.find_opt_files(args.yaml_dir_or_file_1)
files2 = optrecord.find_opt_files(args.yaml_dir_or_file_2)
print_progress = not args.no_progress_indicator
all_remarks1, _, _ = optrecord.gather_results(files1, args.jobs, print_progress)
all_remarks2, _, _ = optrecord.gather_results(files2, args.jobs, print_progress)
added = set(all_remarks2.values()) - set(all_remarks1.values())
removed = set(all_remarks1.values()) - set(all_remarks2.values())
for r in added:
r.Added = True
for r in removed:
r.Added = False
result = added | removed
for r in result:
r.recover_yaml_structure()
with open(args.output, 'w') as stream:
yaml.dump_all(result, stream)

View File

@ -1,78 +0,0 @@
#!/usr/bin/env python2.7
from __future__ import print_function
desc = '''Generate statistics about optimization records from the YAML files
generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
The tools requires PyYAML and Pygments Python packages.'''
import optrecord
import argparse
import operator
from collections import defaultdict
from multiprocessing import cpu_count, Pool
try:
from guppy import hpy
hp = hpy()
except ImportError:
print("Memory consumption not shown because guppy is not installed")
hp = None
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=desc)
parser.add_argument(
'yaml_dirs_or_files',
nargs='+',
help='List of optimization record files or directories searched '
'for optimization record files.')
parser.add_argument(
'--jobs',
'-j',
default=cpu_count(),
type=int,
help='Max job count (defaults to %(default)s, the current CPU count)')
parser.add_argument(
'--no-progress-indicator',
'-n',
action='store_true',
default=False,
help='Do not display any indicator of how many YAML files were read.')
args = parser.parse_args()
print_progress = not args.no_progress_indicator
files = optrecord.find_opt_files(*args.yaml_dirs_or_files)
if not files:
parser.error("No *.opt.yaml files found")
sys.exit(1)
all_remarks, file_remarks, _ = optrecord.gather_results(
files, args.jobs, print_progress)
if print_progress:
print('\n')
bypass = defaultdict(int)
byname = defaultdict(int)
for r in optrecord.itervalues(all_remarks):
bypass[r.Pass] += 1
byname[r.Pass + "/" + r.Name] += 1
total = len(all_remarks)
print("{:24s} {:10d}".format("Total number of remarks", total))
if hp:
h = hp.heap()
print("{:24s} {:10d}".format("Memory per remark",
h.size / len(all_remarks)))
print('\n')
print("Top 10 remarks by pass:")
for (passname, count) in sorted(bypass.items(), key=operator.itemgetter(1),
reverse=True)[:10]:
print(" {:30s} {:2.0f}%". format(passname, count * 100. / total))
print("\nTop 10 remarks:")
for (name, count) in sorted(byname.items(), key=operator.itemgetter(1),
reverse=True)[:10]:
print(" {:30s} {:2.0f}%". format(name, count * 100. / total))

View File

@ -1,326 +0,0 @@
#!/usr/bin/env python2.7
from __future__ import print_function
import argparse
import cgi
import codecs
import errno
import functools
from multiprocessing import cpu_count
import os.path
import re
import shutil
import sys
from pygments import highlight
from pygments.lexers.c_cpp import CppLexer
from pygments.formatters import HtmlFormatter
import optpmap
import optrecord
desc = '''Generate HTML output to visualize optimization records from the YAML files
generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
The tools requires PyYAML and Pygments Python packages.'''
# This allows passing the global context to the child processes.
class Context:
def __init__(self, caller_loc = dict()):
# Map function names to their source location for function where inlining happened
self.caller_loc = caller_loc
context = Context()
def suppress(remark):
if remark.Name == 'sil.Specialized':
return remark.getArgDict()['Function'][0].startswith('\"Swift.')
elif remark.Name == 'sil.Inlined':
return remark.getArgDict()['Callee'][0].startswith(('\"Swift.', '\"specialized Swift.'))
return False
class SourceFileRenderer:
def __init__(self, source_dir, output_dir, filename):
existing_filename = None
if os.path.exists(filename):
existing_filename = filename
else:
fn = os.path.join(source_dir, filename)
if os.path.exists(fn):
existing_filename = fn
self.stream = codecs.open(os.path.join(output_dir, optrecord.html_file_name(filename)), 'w', encoding='utf-8')
if existing_filename:
self.source_stream = open(existing_filename)
else:
self.source_stream = None
print('''
<html>
<h1>Unable to locate file {}</h1>
</html>
'''.format(filename), file=self.stream)
self.html_formatter = HtmlFormatter(encoding='utf-8')
self.cpp_lexer = CppLexer(stripnl=False)
def render_source_lines(self, stream, line_remarks):
file_text = stream.read()
if args.no_highlight:
html_highlighted = file_text.decode('utf-8')
else:
html_highlighted = highlight(
file_text,
self.cpp_lexer,
self.html_formatter)
# Note that the API is different between Python 2 and 3. On
# Python 3, pygments.highlight() returns a bytes object, so we
# have to decode. On Python 2, the output is str but since we
# support unicode characters and the output streams is unicode we
# decode too.
html_highlighted = html_highlighted.decode('utf-8')
# Take off the header and footer, these must be
# reapplied line-wise, within the page structure
html_highlighted = html_highlighted.replace('<div class="highlight"><pre>', '')
html_highlighted = html_highlighted.replace('</pre></div>', '')
for (linenum, html_line) in enumerate(html_highlighted.split('\n'), start=1):
print(u'''
<tr>
<td><a name=\"L{linenum}\">{linenum}</a></td>
<td></td>
<td></td>
<td><div class="highlight"><pre>{html_line}</pre></div></td>
</tr>'''.format(**locals()), file=self.stream)
for remark in line_remarks.get(linenum, []):
if not suppress(remark):
self.render_inline_remarks(remark, html_line)
def render_inline_remarks(self, r, line):
inlining_context = r.DemangledFunctionName
dl = context.caller_loc.get(r.Function)
if dl:
dl_dict = dict(list(dl))
link = optrecord.make_link(dl_dict['File'], dl_dict['Line'] - 2)
inlining_context = "<a href={link}>{r.DemangledFunctionName}</a>".format(**locals())
# Column is the number of characters *including* tabs, keep those and
# replace everything else with spaces.
indent = line[:max(r.Column, 1) - 1]
indent = re.sub('\S', ' ', indent)
print(u'''
<tr>
<td></td>
<td>{r.RelativeHotness}</td>
<td class=\"column-entry-{r.color}\">{r.PassWithDiffPrefix}</td>
<td><pre style="display:inline">{indent}</pre><span class=\"column-entry-yellow\"> {r.message}&nbsp;</span></td>
<td class=\"column-entry-yellow\">{inlining_context}</td>
</tr>'''.format(**locals()), file=self.stream)
def render(self, line_remarks):
if not self.source_stream:
return
print('''
<html>
<meta charset="utf-8" />
<head>
<link rel='stylesheet' type='text/css' href='style.css'>
</head>
<body>
<div class="centered">
<table class="source">
<thead>
<tr>
<th style="width: 2%">Line</td>
<th style="width: 3%">Hotness</td>
<th style="width: 10%">Optimization</td>
<th style="width: 70%">Source</td>
<th style="width: 15%">Inline Context</td>
</tr>
</thead>
<tbody>''', file=self.stream)
self.render_source_lines(self.source_stream, line_remarks)
print('''
</tbody>
</table>
</body>
</html>''', file=self.stream)
class IndexRenderer:
def __init__(self, output_dir, should_display_hotness):
self.stream = codecs.open(os.path.join(output_dir, 'index.html'), 'w', encoding='utf-8')
self.should_display_hotness = should_display_hotness
def render_entry(self, r, odd):
escaped_name = cgi.escape(r.DemangledFunctionName)
print(u'''
<tr>
<td class=\"column-entry-{odd}\"><a href={r.Link}>{r.DebugLocString}</a></td>
<td class=\"column-entry-{odd}\">{r.RelativeHotness}</td>
<td class=\"column-entry-{odd}\">{escaped_name}</td>
<td class=\"column-entry-{r.color}\">{r.PassWithDiffPrefix}</td>
</tr>'''.format(**locals()), file=self.stream)
def render(self, all_remarks):
print('''
<html>
<meta charset="utf-8" />
<head>
<link rel='stylesheet' type='text/css' href='style.css'>
</head>
<body>
<div class="centered">
<table>
<tr>
<td>Source Location</td>
<td>Hotness</td>
<td>Function</td>
<td>Pass</td>
</tr>''', file=self.stream)
max_entries = None
if should_display_hotness:
max_entries = args.max_hottest_remarks_on_index
for i, remark in enumerate(all_remarks[:max_entries]):
if not suppress(remark):
self.render_entry(remark, i % 2)
print('''
</table>
</body>
</html>''', file=self.stream)
def _render_file(source_dir, output_dir, ctx, entry):
global context
context = ctx
filename, remarks = entry
SourceFileRenderer(source_dir, output_dir, filename).render(remarks)
def map_remarks(all_remarks):
# Set up a map between function names and their source location for
# function where inlining happened
for remark in optrecord.itervalues(all_remarks):
if isinstance(remark, optrecord.Passed) and remark.Pass == "inline" and remark.Name == "Inlined":
for arg in remark.Args:
arg_dict = dict(list(arg))
caller = arg_dict.get('Caller')
if caller:
try:
context.caller_loc[caller] = arg_dict['DebugLoc']
except KeyError:
pass
def generate_report(all_remarks,
file_remarks,
source_dir,
output_dir,
should_display_hotness,
num_jobs,
should_print_progress):
try:
os.makedirs(output_dir)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(output_dir):
pass
else:
raise
_render_file_bound = functools.partial(_render_file, source_dir, output_dir, context)
if should_print_progress:
print('Rendering HTML files...')
optpmap.pmap(_render_file_bound,
file_remarks.items(),
num_jobs,
should_print_progress)
if should_display_hotness:
sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.Hotness, r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function), reverse=True)
else:
sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function))
IndexRenderer(args.output_dir, should_display_hotness).render(sorted_remarks)
shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)),
"style.css"), output_dir)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=desc)
parser.add_argument(
'yaml_dirs_or_files',
nargs='+',
help='List of optimization record files or directories searched '
'for optimization record files.')
parser.add_argument(
'--output-dir',
'-o',
default='html',
help='Path to a directory where generated HTML files will be output. '
'If the directory does not already exist, it will be created. '
'"%(default)s" by default.')
parser.add_argument(
'--jobs',
'-j',
default=cpu_count(),
type=int,
help='Max job count (defaults to %(default)s, the current CPU count)')
parser.add_argument(
'--source-dir',
'-s',
default='',
help='set source directory')
parser.add_argument(
'--no-progress-indicator',
'-n',
action='store_true',
default=False,
help='Do not display any indicator of how many YAML files were read '
'or rendered into HTML.')
parser.add_argument(
'--max-hottest-remarks-on-index',
default=1000,
type=int,
help='Maximum number of the hottest remarks to appear on the index page')
parser.add_argument(
'--no-highlight',
action='store_true',
default=False,
help='Do not use a syntax highlighter when rendering the source code')
parser.add_argument(
'--demangler',
help='Set the demangler to be used (defaults to %s)' % optrecord.Remark.default_demangler)
args = parser.parse_args()
print_progress = not args.no_progress_indicator
if args.demangler:
optrecord.Remark.set_demangler(args.demangler)
files = optrecord.find_opt_files(*args.yaml_dirs_or_files)
if not files:
parser.error("No *.opt.yaml files found")
sys.exit(1)
all_remarks, file_remarks, should_display_hotness = \
optrecord.gather_results(files, args.jobs, print_progress)
map_remarks(all_remarks)
generate_report(all_remarks,
file_remarks,
args.source_dir,
args.output_dir,
should_display_hotness,
args.jobs,
print_progress)

View File

@ -1,54 +0,0 @@
import sys
import multiprocessing
_current = None
_total = None
def _init(current, total):
global _current
global _total
_current = current
_total = total
def _wrapped_func(func_and_args):
func, argument, should_print_progress = func_and_args
if should_print_progress:
with _current.get_lock():
_current.value += 1
sys.stdout.write('\r\t{} of {}'.format(_current.value, _total.value))
sys.stdout.flush()
return func(argument)
def pmap(func, iterable, processes, should_print_progress, *args, **kwargs):
"""
A parallel map function that reports on its progress.
Applies `func` to every item of `iterable` and return a list of the
results. If `processes` is greater than one, a process pool is used to run
the functions in parallel. `should_print_progress` is a boolean value that
indicates whether a string 'N of M' should be printed to indicate how many
of the functions have finished being run.
"""
global _current
global _total
_current = multiprocessing.Value('i', 0)
_total = multiprocessing.Value('i', len(iterable))
func_and_args = [(func, arg, should_print_progress,) for arg in iterable]
if processes <= 1:
result = map(_wrapped_func, func_and_args, *args, **kwargs)
else:
pool = multiprocessing.Pool(initializer=_init,
initargs=(_current, _total,),
processes=processes)
result = pool.map(_wrapped_func, func_and_args, *args, **kwargs)
if should_print_progress:
sys.stdout.write('\r')
return result

View File

@ -1,333 +0,0 @@
#!/usr/bin/env python2.7
from __future__ import print_function
import yaml
# Try to use the C parser.
try:
from yaml import CLoader as Loader
except ImportError:
print("For faster parsing, you may want to install libYAML for PyYAML")
from yaml import Loader
import cgi
from collections import defaultdict
import fnmatch
import functools
from multiprocessing import Lock
import os, os.path
import subprocess
try:
# The previously builtin function `intern()` was moved
# to the `sys` module in Python 3.
from sys import intern
except:
pass
import optpmap
try:
dict.iteritems
except AttributeError:
# Python 3
def itervalues(d):
return iter(d.values())
def iteritems(d):
return iter(d.items())
else:
# Python 2
def itervalues(d):
return d.itervalues()
def iteritems(d):
return d.iteritems()
def html_file_name(filename):
return filename.replace('/', '_').replace('#', '_') + ".html"
def make_link(File, Line):
return "\"{}#L{}\"".format(html_file_name(File), Line)
class Remark(yaml.YAMLObject):
# Work-around for http://pyyaml.org/ticket/154.
yaml_loader = Loader
default_demangler = 'c++filt -n'
demangler_proc = None
@classmethod
def set_demangler(cls, demangler):
cls.demangler_proc = subprocess.Popen(demangler.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
cls.demangler_lock = Lock()
@classmethod
def demangle(cls, name):
with cls.demangler_lock:
cls.demangler_proc.stdin.write((name + '\n').encode('utf-8'))
cls.demangler_proc.stdin.flush()
return cls.demangler_proc.stdout.readline().rstrip().decode('utf-8')
# Intern all strings since we have lot of duplication across filenames,
# remark text.
#
# Change Args from a list of dicts to a tuple of tuples. This saves
# memory in two ways. One, a small tuple is significantly smaller than a
# small dict. Two, using tuple instead of list allows Args to be directly
# used as part of the key (in Python only immutable types are hashable).
def _reduce_memory(self):
self.Pass = intern(self.Pass)
self.Name = intern(self.Name)
try:
# Can't intern unicode strings.
self.Function = intern(self.Function)
except:
pass
def _reduce_memory_dict(old_dict):
new_dict = dict()
for (k, v) in iteritems(old_dict):
if type(k) is str:
k = intern(k)
if type(v) is str:
v = intern(v)
elif type(v) is dict:
# This handles [{'Caller': ..., 'DebugLoc': { 'File': ... }}]
v = _reduce_memory_dict(v)
new_dict[k] = v
return tuple(new_dict.items())
self.Args = tuple([_reduce_memory_dict(arg_dict) for arg_dict in self.Args])
# The inverse operation of the dictonary-related memory optimization in
# _reduce_memory_dict. E.g.
# (('DebugLoc', (('File', ...) ... ))) -> [{'DebugLoc': {'File': ...} ....}]
def recover_yaml_structure(self):
def tuple_to_dict(t):
d = dict()
for (k, v) in t:
if type(v) is tuple:
v = tuple_to_dict(v)
d[k] = v
return d
self.Args = [tuple_to_dict(arg_tuple) for arg_tuple in self.Args]
def canonicalize(self):
if not hasattr(self, 'Hotness'):
self.Hotness = 0
if not hasattr(self, 'Args'):
self.Args = []
self._reduce_memory()
@property
def File(self):
return self.DebugLoc['File']
@property
def Line(self):
return int(self.DebugLoc['Line'])
@property
def Column(self):
return self.DebugLoc['Column']
@property
def DebugLocString(self):
return "{}:{}:{}".format(self.File, self.Line, self.Column)
@property
def DemangledFunctionName(self):
return self.demangle(self.Function)
@property
def Link(self):
return make_link(self.File, self.Line)
def getArgString(self, mapping):
mapping = dict(list(mapping))
dl = mapping.get('DebugLoc')
if dl:
del mapping['DebugLoc']
assert(len(mapping) == 1)
(key, value) = list(mapping.items())[0]
if key == 'Caller' or key == 'Callee' or key == 'DirectCallee':
value = cgi.escape(self.demangle(value))
if dl and key != 'Caller':
dl_dict = dict(list(dl))
return u"<a href={}>{}</a>".format(
make_link(dl_dict['File'], dl_dict['Line']), value)
else:
return value
# Return a cached dictionary for the arguments. The key for each entry is
# the argument key (e.g. 'Callee' for inlining remarks. The value is a
# list containing the value (e.g. for 'Callee' the function) and
# optionally a DebugLoc.
def getArgDict(self):
if hasattr(self, 'ArgDict'):
return self.ArgDict
self.ArgDict = {}
for arg in self.Args:
if len(arg) == 2:
if arg[0][0] == 'DebugLoc':
dbgidx = 0
else:
assert(arg[1][0] == 'DebugLoc')
dbgidx = 1
key = arg[1 - dbgidx][0]
entry = (arg[1 - dbgidx][1], arg[dbgidx][1])
else:
arg = arg[0]
key = arg[0]
entry = (arg[1], )
self.ArgDict[key] = entry
return self.ArgDict
def getDiffPrefix(self):
if hasattr(self, 'Added'):
if self.Added:
return '+'
else:
return '-'
return ''
@property
def PassWithDiffPrefix(self):
return self.getDiffPrefix() + self.Pass
@property
def message(self):
# Args is a list of mappings (dictionaries)
values = [self.getArgString(mapping) for mapping in self.Args]
return "".join(values)
@property
def RelativeHotness(self):
if self.max_hotness:
return "{0:.2f}%".format(self.Hotness * 100. / self.max_hotness)
else:
return ''
@property
def key(self):
return (self.__class__, self.PassWithDiffPrefix, self.Name, self.File,
self.Line, self.Column, self.Function, self.Args)
def __hash__(self):
return hash(self.key)
def __eq__(self, other):
return self.key == other.key
def __repr__(self):
return str(self.key)
class Analysis(Remark):
yaml_tag = '!Analysis'
@property
def color(self):
return "white"
class AnalysisFPCommute(Analysis):
yaml_tag = '!AnalysisFPCommute'
class AnalysisAliasing(Analysis):
yaml_tag = '!AnalysisAliasing'
class Passed(Remark):
yaml_tag = '!Passed'
@property
def color(self):
return "green"
class Missed(Remark):
yaml_tag = '!Missed'
@property
def color(self):
return "red"
def get_remarks(input_file):
max_hotness = 0
all_remarks = dict()
file_remarks = defaultdict(functools.partial(defaultdict, list))
with open(input_file) as f:
docs = yaml.load_all(f, Loader=Loader)
for remark in docs:
remark.canonicalize()
# Avoid remarks withoug debug location or if they are duplicated
if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
continue
all_remarks[remark.key] = remark
file_remarks[remark.File][remark.Line].append(remark)
# If we're reading a back a diff yaml file, max_hotness is already
# captured which may actually be less than the max hotness found
# in the file.
if hasattr(remark, 'max_hotness'):
max_hotness = remark.max_hotness
max_hotness = max(max_hotness, remark.Hotness)
return max_hotness, all_remarks, file_remarks
def gather_results(filenames, num_jobs, should_print_progress):
if should_print_progress:
print('Reading YAML files...')
if not Remark.demangler_proc:
Remark.set_demangler(Remark.default_demangler)
remarks = optpmap.pmap(
get_remarks, filenames, num_jobs, should_print_progress)
max_hotness = max(entry[0] for entry in remarks)
def merge_file_remarks(file_remarks_job, all_remarks, merged):
for filename, d in iteritems(file_remarks_job):
for line, remarks in iteritems(d):
for remark in remarks:
# Bring max_hotness into the remarks so that
# RelativeHotness does not depend on an external global.
remark.max_hotness = max_hotness
if remark.key not in all_remarks:
merged[filename][line].append(remark)
all_remarks = dict()
file_remarks = defaultdict(functools.partial(defaultdict, list))
for _, all_remarks_job, file_remarks_job in remarks:
merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
all_remarks.update(all_remarks_job)
return all_remarks, file_remarks, max_hotness != 0
def find_opt_files(*dirs_or_files):
all = []
for dir_or_file in dirs_or_files:
if os.path.isfile(dir_or_file):
all.append(dir_or_file)
else:
for dir, subdirs, files in os.walk(dir_or_file):
# Exclude mounted directories and symlinks (os.walk default).
subdirs[:] = [d for d in subdirs
if not os.path.ismount(os.path.join(dir, d))]
for file in files:
if fnmatch.fnmatch(file, "*.opt.yaml"):
all.append(os.path.join(dir, file))
return all

View File

@ -1,208 +0,0 @@
.source {
table-layout: fixed;
width: 100%;
white-space: nowrap;
}
.source td {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.red {
background-color: #ffd0d0;
}
.cyan {
background-color: cyan;
}
body {
font-family: -apple-system, sans-serif;
}
pre {
margin-top: 0px !important;
margin-bottom: 0px !important;
}
.source-name-title {
padding: 5px 10px;
border-bottom: 1px solid #dbdbdb;
background-color: #eee;
line-height: 35px;
}
.centered {
display: table;
margin-left: left;
margin-right: auto;
border: 1px solid #dbdbdb;
border-radius: 3px;
}
.expansion-view {
background-color: rgba(0, 0, 0, 0);
margin-left: 0px;
margin-top: 5px;
margin-right: 5px;
margin-bottom: 5px;
border: 1px solid #dbdbdb;
border-radius: 3px;
}
table {
border-collapse: collapse;
}
.light-row {
background: #ffffff;
border: 1px solid #dbdbdb;
}
.column-entry {
text-align: right;
}
.column-entry-left {
text-align: left;
}
.column-entry-white {
text-align: right;
background-color: #ffffff;
}
.column-entry-red {
text-align: right;
background-color: #ffd0d0;
}
.column-entry-green {
text-align: right;
background-color: #d0ffd0;
}
.column-entry-yellow {
text-align: left;
background-color: #ffe1a6;
}
.column-entry-0 {
background-color: #ffffff;
}
.column-entry-1 {
background-color: #eeeeee;
}
.line-number {
text-align: right;
color: #aaa;
}
.covered-line {
text-align: right;
color: #0080ff;
}
.uncovered-line {
text-align: right;
color: #ff3300;
}
.tooltip {
position: relative;
display: inline;
background-color: #b3e6ff;
text-decoration: none;
}
.tooltip span.tooltip-content {
position: absolute;
width: 100px;
margin-left: -50px;
color: #FFFFFF;
background: #000000;
height: 30px;
line-height: 30px;
text-align: center;
visibility: hidden;
border-radius: 6px;
}
.tooltip span.tooltip-content:after {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -8px;
width: 0; height: 0;
border-top: 8px solid #000000;
border-right: 8px solid transparent;
border-left: 8px solid transparent;
}
:hover.tooltip span.tooltip-content {
visibility: visible;
opacity: 0.8;
bottom: 30px;
left: 50%;
z-index: 999;
}
th, td {
vertical-align: top;
padding: 2px 5px;
border-collapse: collapse;
border-right: solid 1px #eee;
border-left: solid 1px #eee;
}
td:first-child {
border-left: none;
}
td:last-child {
border-right: none;
}
/* Generated with pygmentize -S colorful -f html >> style.css */
.hll { background-color: #ffffcc }
.c { color: #888888 } /* Comment */
.err { color: #FF0000; background-color: #FFAAAA } /* Error */
.k { color: #008800; font-weight: bold } /* Keyword */
.o { color: #333333 } /* Operator */
.ch { color: #888888 } /* Comment.Hashbang */
.cm { color: #888888 } /* Comment.Multiline */
.cp { color: #557799 } /* Comment.Preproc */
.cpf { color: #888888 } /* Comment.PreprocFile */
.c1 { color: #888888 } /* Comment.Single */
.cs { color: #cc0000; font-weight: bold } /* Comment.Special */
.gd { color: #A00000 } /* Generic.Deleted */
.ge { font-style: italic } /* Generic.Emph */
.gr { color: #FF0000 } /* Generic.Error */
.gh { color: #000080; font-weight: bold } /* Generic.Heading */
.gi { color: #00A000 } /* Generic.Inserted */
.go { color: #888888 } /* Generic.Output */
.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.gt { color: #0044DD } /* Generic.Traceback */
.kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.kp { color: #003388; font-weight: bold } /* Keyword.Pseudo */
.kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.kt { color: #333399; font-weight: bold } /* Keyword.Type */
.m { color: #6600EE; font-weight: bold } /* Literal.Number */
.s { background-color: #fff0f0 } /* Literal.String */
.na { color: #0000CC } /* Name.Attribute */
.nb { color: #007020 } /* Name.Builtin */
.nc { color: #BB0066; font-weight: bold } /* Name.Class */
.no { color: #003366; font-weight: bold } /* Name.Constant */
.nd { color: #555555; font-weight: bold } /* Name.Decorator */
.ni { color: #880000; font-weight: bold } /* Name.Entity */
.ne { color: #FF0000; font-weight: bold } /* Name.Exception */
.nf { color: #0066BB; font-weight: bold } /* Name.Function */
.nl { color: #997700; font-weight: bold } /* Name.Label */
.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
.nt { color: #007700 } /* Name.Tag */
.nv { color: #996633 } /* Name.Variable */
.ow { color: #000000; font-weight: bold } /* Operator.Word */
.w { color: #bbbbbb } /* Text.Whitespace */
.mb { color: #6600EE; font-weight: bold } /* Literal.Number.Bin */
.mf { color: #6600EE; font-weight: bold } /* Literal.Number.Float */
.mh { color: #005588; font-weight: bold } /* Literal.Number.Hex */
.mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.mo { color: #4400EE; font-weight: bold } /* Literal.Number.Oct */
.sb { background-color: #fff0f0 } /* Literal.String.Backtick */
.sc { color: #0044DD } /* Literal.String.Char */
.sd { color: #DD4422 } /* Literal.String.Doc */
.s2 { background-color: #fff0f0 } /* Literal.String.Double */
.se { color: #666666; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */
.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */
.si { background-color: #eeeeee } /* Literal.String.Interpol */
.sx { color: #DD2200; background-color: #fff0f0 } /* Literal.String.Other */
.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */
.s1 { background-color: #fff0f0 } /* Literal.String.Single */
.ss { color: #AA6600 } /* Literal.String.Symbol */
.bp { color: #007020 } /* Name.Builtin.Pseudo */
.vc { color: #336699 } /* Name.Variable.Class */
.vg { color: #dd7700; font-weight: bold } /* Name.Variable.Global */
.vi { color: #3333BB } /* Name.Variable.Instance */
.il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */