Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / procs / sunsparc / debug / tools / coverage.pl
#!/pkg/gnu/bin/perl -w
#
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "@(#)coverage.pl 1.1 06/03/30 SMI"
#
# Purpose of this script is to take the coverage output from legion
# and map it to the dis-assembly of a binary (in this case, hypervisor)
# so that we can map out which code paths in Hypervisor have been
# executed.
#
# Input:
# q.dis - output from dis-assembly of q binary
# coverage.log - output from code coverage_dump tool in legion
#
# Processing:
# - read the coverage data a build up an array of addresses and ref_cnts
# - read the dis-assembly file and figure out the address offsets for
# each instruction.
# - foreach line in the dis-assembly, get the ref_cnt from the coverage
# file and write out to new file.
#
# Output:
# coverage.txt - A text file containing the dis output for the binary
# under examination with a ref_cnt per line showing how
# many times each instn was executed.
# coverage.html- A html file version of the coverage.txt file with
# un-executed code paths highlighted in red text.
#
use strict;
use Switch;
# input files
my $disfile = "q.dis";
my $covfile="coverage.log";
# output files
my $outfile_txt="coverage.txt";
my $outfile_html="coverage.html";
# to be parse from command line
my $hypervisor="";
# init all variables
my $cov_idx = 0;
my $cov_addr = 0;
my $cov_cnt = 0;
my $cov_instn = 0;
my $dis_sym = 0;
my $dis_instn = 0;
my $num_addrs_executed = 0;
my $font_head = 0;
my $font_tail = 0;
my %cov_cnt;
my %cov_instn;
my $hv_base_addr_str = "";
my $ignore0 = 0;
my $ignore1 = 0;
my $percentage = 0;
my $summary = 0;
my $ref_cnt = 0;
my $ref_str = '';
my $col_idx = 0;
my $col_ref_str = "";
my $col_sym = "";
my $col_instn = "";
sub main
{
#
# Figure out from args whether this is an N1, N2 or Rock Hypervisor
# we are analysing. The output from dis needs to have it's addresses
# aligned to the location in memory where Hypervisor runs from.
#
$hypervisor = "$ARGV[0]";
switch ($hypervisor) {
case "n1" { print "Analysing Niagara1 hypervisor\n" }
case "n2" { print "Analysing Niagara2 hypervisor\n" }
case "rock" { print "Analysing Rock hypervisor\n" }
case "" { die "Usage: coverage.pl [processor]\nValid processors, [n1|n2|rock]\n" }
else { die "Fatal: Unknown processor $hypervisor\n" }
}
#
# Load dis and coverage files
#
open(dis_f, $disfile)
|| die "Cannot open $disfile: $!\n";
open(cov_f, $covfile)
|| die "Cannot open $covfile: $!\n";
#
# open output files
#
open(outf_txt, ">$outfile_txt")
|| die "Cannot open $$outfile_txt: $!\n";
open(outf_html, ">$outfile_html")
|| die "Cannot open $$outfile_html: $!\n";
# Print header of html and text files
print_header();
#
# build up an array of the coverage log file output so that
# we can search it using the address as an index later
#
while (<cov_f>) {
($cov_idx,$cov_addr,$cov_cnt,$cov_instn) = split(/:/);
$cov_cnt{$cov_addr} = hex($cov_cnt);
$cov_instn{$cov_addr} = $cov_instn;
}
# Accounting for coverage data
my $num_addrs = 0; # total number of PC samples
my $num_addrs_excuted = 0; # only incr when ref_cnt > 0
my $hv_base_addr = get_hv_base_addr();
printf ("Hypervisor base address is 0x%llx\n", $hv_base_addr);
my $hv_addr_offset = 0x0;
#
# For each line of the dis output, check the coverage array
# for a ref_cnt. Then output the data into a new file using
# the text output and the html output.
#
while (<dis_f>) {
chomp;
next if (!/\s*([^:]+):\s*(.*)$/);
$dis_sym = $1;
$dis_instn = $2;
# Calculate the %pc address for this instruction
my $dis_addr += ($hv_base_addr + $hv_addr_offset);
$hv_addr_offset += 0x4;
#
# Map dis-assembly address to a legion address (offset in memory where HV
# runs from) so that it can match the coverage data.
#
switch ($hypervisor) {
case "n1" { no warnings; $dis_addr = (($dis_addr - hex('fff0000000')) + hex('400000')); }
case "n2" { no warnings; $dis_addr = (($dis_addr - hex('fff0000000')) + hex('100000')); }
case "rock" { $dis_addr += (hex('100000')); }
else { die "Fatal: need to figure out addr conversion for $hypervisor" }
}
#
# convert the address to a string with "0x" and the hex number
# so that we can use it as an index into the coverage data array
#
my $index_addr = sprintf("0x%llx", $dis_addr);
$ref_cnt = 0x0;
$ref_cnt = $cov_cnt{$index_addr} if $cov_cnt{$index_addr};
if ($ref_cnt == 0) {
# %pc not executed
$ref_str = 'NIL';
} else {
# %pc executed
$ref_str = sprintf("%d", $ref_cnt);
$num_addrs_executed += 1;
}
#
# Count number of %pc samples
# Ignore illtrap %pcs for coverage purposes
#
if (($dis_instn =~ /illtrap/) && (!$ref_cnt)) {
# Don't count or print illtrap %pcs that have no ref_cnt
} else {
# count number of %pcs whether they are executed or not
$num_addrs += 1;
print_row($index_addr, $ref_str, $dis_sym, $dis_instn);
}
}
#
# Calculate coverage accounting totals
#
$percentage = (($num_addrs_executed / ($num_addrs)) * 100);
$summary = sprintf ("Summary: Number of valid %%pc values in binary is %llu, coverage is [%.2f%%]\n",
$num_addrs, $percentage);
printf outf_txt ("%s", $summary);
printf outf_html ("<br>%s<br>\n", $summary);
printf ("%s", $summary);
print_footer();
} #end main
sub print_header
{
print outf_txt "Addr, Ref_Cnt, Label, Instruction (raw, decoded)\n";
print outf_html << "EOF";
<html>
<head>
<title>Hypervisor Coverage data for $hypervisor</title>
</head>
<body>
<table WIDTH="100%" NOSAVE >
EOF
} # end print_header
sub print_footer
{
print outf_html << "EOF";
</table>
</verbatim>
</body>
</html>
EOF
} # end print_footer
#
# print a row of the table using args passed in
#
sub print_row
{
$col_idx = $_[0];
$col_ref_str = $_[1];
$col_sym = $_[2];
$col_instn = $_[3];
# chomp($col_instn);
$font_head="";
$font_tail="";
if ($col_ref_str eq 'NIL') {
# Mark all NIL lines in red font
$font_head= "<font color=red>";
$font_tail= "</font>";
print outf_txt " ";
} elsif ($col_ref_str eq 'N/A') {
# Print this for each line whose ref_cnt is N/A
print outf_txt " ";
} else {
# Print this for each line whose ref_cnt is non-zero
print outf_txt "-->";
}
#
# output to html file
#
print outf_html "<tr>\n";
print outf_html "\t<td>$font_head $col_idx $font_tail</td>\n";
print outf_html "\t<td>$font_head $col_ref_str $font_tail</td>\n";
print outf_html "\t<td>$font_head $col_sym $font_tail</td>\n";
print outf_html "\t<td>$font_head $col_instn $font_tail</td>\n";
print outf_html "</tr>";
print outf_html "\n";
#
# output to text file
#
print outf_txt "$col_idx, $col_ref_str, $col_sym, $col_instn\n";
} # end print_row
#
# Get the base address of the hypervisor using /usr/ccs/bin/nm
# to lookup the address of the _START_ symbol
#
sub get_hv_base_addr
{
my $returncode = `/usr/ccs/bin/nm -xp q | grep _START_`;
($hv_base_addr_str,$ignore0,$ignore1) = split(/ /, $returncode);
no warnings;
return hex($hv_base_addr_str);
} # end get_hv_base_addr
main();