Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | #!/pkg/gnu/bin/perl -w |
2 | # | |
3 | # Copyright 2006 Sun Microsystems, Inc. All rights reserved. | |
4 | # Use is subject to license terms. | |
5 | # | |
6 | # ident "@(#)coverage.pl 1.1 06/03/30 SMI" | |
7 | # | |
8 | # Purpose of this script is to take the coverage output from legion | |
9 | # and map it to the dis-assembly of a binary (in this case, hypervisor) | |
10 | # so that we can map out which code paths in Hypervisor have been | |
11 | # executed. | |
12 | # | |
13 | # Input: | |
14 | # q.dis - output from dis-assembly of q binary | |
15 | # coverage.log - output from code coverage_dump tool in legion | |
16 | # | |
17 | # Processing: | |
18 | # - read the coverage data a build up an array of addresses and ref_cnts | |
19 | # - read the dis-assembly file and figure out the address offsets for | |
20 | # each instruction. | |
21 | # - foreach line in the dis-assembly, get the ref_cnt from the coverage | |
22 | # file and write out to new file. | |
23 | # | |
24 | # Output: | |
25 | # coverage.txt - A text file containing the dis output for the binary | |
26 | # under examination with a ref_cnt per line showing how | |
27 | # many times each instn was executed. | |
28 | # coverage.html- A html file version of the coverage.txt file with | |
29 | # un-executed code paths highlighted in red text. | |
30 | # | |
31 | ||
32 | use strict; | |
33 | use Switch; | |
34 | ||
35 | # input files | |
36 | my $disfile = "q.dis"; | |
37 | my $covfile="coverage.log"; | |
38 | ||
39 | # output files | |
40 | my $outfile_txt="coverage.txt"; | |
41 | my $outfile_html="coverage.html"; | |
42 | ||
43 | # to be parse from command line | |
44 | my $hypervisor=""; | |
45 | ||
46 | # init all variables | |
47 | my $cov_idx = 0; | |
48 | my $cov_addr = 0; | |
49 | my $cov_cnt = 0; | |
50 | my $cov_instn = 0; | |
51 | my $dis_sym = 0; | |
52 | my $dis_instn = 0; | |
53 | my $num_addrs_executed = 0; | |
54 | my $font_head = 0; | |
55 | my $font_tail = 0; | |
56 | my %cov_cnt; | |
57 | my %cov_instn; | |
58 | my $hv_base_addr_str = ""; | |
59 | my $ignore0 = 0; | |
60 | my $ignore1 = 0; | |
61 | my $percentage = 0; | |
62 | my $summary = 0; | |
63 | my $ref_cnt = 0; | |
64 | my $ref_str = ''; | |
65 | my $col_idx = 0; | |
66 | my $col_ref_str = ""; | |
67 | my $col_sym = ""; | |
68 | my $col_instn = ""; | |
69 | ||
70 | sub main | |
71 | { | |
72 | # | |
73 | # Figure out from args whether this is an N1, N2 or Rock Hypervisor | |
74 | # we are analysing. The output from dis needs to have it's addresses | |
75 | # aligned to the location in memory where Hypervisor runs from. | |
76 | # | |
77 | $hypervisor = "$ARGV[0]"; | |
78 | switch ($hypervisor) { | |
79 | case "n1" { print "Analysing Niagara1 hypervisor\n" } | |
80 | case "n2" { print "Analysing Niagara2 hypervisor\n" } | |
81 | case "rock" { print "Analysing Rock hypervisor\n" } | |
82 | case "" { die "Usage: coverage.pl [processor]\nValid processors, [n1|n2|rock]\n" } | |
83 | else { die "Fatal: Unknown processor $hypervisor\n" } | |
84 | } | |
85 | ||
86 | # | |
87 | # Load dis and coverage files | |
88 | # | |
89 | open(dis_f, $disfile) | |
90 | || die "Cannot open $disfile: $!\n"; | |
91 | ||
92 | open(cov_f, $covfile) | |
93 | || die "Cannot open $covfile: $!\n"; | |
94 | ||
95 | # | |
96 | # open output files | |
97 | # | |
98 | open(outf_txt, ">$outfile_txt") | |
99 | || die "Cannot open $$outfile_txt: $!\n"; | |
100 | ||
101 | open(outf_html, ">$outfile_html") | |
102 | || die "Cannot open $$outfile_html: $!\n"; | |
103 | ||
104 | # Print header of html and text files | |
105 | print_header(); | |
106 | ||
107 | # | |
108 | # build up an array of the coverage log file output so that | |
109 | # we can search it using the address as an index later | |
110 | # | |
111 | while (<cov_f>) { | |
112 | ($cov_idx,$cov_addr,$cov_cnt,$cov_instn) = split(/:/); | |
113 | $cov_cnt{$cov_addr} = hex($cov_cnt); | |
114 | $cov_instn{$cov_addr} = $cov_instn; | |
115 | } | |
116 | ||
117 | # Accounting for coverage data | |
118 | my $num_addrs = 0; # total number of PC samples | |
119 | my $num_addrs_excuted = 0; # only incr when ref_cnt > 0 | |
120 | ||
121 | my $hv_base_addr = get_hv_base_addr(); | |
122 | printf ("Hypervisor base address is 0x%llx\n", $hv_base_addr); | |
123 | my $hv_addr_offset = 0x0; | |
124 | ||
125 | # | |
126 | # For each line of the dis output, check the coverage array | |
127 | # for a ref_cnt. Then output the data into a new file using | |
128 | # the text output and the html output. | |
129 | # | |
130 | while (<dis_f>) { | |
131 | ||
132 | chomp; | |
133 | next if (!/\s*([^:]+):\s*(.*)$/); | |
134 | ||
135 | $dis_sym = $1; | |
136 | $dis_instn = $2; | |
137 | ||
138 | # Calculate the %pc address for this instruction | |
139 | my $dis_addr += ($hv_base_addr + $hv_addr_offset); | |
140 | $hv_addr_offset += 0x4; | |
141 | ||
142 | # | |
143 | # Map dis-assembly address to a legion address (offset in memory where HV | |
144 | # runs from) so that it can match the coverage data. | |
145 | # | |
146 | switch ($hypervisor) { | |
147 | case "n1" { no warnings; $dis_addr = (($dis_addr - hex('fff0000000')) + hex('400000')); } | |
148 | case "n2" { no warnings; $dis_addr = (($dis_addr - hex('fff0000000')) + hex('100000')); } | |
149 | case "rock" { $dis_addr += (hex('100000')); } | |
150 | else { die "Fatal: need to figure out addr conversion for $hypervisor" } | |
151 | } | |
152 | ||
153 | # | |
154 | # convert the address to a string with "0x" and the hex number | |
155 | # so that we can use it as an index into the coverage data array | |
156 | # | |
157 | my $index_addr = sprintf("0x%llx", $dis_addr); | |
158 | ||
159 | $ref_cnt = 0x0; | |
160 | $ref_cnt = $cov_cnt{$index_addr} if $cov_cnt{$index_addr}; | |
161 | ||
162 | if ($ref_cnt == 0) { | |
163 | # %pc not executed | |
164 | $ref_str = 'NIL'; | |
165 | } else { | |
166 | # %pc executed | |
167 | $ref_str = sprintf("%d", $ref_cnt); | |
168 | $num_addrs_executed += 1; | |
169 | } | |
170 | ||
171 | # | |
172 | # Count number of %pc samples | |
173 | # Ignore illtrap %pcs for coverage purposes | |
174 | # | |
175 | if (($dis_instn =~ /illtrap/) && (!$ref_cnt)) { | |
176 | # Don't count or print illtrap %pcs that have no ref_cnt | |
177 | } else { | |
178 | # count number of %pcs whether they are executed or not | |
179 | $num_addrs += 1; | |
180 | print_row($index_addr, $ref_str, $dis_sym, $dis_instn); | |
181 | } | |
182 | } | |
183 | ||
184 | # | |
185 | # Calculate coverage accounting totals | |
186 | # | |
187 | $percentage = (($num_addrs_executed / ($num_addrs)) * 100); | |
188 | $summary = sprintf ("Summary: Number of valid %%pc values in binary is %llu, coverage is [%.2f%%]\n", | |
189 | $num_addrs, $percentage); | |
190 | printf outf_txt ("%s", $summary); | |
191 | printf outf_html ("<br>%s<br>\n", $summary); | |
192 | printf ("%s", $summary); | |
193 | ||
194 | print_footer(); | |
195 | ||
196 | } #end main | |
197 | ||
198 | ||
199 | ||
200 | sub print_header | |
201 | { | |
202 | print outf_txt "Addr, Ref_Cnt, Label, Instruction (raw, decoded)\n"; | |
203 | ||
204 | print outf_html << "EOF"; | |
205 | <html> | |
206 | <head> | |
207 | <title>Hypervisor Coverage data for $hypervisor</title> | |
208 | </head> | |
209 | <body> | |
210 | <table WIDTH="100%" NOSAVE > | |
211 | EOF | |
212 | } # end print_header | |
213 | ||
214 | sub print_footer | |
215 | { | |
216 | print outf_html << "EOF"; | |
217 | </table> | |
218 | </verbatim> | |
219 | </body> | |
220 | </html> | |
221 | EOF | |
222 | ||
223 | } # end print_footer | |
224 | ||
225 | ||
226 | # | |
227 | # print a row of the table using args passed in | |
228 | # | |
229 | sub print_row | |
230 | { | |
231 | $col_idx = $_[0]; | |
232 | $col_ref_str = $_[1]; | |
233 | $col_sym = $_[2]; | |
234 | $col_instn = $_[3]; | |
235 | ||
236 | # chomp($col_instn); | |
237 | ||
238 | $font_head=""; | |
239 | $font_tail=""; | |
240 | ||
241 | if ($col_ref_str eq 'NIL') { | |
242 | # Mark all NIL lines in red font | |
243 | $font_head= "<font color=red>"; | |
244 | $font_tail= "</font>"; | |
245 | print outf_txt " "; | |
246 | ||
247 | } elsif ($col_ref_str eq 'N/A') { | |
248 | # Print this for each line whose ref_cnt is N/A | |
249 | print outf_txt " "; | |
250 | } else { | |
251 | # Print this for each line whose ref_cnt is non-zero | |
252 | print outf_txt "-->"; | |
253 | } | |
254 | ||
255 | # | |
256 | # output to html file | |
257 | # | |
258 | print outf_html "<tr>\n"; | |
259 | print outf_html "\t<td>$font_head $col_idx $font_tail</td>\n"; | |
260 | print outf_html "\t<td>$font_head $col_ref_str $font_tail</td>\n"; | |
261 | print outf_html "\t<td>$font_head $col_sym $font_tail</td>\n"; | |
262 | print outf_html "\t<td>$font_head $col_instn $font_tail</td>\n"; | |
263 | print outf_html "</tr>"; | |
264 | print outf_html "\n"; | |
265 | ||
266 | # | |
267 | # output to text file | |
268 | # | |
269 | print outf_txt "$col_idx, $col_ref_str, $col_sym, $col_instn\n"; | |
270 | ||
271 | } # end print_row | |
272 | ||
273 | # | |
274 | # Get the base address of the hypervisor using /usr/ccs/bin/nm | |
275 | # to lookup the address of the _START_ symbol | |
276 | # | |
277 | sub get_hv_base_addr | |
278 | { | |
279 | my $returncode = `/usr/ccs/bin/nm -xp q | grep _START_`; | |
280 | ($hv_base_addr_str,$ignore0,$ignore1) = split(/ /, $returncode); | |
281 | no warnings; | |
282 | return hex($hv_base_addr_str); | |
283 | ||
284 | } # end get_hv_base_addr | |
285 | ||
286 | ||
287 | main(); |