mirror of
https://github.com/verilator/verilator.git
synced 2024-12-28 18:27:34 +00:00
193 lines
7.3 KiB
Python
Executable File
193 lines
7.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# pylint: disable=C0103,C0114,C0116,C0209,R0914,R0912,R0915,eval-used
|
|
######################################################################
|
|
|
|
import argparse
|
|
import collections
|
|
import re
|
|
# from pprint import pprint
|
|
|
|
######################################################################
|
|
|
|
|
|
def profcfunc(filename):
|
|
funcs = {}
|
|
|
|
with open(filename, "r", encoding="utf8") as fh:
|
|
|
|
for line in fh:
|
|
# %time cumesec selfsec calls {stuff} name
|
|
match = re.match(
|
|
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
|
|
line)
|
|
if match:
|
|
pct = float(match.group(1))
|
|
sec = float(match.group(2))
|
|
calls = float(match.group(3))
|
|
func = match.group(4)
|
|
if func not in funcs:
|
|
funcs[func] = {'pct': 0, 'sec': 0, 'calls': 0}
|
|
funcs[func]['pct'] += pct
|
|
funcs[func]['sec'] += sec
|
|
funcs[func]['calls'] += calls
|
|
continue
|
|
|
|
# Older gprofs have no call column for single-call functions
|
|
# %time cumesec selfsec {stuff} name
|
|
match = re.match(r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
|
|
line)
|
|
if match:
|
|
pct = float(match.group(1))
|
|
sec = float(match.group(2))
|
|
calls = 1
|
|
func = match.group(3)
|
|
if func not in funcs:
|
|
funcs[func] = {'pct': 0, 'sec': 0, 'calls': 0}
|
|
funcs[func]['pct'] += pct
|
|
funcs[func]['sec'] += sec
|
|
funcs[func]['calls'] += calls
|
|
continue
|
|
|
|
# Find modules
|
|
verilated_mods = {}
|
|
for func in funcs:
|
|
match = re.search(r'(.*)::eval(_step)?\(', func)
|
|
if match:
|
|
prefix = match.group(1)
|
|
if Args.debug:
|
|
print("-got _eval %s prefix=%s" % (func, prefix))
|
|
verilated_mods[prefix] = re.compile(r'^' + prefix)
|
|
# pprint(verilated_mods)
|
|
|
|
# Sort by Verilog name
|
|
vfuncs = {}
|
|
groups = {}
|
|
groups['type'] = collections.defaultdict(lambda: 0)
|
|
groups['design'] = collections.defaultdict(lambda: 0)
|
|
groups['module'] = collections.defaultdict(lambda: 0)
|
|
|
|
for func, func_item in funcs.items():
|
|
pct = func_item['pct']
|
|
vfunc = func
|
|
|
|
funcarg = re.sub(r'^.*\(', '', func)
|
|
|
|
design = None
|
|
for vde, vde_item in verilated_mods.items():
|
|
if vde_item.match(func) or vde_item.match(funcarg):
|
|
design = vde
|
|
break
|
|
|
|
vdesign = "-"
|
|
|
|
prof_match = re.search(r'__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(', vfunc)
|
|
if design and prof_match:
|
|
linefunc = prof_match.group(1)
|
|
lineno = int(prof_match.group(2))
|
|
vfunc = "VBlock %s:%d" % (linefunc, lineno)
|
|
vdesign = design
|
|
groups['type']["Verilog Blocks under " + design] += pct
|
|
groups['design'][design] += pct
|
|
groups['module'][linefunc] += pct
|
|
elif design:
|
|
vfunc = "VCommon " + func
|
|
vdesign = design
|
|
groups['type']["Common code under " + design] += pct
|
|
groups['design'][design] += pct
|
|
groups['module'][design + " common code"] += pct
|
|
elif re.match(r'(VL_[A-Z0-9_]+|_?vl_[a-zA-Z0-9_]+|Verilated)', vfunc):
|
|
vfunc = "VLib " + func
|
|
groups['type']['VLib'] += pct
|
|
groups['design']['VLib'] += pct
|
|
groups['module']['VLib'] += pct
|
|
elif re.match(r'^_mcount_private', vfunc):
|
|
vfunc = "Prof " + func
|
|
groups['type']['Prof'] += pct
|
|
groups['design']['Prof'] += pct
|
|
groups['module']['Prof'] += pct
|
|
else:
|
|
vfunc = "C++ " + func
|
|
groups['type']['C++'] += pct
|
|
groups['design']['C++'] += pct
|
|
groups['module']['C++'] += pct
|
|
|
|
if vfunc not in vfuncs:
|
|
vfuncs[vfunc] = func_item
|
|
vfuncs[vfunc]['design'] = vdesign
|
|
else:
|
|
vfuncs[vfunc]['pct'] += func_item['pct']
|
|
vfuncs[vfunc]['calls'] += func_item['calls']
|
|
vfuncs[vfunc]['sec'] += func_item['sec']
|
|
|
|
for ftype in ['type', 'design', 'module']:
|
|
missing = 100
|
|
for item in groups[ftype].keys():
|
|
missing -= groups[ftype][item]
|
|
groups[ftype]["\377Unaccounted for/rounding error"] = missing
|
|
|
|
print("Overall summary by %s:" % ftype)
|
|
print(" %-6s %s" % ("% time", ftype))
|
|
for what in sorted(groups[ftype].keys()):
|
|
# \377 used to establish sort order
|
|
pwhat = re.sub(r'^\377', '', what)
|
|
print(" %6.2f %s" % (groups[ftype][what], pwhat))
|
|
print()
|
|
|
|
design_width = 1
|
|
for func, func_item in vfuncs.items():
|
|
design_width = max(design_width, len(func_item['design']))
|
|
|
|
print("Verilog code profile:")
|
|
print(" These are split into three categories:")
|
|
print(" C++: Time in non-Verilated C++ code")
|
|
print(" Prof: Time in profile overhead")
|
|
print(" VBlock: Time attributable to a block in a" + " Verilog file and line")
|
|
print(" VCommon: Time in a Verilated module," + " due to all parts of the design")
|
|
print(" VLib: Time in Verilated common libraries," + " called by the Verilated code")
|
|
print()
|
|
|
|
print(" % cumulative self ")
|
|
print((" time seconds seconds calls %-" + str(design_width) +
|
|
"s type filename and line number") % "design")
|
|
|
|
cume = 0
|
|
for func in sorted(vfuncs.keys(), key=lambda f: vfuncs[f]['sec'], reverse=True):
|
|
cume += vfuncs[func]['sec']
|
|
print(("%6.2f %9.2f %8.2f %10d %-" + str(design_width) + "s %s") %
|
|
(vfuncs[func]['pct'], cume, vfuncs[func]['sec'], vfuncs[func]['calls'],
|
|
vfuncs[func]['design'], func))
|
|
|
|
|
|
######################################################################
|
|
######################################################################
|
|
|
|
parser = argparse.ArgumentParser(
|
|
allow_abbrev=False,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
description="""Read gprof report created with --prof-cfuncs.
|
|
|
|
Verilator_profcfunc reads a profile report created by gprof. The names of
|
|
the functions are then transformed, assuming the user used Verilator's
|
|
--prof-cfuncs, and a report printed showing the percentage of time, etc,
|
|
in each Verilog block.
|
|
|
|
For documentation see
|
|
https://verilator.org/guide/latest/exe_verilator_profcfunc.html""",
|
|
epilog="""Copyright 2002-2024 by Wilson Snyder. This program is free software; you
|
|
can redistribute it and/or modify it under the terms of either the GNU
|
|
Lesser General Public License Version 3 or the Perl Artistic License
|
|
Version 2.0.
|
|
|
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
|
|
|
parser.add_argument('--debug', action='store_const', const=9, help='enable debug')
|
|
parser.add_argument('filename', help='input gprof output to process')
|
|
|
|
Args = parser.parse_args()
|
|
profcfunc(Args.filename)
|
|
|
|
######################################################################
|
|
# Local Variables:
|
|
# compile-command: "./verilator_profcfunc ../test_regress/t/t_profcfunc.gprof"
|
|
# End:
|