Convert verilator_profcfunc to python.
This commit is contained in:
parent
f37c875be8
commit
1ee46ac5e1
|
|
@ -142,7 +142,7 @@ EXAMPLES_FIRST = \
|
||||||
EXAMPLES = $(EXAMPLES_FIRST) $(filter-out $(EXAMPLES_FIRST), $(sort $(wildcard examples/*)))
|
EXAMPLES = $(EXAMPLES_FIRST) $(filter-out $(EXAMPLES_FIRST), $(sort $(wildcard examples/*)))
|
||||||
|
|
||||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||||
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_gantt.1 verilator_profcfunc.1
|
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1
|
||||||
|
|
||||||
default: all
|
default: all
|
||||||
all: all_nomsg msg_test
|
all: all_nomsg msg_test
|
||||||
|
|
@ -369,6 +369,7 @@ clang-format:
|
||||||
|
|
||||||
PY_PROGRAMS = \
|
PY_PROGRAMS = \
|
||||||
bin/verilator_ccache_report \
|
bin/verilator_ccache_report \
|
||||||
|
bin/verilator_profcfunc \
|
||||||
examples/xml_py/vl_file_copy \
|
examples/xml_py/vl_file_copy \
|
||||||
examples/xml_py/vl_hier_graph \
|
examples/xml_py/vl_hier_graph \
|
||||||
docs/guide/conf.py \
|
docs/guide/conf.py \
|
||||||
|
|
|
||||||
|
|
@ -1,266 +1,204 @@
|
||||||
#!/usr/bin/env perl
|
#!/usr/bin/env python3
|
||||||
# See copyright, etc in below POD section.
|
# pylint: disable=C0103,C0114,C0116,R0914,R0912,R0915,eval-used
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
require 5.006_001;
|
import argparse
|
||||||
use warnings;
|
import collections
|
||||||
use Getopt::Long;
|
import re
|
||||||
use IO::File;
|
# from pprint import pprint
|
||||||
use Pod::Usage;
|
|
||||||
eval { use Data::Dumper; $Data::Dumper::Indent = 1; }; # Debug, ok if missing
|
|
||||||
use strict;
|
|
||||||
use vars qw($Debug);
|
|
||||||
|
|
||||||
#======================================================================
|
######################################################################
|
||||||
|
|
||||||
|
|
||||||
#======================================================================
|
def profcfunc(filename):
|
||||||
# main
|
funcs = {}
|
||||||
|
|
||||||
$Debug = 0;
|
with open(filename) as fh:
|
||||||
my $Opt_File;
|
|
||||||
autoflush STDOUT 1;
|
|
||||||
autoflush STDERR 1;
|
|
||||||
Getopt::Long::config("no_auto_abbrev");
|
|
||||||
if (! GetOptions(
|
|
||||||
"help" => \&usage,
|
|
||||||
"debug" => \&debug,
|
|
||||||
"<>" => \¶meter,
|
|
||||||
)) {
|
|
||||||
die "%Error: Bad usage, try 'verilator_profcfunc --help'\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
defined $Opt_File or die "%Error: No filename given\n";
|
for line in fh:
|
||||||
|
|
||||||
profcfunc($Opt_File);
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
|
||||||
|
|
||||||
sub usage {
|
|
||||||
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
|
||||||
exit(1); # Unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
sub debug {
|
|
||||||
$Debug = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub parameter {
|
|
||||||
my $param = shift;
|
|
||||||
if (!defined $Opt_File) {
|
|
||||||
$Opt_File = $param;
|
|
||||||
} else {
|
|
||||||
die "%Error: Unknown parameter: $param\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
|
|
||||||
sub profcfunc {
|
|
||||||
my $filename = shift;
|
|
||||||
# Remove hex numbers before diffing
|
|
||||||
my $fh = IO::File->new($filename) or die "%Error: $! $filename,";
|
|
||||||
|
|
||||||
my %funcs;
|
|
||||||
|
|
||||||
while (defined(my $line = $fh->getline)) {
|
|
||||||
# %time cumesec selfsec calls {stuff} name
|
# %time cumesec selfsec calls {stuff} name
|
||||||
if ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
|
match = re.match(
|
||||||
my $pct=$1; my $sec=$2; my $calls=$3; my $func=$4;
|
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
|
||||||
$funcs{$func}{pct} += $pct;
|
line)
|
||||||
$funcs{$func}{sec} += $sec;
|
if match:
|
||||||
$funcs{$func}{calls} += $calls;
|
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
|
# Older gprofs have no call column for single-call functions
|
||||||
# %time cumesec selfsec {stuff} name
|
# %time cumesec selfsec {stuff} name
|
||||||
elsif ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
|
match = re.match(
|
||||||
my $pct=$1; my $sec=$2; my $calls=1; my $func=$3;
|
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
|
||||||
$funcs{$func}{pct} += $pct;
|
line)
|
||||||
$funcs{$func}{sec} += $sec;
|
if match:
|
||||||
$funcs{$func}{calls} += $calls;
|
pct = float(match.group(1))
|
||||||
}
|
sec = float(match.group(2))
|
||||||
}
|
calls = 1
|
||||||
$fh->close;
|
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
|
# Find modules
|
||||||
my %verilated_mods;
|
verilated_mods = {}
|
||||||
foreach my $func (keys %funcs) {
|
for func in funcs:
|
||||||
if ($func =~ /(.*)::eval\(/) {
|
match = re.search(r'(.*)::eval\(', func)
|
||||||
print "-got _eval $func prefix=$1\n" if $Debug;
|
if match:
|
||||||
$verilated_mods{$1} = qr/^$1/;
|
prefix = match.group(1)
|
||||||
}
|
if Args.debug:
|
||||||
}
|
print("-got _eval %s prefix=%s" % (func, prefix))
|
||||||
#print Dumper(\%verilated_mods);
|
verilated_mods[prefix] = re.compile(r'^' + prefix)
|
||||||
|
# pprint(verilated_mods)
|
||||||
|
|
||||||
# Sort by Verilog name
|
# Sort by Verilog name
|
||||||
my %vfuncs;
|
vfuncs = {}
|
||||||
my %groups;
|
groups = {}
|
||||||
foreach my $func (keys %funcs) {
|
groups['type'] = collections.defaultdict(lambda: 0)
|
||||||
my $pct = $funcs{$func}{pct};
|
groups['design'] = collections.defaultdict(lambda: 0)
|
||||||
my $vfunc = $func;
|
groups['module'] = collections.defaultdict(lambda: 0)
|
||||||
|
|
||||||
(my $funcarg = $func) =~ s/^.*\(//;
|
for func in funcs:
|
||||||
|
pct = funcs[func]['pct']
|
||||||
|
vfunc = func
|
||||||
|
|
||||||
my $design;
|
funcarg = re.sub(r'^.*\(', '', func)
|
||||||
foreach my $vde (keys %verilated_mods) {
|
|
||||||
if ($func =~ /$verilated_mods{$vde}/
|
|
||||||
|| $funcarg =~ /$verilated_mods{$vde}/) {
|
|
||||||
$design = $vde;
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
my $vdesign = "-";
|
design = None
|
||||||
if ($design && $vfunc =~ /__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(/) {
|
for vde in verilated_mods:
|
||||||
$vfunc = sprintf("VBlock %s:%d", $1, $2);
|
if verilated_mods[vde].match(func) or verilated_mods[vde].match(
|
||||||
$vdesign = $design;
|
funcarg):
|
||||||
$groups{type}{"Verilog Blocks under $design"} += $pct;
|
design = vde
|
||||||
$groups{design}{$design} += $pct;
|
break
|
||||||
$groups{module}{$1} += $pct;
|
|
||||||
} else {
|
vdesign = "-"
|
||||||
if ($design) {
|
|
||||||
$vfunc = sprintf("VCommon %s", $func);
|
prof_match = re.search(r'__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(', vfunc)
|
||||||
$vdesign = $design;
|
if design and prof_match:
|
||||||
$groups{type}{"Common code under $design"} += $pct;
|
linefunc = prof_match.group(1)
|
||||||
$groups{design}{$design} += $pct;
|
lineno = int(prof_match.group(2))
|
||||||
$groups{module}{$design." common code"} += $pct;
|
vfunc = "VBlock %s:%d" % (linefunc, lineno)
|
||||||
} elsif ($func =~ /^VL_[A-Z0-9_]+/
|
vdesign = design
|
||||||
|| $func =~ /^_?vl_[a-zA-Z0-9_]+/
|
groups['type']["Verilog Blocks under " + design] += pct
|
||||||
|| $func =~ /^verilated/i) {
|
groups['design'][design] += pct
|
||||||
$vfunc = sprintf("VLib %s", $func);
|
groups['module'][linefunc] += pct
|
||||||
$groups{type}{'VLib'} += $pct;
|
elif design:
|
||||||
$groups{design}{'VLib'} += $pct;
|
vfunc = "VCommon " + func
|
||||||
$groups{module}{'VLib'} += $pct;
|
vdesign = design
|
||||||
} elsif ($func =~ /^_mcount_private/) {
|
groups['type']["Common code under " + design] += pct
|
||||||
$vfunc = sprintf("Prof %s", $func);
|
groups['design'][design] += pct
|
||||||
$groups{type}{'Prof'} += $pct;
|
groups['module'][design + " common code"] += pct
|
||||||
$groups{design}{'Prof'} += $pct;
|
elif re.match(r'(VL_[A-Z0-9_]+|_?vl_[a-zA-Z0-9_]+|Verilated)', vfunc):
|
||||||
$groups{module}{'Prof'} += $pct;
|
vfunc = "VLib " + func
|
||||||
} else {
|
groups['type']['VLib'] += pct
|
||||||
$vfunc = sprintf("C++ %s", $func);
|
groups['design']['VLib'] += pct
|
||||||
$groups{type}{'C++'} += $pct;
|
groups['module']['VLib'] += pct
|
||||||
$groups{design}{'C++'} += $pct;
|
elif re.match(r'^_mcount_private', vfunc):
|
||||||
$groups{module}{'C++'} += $pct;
|
vfunc = "Prof " + func
|
||||||
}
|
groups['type']['Prof'] += pct
|
||||||
}
|
groups['design']['Prof'] += pct
|
||||||
if (!$vfuncs{$vfunc}) {
|
groups['module']['Prof'] += pct
|
||||||
$vfuncs{$vfunc} = $funcs{$func};
|
else:
|
||||||
$vfuncs{$vfunc}{design} = $vdesign;
|
vfunc = "C++ " + func
|
||||||
} else {
|
groups['type']['C++'] += pct
|
||||||
$vfuncs{$vfunc}{pct} += $funcs{$func}{pct};
|
groups['design']['C++'] += pct
|
||||||
$vfuncs{$vfunc}{calls} += $funcs{$func}{calls};
|
groups['module']['C++'] += pct
|
||||||
$vfuncs{$vfunc}{sec} += $funcs{$func}{sec};
|
|
||||||
}
|
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 %8d %-" + str(design_width) + "s %s") %
|
||||||
|
(vfuncs[func]['pct'], cume, vfuncs[func]['sec'],
|
||||||
|
vfuncs[func]['calls'], vfuncs[func]['design'], func))
|
||||||
|
|
||||||
|
|
||||||
foreach my $type (qw(type design module)) {
|
######################################################################
|
||||||
my $missing = 100;
|
######################################################################
|
||||||
foreach (sort (keys %{$groups{$type}})) {
|
|
||||||
$missing -= $groups{$type}{$_};
|
|
||||||
}
|
|
||||||
if ($missing) {
|
|
||||||
$groups{$type}{"\377Unaccounted for/rounding error"} = $missing;
|
|
||||||
}
|
|
||||||
|
|
||||||
print("Overall summary by $type:\n");
|
parser = argparse.ArgumentParser(
|
||||||
printf(" %-6s %s\n","% time",$type);
|
allow_abbrev=False,
|
||||||
foreach my $what (sort (keys %{$groups{$type}})) {
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
(my $pwhat = $what) =~ s/^\377//; # Just used to establish sort order
|
description="""Read gprof report created with --prof-cfuncs""",
|
||||||
printf(" %6.2f %s\n", $groups{$type}{$what}, $pwhat);
|
epilog=
|
||||||
}
|
"""Verilator_profcfunc reads a profile report created by gprof. The names of
|
||||||
print("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
my $design_width = 1;
|
|
||||||
foreach my $func (keys %vfuncs) {
|
|
||||||
if ($design_width < length($vfuncs{$func}{design})) {
|
|
||||||
$design_width = length($vfuncs{$func}{design});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print("Verilog code profile:\n");
|
|
||||||
print(" These are split into three categories:\n");
|
|
||||||
print(" C++: Time in non-Verilated C++ code\n");
|
|
||||||
print(" Prof: Time in profile overhead\n");
|
|
||||||
print(" VBlock: Time attributable to a block in a Verilog file and line\n");
|
|
||||||
print(" VCommon: Time in a Verilated module, due to all parts of the design\n");
|
|
||||||
print(" VLib: Time in Verilated common libraries, called by the Verilated code\n");
|
|
||||||
print("\n");
|
|
||||||
|
|
||||||
print(" % cumulative self \n");
|
|
||||||
print(" time seconds seconds calls ");
|
|
||||||
printf("%-${design_width}s", "design");
|
|
||||||
print(" type filename and line number\n");
|
|
||||||
|
|
||||||
my $cume = 0;
|
|
||||||
foreach my $func (sort {$vfuncs{$b}{sec} <=> $vfuncs{$a}{sec}
|
|
||||||
|| $a cmp $b}
|
|
||||||
(keys %vfuncs)) {
|
|
||||||
$cume += $vfuncs{$func}{sec};
|
|
||||||
printf +("%6.2f %9.2f %8.2f %8d %-${design_width}s %s\n",
|
|
||||||
$vfuncs{$func}{pct},
|
|
||||||
$cume,
|
|
||||||
$vfuncs{$func}{sec},
|
|
||||||
$vfuncs{$func}{calls},
|
|
||||||
$vfuncs{$func}{design},
|
|
||||||
$func);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
__END__
|
|
||||||
|
|
||||||
=pod
|
|
||||||
|
|
||||||
=head1 NAME
|
|
||||||
|
|
||||||
verilator_profcfunc - Read gprof report created with --prof-cfuncs
|
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
|
||||||
|
|
||||||
verilator --prof-cfuncs ....
|
|
||||||
gcc ....
|
|
||||||
{run executable}
|
|
||||||
gprof
|
|
||||||
verilator_profcfuncs gprof.out
|
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
|
||||||
|
|
||||||
Verilator_profcfunc reads a profile report created by gprof. The names of
|
|
||||||
the functions are then transformed, assuming the user used Verilator's
|
the functions are then transformed, assuming the user used Verilator's
|
||||||
--prof-cfuncs, and a report printed showing the percentage of time, etc,
|
--prof-cfuncs, and a report printed showing the percentage of time, etc,
|
||||||
in each Verilog block.
|
in each Verilog block.
|
||||||
|
|
||||||
For documentation see
|
For documentation see
|
||||||
L<https://verilator.org/guide/latest/exe_verilator_profcfuncs.html>.
|
https://verilator.org/guide/latest/exe_verilator_profcfuncs.html
|
||||||
|
|
||||||
=head1 ARGUMENT SUMMARY
|
Copyright 2002-2021 by Wilson Snyder. This program is free software; you
|
||||||
|
|
||||||
<filename> Input file (gprof.out)
|
|
||||||
--help Display this help
|
|
||||||
|
|
||||||
=head1 DISTRIBUTION
|
|
||||||
|
|
||||||
The latest version is available from L<https://verilator.org>.
|
|
||||||
|
|
||||||
Copyright 2007-2021 by Wilson Snyder. This program is free software; you
|
|
||||||
can redistribute it and/or modify it under the terms of either the GNU
|
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
|
Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
Version 2.0.
|
Version 2.0.
|
||||||
|
|
||||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||||
|
|
||||||
=head1 SEE ALSO
|
parser.add_argument('--debug',
|
||||||
|
action='store_const',
|
||||||
|
const=9,
|
||||||
|
help='enable debug')
|
||||||
|
parser.add_argument('filename', help='input gprof output to process')
|
||||||
|
|
||||||
C<verilator>
|
Args = parser.parse_args()
|
||||||
|
profcfunc(Args.filename)
|
||||||
and L<https://verilator.org/guide/latest/exe_verilator_profcfuncs.html> for
|
|
||||||
detailed documentation.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
### Local Variables:
|
# Local Variables:
|
||||||
### compile-command: "$V4/bin/verilator_profcfunc $V4/test_c/obj_dir/V*_03_*.tree $V4N/test_c/obj_dir/V*_03_*.tree"
|
# compile-command: "./verilator_profcfunc ../test_regress/t/t_profcfunc.gprof"
|
||||||
### End:
|
# End:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue