| #!/usr/bin/env python3 |
| # pylint: disable=C0103,C0114,C0116,R0914,R0912,R0915,eval-used |
| ###################################################################### |
| |
| import argparse |
| import collections |
| import re |
| # from pprint import pprint |
| |
| ###################################################################### |
| |
| |
| def profcfunc(filename): |
| funcs = {} |
| |
| with open(filename) 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 in funcs: |
| pct = funcs[func]['pct'] |
| vfunc = func |
| |
| funcarg = re.sub(r'^.*\(', '', func) |
| |
| design = None |
| for vde in verilated_mods: |
| if verilated_mods[vde].match(func) or verilated_mods[vde].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] = funcs[func] |
| vfuncs[vfunc]['design'] = vdesign |
| else: |
| vfuncs[vfunc]['pct'] += funcs[func]['pct'] |
| vfuncs[vfunc]['calls'] += funcs[func]['calls'] |
| vfuncs[vfunc]['sec'] += funcs[func]['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 in vfuncs: |
| if design_width < len(vfuncs[func]['design']): |
| design_width = len(vfuncs[func]['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""", |
| epilog= |
| """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 |
| |
| Copyright 2002-2022 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: |