Files
verilator/bin/verilator_profcfunc
T
Geza Lore 8546d5db06 Internals: Move the eval loop into the runtime library (#8225)
The loops modelling the SystemVerilog scheduling regions are no longer
generated. They now live in 'VerilatedEvalLoop' in the runtime library.
The generated model holds one as a member, passing itself to it, and
exposes each evaluation entry point to it as a pure virtual method on
VerilatedModel. The model's 'eval' and 'eval_step' remain the top level
entry points, and are backward compatible.

V3Sched no longer emits '_eval' or '_eval_settle', etc.. Instead every
evaluation entry point called from the runtime is enumerated by 'VEval',

Scheduling creates all entry points, for all scheduling regions, even if
they are empty, and the runtime eval loop calls everything
unconditionally. If regions are empty, this is simply a call to an empty
function. This will hurt performance on very small models, but should
not be noticeable on anything meaningful, so it is likely best to keep
to reduce complexity.

A scheduling entry points evaluate a single iteration and returns
whether it did any work, they are effectively the previous
`_eval_phase_*` functions.
2026-08-27 07:53:02 -04:00

199 lines
7.7 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: str) -> None:
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.0, 'sec': 0.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 only the model entry points: 'eval', 'eval_step', 'eval_end_step',
# and the 'evalXxx' region entry points, but not e.g. a user's '::evaluate'
match = re.match(r'([A-Za-z_][A-Za-z_0-9]*)::eval(_step|_end_step|[A-Z][A-Za-z_0-9]*)?\(',
func)
if match:
prefix = match.group(1)
if prefix.startswith('Verilated'): # Run-time library, not a model
continue
if Args.debug:
print("-got _eval %s prefix=%s" % (func, prefix))
verilated_mods[prefix] = re.compile(r'^' + re.escape(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.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 Verilator --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="""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-FileCopyrightText: 2002-2026 Wilson Snyder
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: