Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | # ========== Copyright Header Begin ========================================== |
2 | # | |
3 | # OpenSPARC T2 Processor File: Logfile.pm | |
4 | # Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved | |
5 | # 4150 Network Circle, Santa Clara, California 95054, U.S.A. | |
6 | # | |
7 | # * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
8 | # | |
9 | # This program is free software; you can redistribute it and/or modify | |
10 | # it under the terms of the GNU General Public License as published by | |
11 | # the Free Software Foundation; version 2 of the License. | |
12 | # | |
13 | # This program is distributed in the hope that it will be useful, | |
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | # GNU General Public License for more details. | |
17 | # | |
18 | # You should have received a copy of the GNU General Public License | |
19 | # along with this program; if not, write to the Free Software | |
20 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | # | |
22 | # For the avoidance of doubt, and except that if any non-GPL license | |
23 | # choice is available it will apply instead, Sun elects to use only | |
24 | # the General Public License version 2 (GPLv2) at this time for any | |
25 | # software where a choice of GPL license versions is made | |
26 | # available with the language indicating that GPLv2 or any later version | |
27 | # may be used, or where a choice of which version of the GPL is applied is | |
28 | # otherwise unspecified. | |
29 | # | |
30 | # Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, | |
31 | # CA 95054 USA or visit www.sun.com if you need additional information or | |
32 | # have any questions. | |
33 | # | |
34 | # ========== Copyright Header End ============================================ | |
35 | package AnalyzeDiag::Logfile; | |
36 | ||
37 | use strict; | |
38 | use warnings; | |
39 | use File::Spec; | |
40 | use Math::BigInt; | |
41 | ||
42 | use AnalyzeDiag::Output; | |
43 | ||
44 | use fields qw(filename); | |
45 | ||
46 | ############################################################################### | |
47 | ||
48 | sub create_from_dir { | |
49 | my $class = shift; | |
50 | my $dir = shift; | |
51 | ||
52 | my $vcslog = File::Spec->catfile($dir, 'sim.log'); | |
53 | my $saslog = File::Spec->catfile($dir, 'sas.log'); | |
54 | ||
55 | my $log; | |
56 | if(-e $vcslog) { | |
57 | # RTL Run | |
58 | $log = AnalyzeDiag::Logfile::Rtl->new(); | |
59 | $log->filename($vcslog); | |
60 | } elsif(-e $saslog) { | |
61 | # Simulator Run | |
62 | $log = AnalyzeDiag::Logfile::Mom->new(); | |
63 | $log->filename($saslog); | |
64 | } else { | |
65 | script_die "Cannot find '$vcslog' or '$saslog'.\n"; | |
66 | } | |
67 | return $log; | |
68 | } | |
69 | ||
70 | ############################################################################### | |
71 | ||
72 | sub new { | |
73 | my $this = shift; | |
74 | ||
75 | unless (ref $this) { | |
76 | $this = fields::new($this); | |
77 | } | |
78 | ||
79 | return $this; | |
80 | } | |
81 | ||
82 | ############################################################################### | |
83 | ||
84 | sub filename { | |
85 | my $this = shift; | |
86 | my $filename = shift; | |
87 | $this->{filename} = $filename if defined $filename; | |
88 | return $this->{filename}; | |
89 | } | |
90 | ||
91 | ############################################################################### | |
92 | ||
93 | sub get_cycle_period { | |
94 | my $this = shift; | |
95 | ||
96 | script_die "Can't get_cycle_period on abstract superclass!\n"; | |
97 | } | |
98 | ||
99 | ############################################################################### | |
100 | ||
101 | sub get_times_for_pcs { | |
102 | my $this = shift; | |
103 | my $pclist = shift; | |
104 | ||
105 | my %pchash = map { $_ => [] } @$pclist; | |
106 | my %shortpc = map { $_ => $_ } @$pclist; | |
107 | ||
108 | foreach (values %shortpc) { s/^0+// } | |
109 | %shortpc = reverse %shortpc; | |
110 | ||
111 | debug "get_times_for_pcs: shortpc hash ->\n"; | |
112 | foreach my $pc (keys %shortpc) { | |
113 | debug " short=$pc, long=$shortpc{$pc}\n"; | |
114 | } | |
115 | ||
116 | my $fh = IO::File->new("<$this->{filename}") or | |
117 | script_die "Can't open '$this->{filename}': $!\n"; | |
118 | ||
119 | $this->search_file_for_pcs($fh, \%pchash, \%shortpc); | |
120 | ||
121 | return \%pchash; | |
122 | } | |
123 | ||
124 | ############################################################################### | |
125 | ||
126 | sub search_file_for_pcs { | |
127 | my $this = shift; | |
128 | my $fh = shift; | |
129 | my $pchash = shift; | |
130 | my $shortpc = shift; | |
131 | ||
132 | my $ref = ref $this; | |
133 | script_die "Error: search_file_for_pcs not implemented for $ref!\n"; | |
134 | } | |
135 | ||
136 | ############################################################################### | |
137 | ||
138 | sub subtract_times { | |
139 | my $this = shift; | |
140 | my $arg1 = shift; | |
141 | my $arg2 = shift; | |
142 | ||
143 | if(ref $arg1 and ref $arg2) { | |
144 | script_die "Error: subtract_times args are different lengths!\n" | |
145 | unless @$arg1 == @$arg2; | |
146 | ||
147 | my @results; | |
148 | foreach my $i ( 0 .. (@$arg1 - 1) ) { | |
149 | my $diff = $this->subtract_times($arg1->[$i], $arg2->[$i]); | |
150 | push @results, $diff; | |
151 | } | |
152 | ||
153 | return @results; | |
154 | } elsif(not ref $arg1 and not ref $arg2) { | |
155 | my $bi1 = Math::BigInt->new($arg1); | |
156 | my $bi2 = Math::BigInt->new($arg2); | |
157 | my $diff = $bi1->bsub($bi2)->bstr(); | |
158 | return $diff; | |
159 | } else { | |
160 | script_die "Error: subtract times needs two ref args or two scalar ". | |
161 | "args, not one of each!\n"; | |
162 | } | |
163 | } | |
164 | ||
165 | ############################################################################### | |
166 | ||
167 | sub to_cycles { | |
168 | my $this = shift; | |
169 | my $arg = shift; | |
170 | ||
171 | if(ref $arg) { | |
172 | my @results; | |
173 | foreach my $elem (@$arg) { | |
174 | my $cycles = $this->to_cycles($elem); | |
175 | push @results, $cycles; | |
176 | } | |
177 | return @results; | |
178 | } else { | |
179 | my $period = $this->get_cycle_period(); | |
180 | return $arg if $period == 1; | |
181 | my $num = Math::BigInt->new($arg); | |
182 | my $quotient = Math::BigInt->new('0'); | |
183 | my $remainder = Math::BigInt->new('0'); | |
184 | ($quotient, $remainder) = $num->bdiv($period); | |
185 | if ( $remainder > ($period - $remainder ) ) { | |
186 | $num->binc(); | |
187 | } | |
188 | $num->bstr(); | |
189 | return $num; | |
190 | } | |
191 | } | |
192 | ||
193 | ############################################################################### | |
194 | ############################################################################### | |
195 | ||
196 | package AnalyzeDiag::Logfile::Rtl; | |
197 | ||
198 | use strict; | |
199 | use warnings; | |
200 | ||
201 | use IO::File; | |
202 | ||
203 | use AnalyzeDiag::Output; | |
204 | ||
205 | use base qw(AnalyzeDiag::Logfile); | |
206 | use fields qw(cycle_period); | |
207 | ||
208 | our $Freq_Regex_m1 = qr/Info:\s*Selected Core Clock Frequency\s(\d+)\s*MHz/; | |
209 | our $Pll_Regex_m1 = qr/pll_clk frequency\s*=\s*(\d+)/; | |
210 | our $Cmp_div_Regex_m1 = qr/cmp_clk divider\s*=\s*(\d+)/; | |
211 | our $Cmp_clk_period_Regex_m1 = qr/cmp_clk period\s*=\s*(\d+)/; | |
212 | our $Hex_Regex = qr/[\da-f]+/; | |
213 | ||
214 | ############################################################################### | |
215 | ||
216 | sub new { | |
217 | my $this = shift; | |
218 | ||
219 | $this = fields::new($this); | |
220 | ||
221 | return $this; | |
222 | } | |
223 | ||
224 | ############################################################################### | |
225 | ||
226 | sub get_cycle_period { | |
227 | my $this = shift; | |
228 | return $this->{cycle_period} if defined $this->{cycle_period}; | |
229 | ||
230 | my $fh = IO::File->new("<$this->{filename}") or | |
231 | script_die "Can't open '$this->{filename}': $!\n"; | |
232 | ||
233 | # Used to be this was the only divider, so old versions of the model | |
234 | # didn't print it out. Make it the default so we can handle old and | |
235 | # new. | |
236 | my $cmp_clk_divider = 2; | |
237 | my $pll_freq; | |
238 | ||
239 | while(<$fh>) { | |
240 | if(/$Cmp_clk_period_Regex_m1/) { | |
241 | # I found the period I'm looking for. This trumps everything else. | |
242 | my $period_ps = $1; | |
243 | script_die "Found clock period of 0! in '$this->{filename}'!\n" | |
244 | unless $period_ps; | |
245 | $this->{cycle_period} = $period_ps; | |
246 | chat "Found cycle_period spelled out: $period_ps\n", 2; | |
247 | last; | |
248 | } elsif(/$Pll_Regex_m1/) { | |
249 | $pll_freq = $1; | |
250 | chat "Found PLL frequency: $pll_freq\n", 2; | |
251 | } elsif(/$Cmp_div_Regex_m1/) { | |
252 | $cmp_clk_divider = $1; | |
253 | chat "Found CMP clock divider: $cmp_clk_divider\n", 2; | |
254 | } elsif(/$Freq_Regex_m1/) { | |
255 | # Time's up. Any timing-related messages that haven't shown up | |
256 | # by now aren't coming | |
257 | if(defined $pll_freq) { | |
258 | my $pll_period; | |
259 | # HACK! These are periods hardcoded from cmp_clk.v. | |
260 | # Hopefully, we're using a version of hte model that has the | |
261 | # "cmp_period" defined, so we don't have to use this. | |
262 | my %pll_periods = ( | |
263 | 2000 => 250, | |
264 | 2200 => 227, | |
265 | 2333 => 214, | |
266 | 2334 => 214, # from old sys_clk.v | |
267 | 2400 => 208, | |
268 | 2550 => 196, | |
269 | 2600 => 192, | |
270 | 2800 => 179, | |
271 | default => 208, | |
272 | ); | |
273 | $pll_period = exists $pll_periods{$pll_freq} ? | |
274 | $pll_periods{$pll_freq} : $pll_periods{default}; | |
275 | my $period_ps = $pll_period * 2 * $cmp_clk_divider; | |
276 | $this->{cycle_period} = $period_ps; | |
277 | chat "Computed cycle_period from PLL ($pll_freq) and div ". | |
278 | "($cmp_clk_divider): $period_ps\n", 2; | |
279 | } else { | |
280 | # Couldn't find anything but the frequency message. Use that. | |
281 | my $freq = $1; | |
282 | my $period_ps = int(1 / $freq * 1000000); | |
283 | $this->{cycle_period} = $period_ps; | |
284 | chat "Guessed at cycle_period from frequency ($freq): $period_ps\n"; | |
285 | } | |
286 | last; | |
287 | } | |
288 | } | |
289 | ||
290 | script_die "Could not find clock frequency in '$this->{filename}'\n" | |
291 | unless defined $this->{cycle_period}; | |
292 | ||
293 | return $this->{cycle_period}; | |
294 | } | |
295 | ||
296 | ############################################################################### | |
297 | ||
298 | sub search_file_for_pcs { | |
299 | my $this = shift; | |
300 | my $fh = shift; | |
301 | my $pchash = shift; | |
302 | my $shortpc = shift; | |
303 | ||
304 | my $last_inst; | |
305 | my $this_inst; | |
306 | ||
307 | my @work; | |
308 | ||
309 | ||
310 | while(<$fh>) { | |
311 | # Find time for PC, don't commit it yet since we don't know if it will | |
312 | # be restarted | |
313 | if(/^ \( (\d+) \) Info-perm \s* thread \( $Hex_Regex \) \s* | |
314 | pc \( 0* ($Hex_Regex) \)/ox | |
315 | ) | |
316 | { | |
317 | my ($time, $pc) = ($1, $2); | |
318 | next unless exists $shortpc->{$pc}; | |
319 | ||
320 | my $long_pc = $shortpc->{$pc}; | |
321 | if(exists $pchash->{$long_pc}) { | |
322 | chat " Found pc=$pc, time=$time\n"; | |
323 | push @work, [$long_pc, $time]; | |
324 | } | |
325 | } | |
326 | elsif(/^ \d+ \: pc-MATCH .* val \s* = \s* 0* ($Hex_Regex) \s* $/ox) | |
327 | { | |
328 | ||
329 | my $pc = $1; | |
330 | next unless exists $shortpc->{$pc}; | |
331 | my $long_pc = $shortpc->{$pc}; | |
332 | ||
333 | my $entry = shift @work; | |
334 | script_die "Parse error in log file: Bad pc $entry->[0] ". | |
335 | "(expected $long_pc)!\n" unless $long_pc eq $entry->[0]; | |
336 | ||
337 | if(not defined $last_inst or $last_inst ne 'retry') { | |
338 | my $time = $entry->[1]; | |
339 | chat "PC=$long_pc, time=$time\n"; | |
340 | push @{$pchash->{$long_pc}}, $time; | |
341 | } else { | |
342 | debug "Retry!\n"; | |
343 | } | |
344 | ||
345 | } | |
346 | # Keep track of which instruction was the last one | |
347 | elsif(/^ \d+ \:sas\> \s+ \d+ \: \s+ \[swvp.*\] \s+ | |
348 | \<v\: $Hex_Regex \> \s+ \<p\: $Hex_Regex \> \s+ (\S+)/ox | |
349 | ) | |
350 | { | |
351 | $last_inst = $this_inst; | |
352 | $this_inst = $1; | |
353 | } | |
354 | } | |
355 | ||
356 | } | |
357 | ||
358 | ############################################################################### | |
359 | ############################################################################### | |
360 | ############################################################################### | |
361 | ||
362 | package AnalyzeDiag::Logfile::Mom; | |
363 | ||
364 | use strict; | |
365 | use warnings; | |
366 | ||
367 | use AnalyzeDiag::Output; | |
368 | ||
369 | use base qw(AnalyzeDiag::Logfile); | |
370 | use fields qw(); | |
371 | ||
372 | ############################################################################### | |
373 | ||
374 | sub new { | |
375 | my $this = shift; | |
376 | ||
377 | $this = fields::new($this); | |
378 | ||
379 | return $this; | |
380 | } | |
381 | ||
382 | ############################################################################### | |
383 | ||
384 | sub get_cycle_period { | |
385 | my $this = shift; | |
386 | return 1; | |
387 | } | |
388 | ||
389 | ############################################################################### | |
390 | ||
391 | sub search_file_for_pcs { | |
392 | my $this = shift; | |
393 | my $fh = shift; | |
394 | my $pchash = shift; | |
395 | my $shortpc = shift; | |
396 | ||
397 | my $last_inst; | |
398 | my $this_inst; | |
399 | ||
400 | my @work; | |
401 | ||
402 | ||
403 | while(<$fh>) { | |
404 | ||
405 | if(/^ \@(\d+): \s+ \d+ \.pc \= ($Hex_Regex) \s+ \[ .* \s+ latency=\d+ \s+ | |
406 | (\S+) /ox) | |
407 | { | |
408 | my ($time, $pc, $this_inst) = ($1, $2, $3); | |
409 | $pc =~ s/^0+//; | |
410 | ||
411 | next unless exists $shortpc->{$pc}; | |
412 | my $long_pc = $shortpc->{$pc}; | |
413 | chat "FOUND PC=$pc\n"; | |
414 | chat " Last inst = $last_inst\n" if defined $last_inst; | |
415 | if(not defined $last_inst or $last_inst ne 'retry') { | |
416 | chat "PC=$long_pc, time=$time\n"; | |
417 | push @{$pchash->{$long_pc}}, $time; | |
418 | } | |
419 | ||
420 | $last_inst = $this_inst; | |
421 | ||
422 | } | |
423 | ||
424 | } | |
425 | ||
426 | } | |
427 | ||
428 | ############################################################################### | |
429 | ############################################################################### | |
430 | 1; |