line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
4
|
|
|
4
|
|
3668
|
use strict; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
170
|
|
2
|
4
|
|
|
4
|
|
22
|
use warnings; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
142
|
|
3
|
4
|
|
|
4
|
|
107
|
use 5.012; |
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
139
|
|
4
|
4
|
|
|
4
|
|
4649
|
use autodie; |
|
4
|
|
|
|
|
99298
|
|
|
4
|
|
|
|
|
30
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Text::Cadenceparser; |
7
|
|
|
|
|
|
|
{ |
8
|
|
|
|
|
|
|
$Text::Cadenceparser::VERSION = '1.12'; |
9
|
|
|
|
|
|
|
} |
10
|
|
|
|
|
|
|
|
11
|
4
|
|
|
4
|
|
25159
|
use Carp qw/croak carp/; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
338
|
|
12
|
4
|
|
|
4
|
|
12970
|
use Data::Dumper; |
|
4
|
|
|
|
|
21220
|
|
|
4
|
|
|
|
|
316
|
|
13
|
|
|
|
|
|
|
|
14
|
4
|
|
|
4
|
|
34
|
use constant DEBUG => $ENV{TEXT_CADENCEPARSER_DEBUG}; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
329
|
|
15
|
4
|
|
|
|
|
17280
|
use constant DEBUG1 => |
16
|
4
|
|
|
4
|
|
20
|
$ENV{TEXT_CADENCEPARSER_DEBUG1}; # more verbose debugging info |
|
4
|
|
|
|
|
7
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub new { |
20
|
11
|
|
|
11
|
1
|
8719
|
my ( $pkg, %p ) = @_; |
21
|
|
|
|
|
|
|
|
22
|
11
|
|
|
|
|
73
|
my $self = bless { |
23
|
|
|
|
|
|
|
_msg => {}, # Internal message hash, will store the report results |
24
|
|
|
|
|
|
|
_files_parsed => 0, # Number of files parsed |
25
|
|
|
|
|
|
|
%p |
26
|
|
|
|
|
|
|
}, $pkg; |
27
|
|
|
|
|
|
|
|
28
|
11
|
100
|
|
|
|
61
|
if ( defined $self->{folder} ) { |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# When folder is defined, then we need to produce a synthesis report synopsis |
31
|
3
|
|
|
|
|
10
|
$self->{_files_parsed} = $self->_read_logfiles(); # Gather the data |
32
|
|
|
|
|
|
|
} else { |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# Gather file info for displaying area/power |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# First check the input parameters |
37
|
|
|
|
|
|
|
# Key is required |
38
|
8
|
50
|
66
|
|
|
90
|
if ( |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
39
|
|
|
|
|
|
|
!defined $self->{key} |
40
|
|
|
|
|
|
|
|| ( $self->{key} ne 'area' |
41
|
|
|
|
|
|
|
&& $self->{key} ne 'active' |
42
|
|
|
|
|
|
|
&& $self->{key} ne 'leakage' ) |
43
|
|
|
|
|
|
|
) |
44
|
|
|
|
|
|
|
{ |
45
|
0
|
|
|
|
|
0
|
croak |
46
|
|
|
|
|
|
|
"'key' is a required input parameter and should be 'area', 'active' or 'leakage'"; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# Threshold not, defaults to 1 if it is not defined |
50
|
8
|
|
100
|
|
|
43
|
$self->{threshold} = $self->{threshold} || 1; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# Sanity check on input files based on the sort key |
53
|
8
|
50
|
66
|
|
|
90
|
if ( $self->{key} eq 'area' && ( !defined $self->{area_rpt} ) ) { |
54
|
0
|
|
|
|
|
0
|
croak |
55
|
|
|
|
|
|
|
"Please specify an area report file if you want to sort according to area"; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
8
|
50
|
66
|
|
|
63
|
if ( ( $self->{key} eq 'active' || $self->{key} eq 'leakage' ) |
|
|
|
66
|
|
|
|
|
59
|
|
|
|
|
|
|
&& ( !defined $self->{power_rpt} ) ) |
60
|
|
|
|
|
|
|
{ |
61
|
0
|
|
|
|
|
0
|
croak |
62
|
|
|
|
|
|
|
"Please specify a power report file if you want to sort according to power numbers"; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# Read the reports |
66
|
8
|
|
|
|
|
23
|
$self->{_files_parsed} = $self->_read_reports(); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# And sort the results |
69
|
7
|
|
|
|
|
48
|
$self->_sort_data(); |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
10
|
|
|
|
|
93
|
return $self; |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
|
77
|
2
|
|
|
2
|
1
|
995
|
sub files_parsed { shift->{_files_parsed} } |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
sub count { |
80
|
3
|
|
|
3
|
1
|
1554
|
my ( $self, $type ) = @_; |
81
|
3
|
|
|
|
|
5
|
my $count = keys %{ $self->{_msg}->{$type} }; |
|
3
|
|
|
|
|
12
|
|
82
|
3
|
|
|
|
|
7
|
return $count; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub overview { |
87
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $type ) = @_; |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# Report slack |
90
|
0
|
0
|
|
|
|
0
|
if ( $self->{_slack}->{_negative} ) { |
91
|
0
|
|
|
|
|
0
|
print "ERROR: "; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
0
|
|
|
|
|
0
|
foreach my $clock ( keys %{ $self->{_slack} } ) { |
|
0
|
|
|
|
|
0
|
|
95
|
0
|
0
|
|
|
|
0
|
next if ( $clock eq '_negative' ); # Skip housekeeping variable |
96
|
0
|
|
|
|
|
0
|
my $slack = $self->{_slack}->{$clock}->{slack}; |
97
|
0
|
|
0
|
|
|
0
|
my $violators = $self->{_slack}->{$clock}->{violators} || 'no'; |
98
|
0
|
|
|
|
|
0
|
say "Clock '$clock' slack is $slack ps. (# $violators nets);"; |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
0
|
|
|
|
|
0
|
say "-------------"; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# Report info/warning/errors |
104
|
0
|
|
|
|
|
0
|
my @types = ( 'info', 'warning', 'error' ); |
105
|
|
|
|
|
|
|
|
106
|
0
|
|
|
|
|
0
|
foreach my $type (@types) { |
107
|
0
|
|
|
|
|
0
|
my $count = keys %{ $self->{_msg}->{$type} }; |
|
0
|
|
|
|
|
0
|
|
108
|
0
|
|
|
|
|
0
|
say "$count '$type' messages found"; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
0
|
|
|
|
|
0
|
say "-------------"; |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
sub get { |
116
|
36
|
|
|
36
|
1
|
1721
|
my ( $self, $type ) = @_; |
117
|
|
|
|
|
|
|
|
118
|
36
|
50
|
|
|
|
191
|
return $self->{_msg}->{$type} if ( $type ~~ [qw(info warning error)] ); |
119
|
36
|
100
|
|
|
|
912
|
return $self->{_data}->{root}->{$type}->{total} |
120
|
|
|
|
|
|
|
if ( $type ~~ [qw(area active leakage)] ); |
121
|
2
|
|
|
|
|
6
|
return $self->{$type}; # Enable self-checking of parameters in tests |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
sub list { |
125
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $type ) = @_; |
126
|
|
|
|
|
|
|
|
127
|
0
|
|
|
|
|
0
|
my $messages = $self->get($type); |
128
|
|
|
|
|
|
|
|
129
|
0
|
0
|
|
|
|
0
|
say "* Detected $type messages:" if ( keys %{$messages} ); |
|
0
|
|
|
|
|
0
|
|
130
|
|
|
|
|
|
|
|
131
|
0
|
|
|
|
|
0
|
foreach my $key ( keys %{$messages} ) { |
|
0
|
|
|
|
|
0
|
|
132
|
0
|
|
|
|
|
0
|
$self->_nice_print( $key, $messages->{$key} ); |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub slack { |
137
|
1
|
|
|
1
|
1
|
489
|
my ( $self, $clock ) = @_; |
138
|
|
|
|
|
|
|
|
139
|
1
|
|
|
|
|
5
|
return $self->{_slack}->{$clock}->{slack}; |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub report { |
144
|
5
|
|
|
5
|
1
|
8307
|
my ( $self, %p ) = @_; |
145
|
|
|
|
|
|
|
|
146
|
5
|
|
|
|
|
14
|
my $data = $self->{_data}; |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# First report the totals |
149
|
5
|
100
|
|
|
|
13
|
say "Total area : " . $self->get('area') . " um2" |
150
|
|
|
|
|
|
|
if ( $self->get('area') ); |
151
|
5
|
100
|
|
|
|
21
|
say "Total active : " . $self->get('active') . " mW" |
152
|
|
|
|
|
|
|
if ( $self->get('active') ); |
153
|
5
|
100
|
|
|
|
21
|
say "Total leakage: " . $self->get('leakage') . " mW" |
154
|
|
|
|
|
|
|
if ( $self->get('leakage') ); |
155
|
|
|
|
|
|
|
|
156
|
5
|
|
|
|
|
33
|
say "% : " . $self->_format_str("Name") . "\tArea\tActive\tLeakage "; |
157
|
5
|
|
|
|
|
16
|
say " " . $self->_format_str("") . "\t(um2)\t(mW)\t(mW)"; |
158
|
5
|
|
|
|
|
137
|
say "----------------------------------------------------------------"; |
159
|
|
|
|
|
|
|
|
160
|
5
|
|
|
|
|
11
|
foreach my $procent ( sort { $b <=> $a } keys %{$data->{detail}} ) { |
|
55
|
|
|
|
|
84
|
|
|
5
|
|
|
|
|
39
|
|
161
|
30
|
|
|
|
|
40
|
foreach my $item ( sort keys %{$data->{detail}->{$procent}} ) { |
|
30
|
|
|
|
|
256
|
|
162
|
234
|
|
|
|
|
620
|
my $leaf = $data->{detail}->{$procent}->{$item}; |
163
|
234
|
|
100
|
|
|
604
|
my $area = $leaf->{area} || '--'; |
164
|
234
|
|
100
|
|
|
633
|
my $active = $leaf->{active} || '--'; |
165
|
234
|
|
100
|
|
|
650
|
my $leak = $leaf->{leakage} || '--'; |
166
|
|
|
|
|
|
|
|
167
|
234
|
|
|
|
|
465
|
say $self->_format_int($procent) . " : " |
168
|
|
|
|
|
|
|
. $self->_format_str( $leaf->{name} ) |
169
|
|
|
|
|
|
|
. "\t$area\t$active\t$leak"; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# Output formatting in case the value is zero (file not read) |
174
|
5
|
|
100
|
|
|
43
|
my $stash_area = $data->{stash}->{area} || '--'; |
175
|
5
|
|
100
|
|
|
29
|
my $stash_active = $data->{stash}->{active} || '--'; |
176
|
5
|
|
100
|
|
|
25
|
my $stash_leakage = $data->{stash}->{leakage} || '--'; |
177
|
|
|
|
|
|
|
|
178
|
5
|
|
|
|
|
20
|
say $self->_format_int( $data->{stash}->{percent} ) . " : " |
179
|
|
|
|
|
|
|
. $self->_format_str('') |
180
|
|
|
|
|
|
|
. "\t$stash_area\t$stash_active\t$stash_leakage"; |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub get_final { |
185
|
3
|
|
|
3
|
1
|
1076
|
my ( $self, $key) = @_; |
186
|
|
|
|
|
|
|
|
187
|
3
|
|
|
|
|
14
|
return $self->{_final}->{$key}; |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# Private function to display a single message to STDOUT |
192
|
|
|
|
|
|
|
sub _nice_print { |
193
|
0
|
|
|
0
|
|
0
|
my ( $self, $key, $message ) = @_; |
194
|
|
|
|
|
|
|
|
195
|
0
|
|
|
|
|
0
|
my $count = $message->{count}; |
196
|
0
|
|
|
|
|
0
|
my $msg = $message->{message}; |
197
|
|
|
|
|
|
|
|
198
|
0
|
|
|
|
|
0
|
printf "%s\t%4i %s\n", $key, $count, $msg; |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# Internal function to read the logfiles from the source folder and place the |
203
|
|
|
|
|
|
|
# messages into the data hash |
204
|
|
|
|
|
|
|
sub _read_logfiles { |
205
|
|
|
|
|
|
|
|
206
|
3
|
|
|
3
|
|
7
|
my ( $self, %p ) = @_; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# Glob all relevant logfiles (=gather their names) |
209
|
3
|
|
|
|
|
6
|
my $foldername = $self->{folder}; |
210
|
|
|
|
|
|
|
|
211
|
3
|
50
|
|
|
|
136
|
if ( !-e $foldername ) { |
212
|
0
|
|
|
|
|
0
|
croak "Result folder '$foldername' does not exist"; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
3
|
|
|
|
|
381
|
my @files = glob( $foldername . "/*.summary" ); |
216
|
|
|
|
|
|
|
|
217
|
3
|
|
|
|
|
8
|
print "Going to parse files from folder '$foldername': \n" . Dumper(@files) |
218
|
|
|
|
|
|
|
if DEBUG1; |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# Go over the files one by one and extract info |
221
|
3
|
|
|
|
|
9
|
foreach my $file (@files) { |
222
|
6
|
|
|
|
|
16
|
$self->_gather_entries($file); |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
3
|
|
|
|
|
5
|
print "Found: \n" . Dumper( $self->{_msg} ) if DEBUG; |
226
|
|
|
|
|
|
|
|
227
|
3
|
|
|
|
|
146
|
my @timing_logs = glob( $foldername . "/report_qor_map_*" ); |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# Go over the files one by one and extract slack info |
230
|
3
|
|
|
|
|
10
|
foreach my $file (@timing_logs) { |
231
|
1
|
|
|
|
|
4
|
$self->_gather_slack($file); |
232
|
1
|
|
|
|
|
4
|
push @files, $file; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
3
|
|
|
|
|
8
|
my $final_file = $foldername . "/final.rpt"; |
236
|
|
|
|
|
|
|
|
237
|
3
|
100
|
|
|
|
51
|
if (-e $foldername . "/final.rpt") { |
238
|
2
|
|
|
|
|
6
|
$self->_gather_final($final_file); |
239
|
2
|
|
|
|
|
4
|
push @files, $final_file; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
3
|
|
|
|
|
12
|
return scalar @files; |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# Internal function to read the power/area reports |
247
|
|
|
|
|
|
|
sub _read_reports { |
248
|
8
|
|
|
8
|
|
19
|
my ( $self, %p ) = @_; |
249
|
|
|
|
|
|
|
|
250
|
8
|
|
|
|
|
12
|
my @files; |
251
|
|
|
|
|
|
|
|
252
|
8
|
100
|
|
|
|
37
|
push @files, $self->_parse_area() if ( defined $self->{area_rpt} ); |
253
|
8
|
100
|
|
|
|
52
|
push @files, $self->_parse_power() if ( defined $self->{power_rpt} ); |
254
|
|
|
|
|
|
|
|
255
|
7
|
|
|
|
|
36
|
return @files; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# Parse the area log report, expecting Encounter RTL compiler file |
259
|
|
|
|
|
|
|
sub _parse_area { |
260
|
6
|
|
|
6
|
|
11
|
my ( $self, %p ) = @_; |
261
|
|
|
|
|
|
|
|
262
|
6
|
|
|
|
|
13
|
my $filename = $self->{area_rpt}; |
263
|
|
|
|
|
|
|
|
264
|
6
|
50
|
|
|
|
305
|
open my $fh, "<", $filename |
265
|
|
|
|
|
|
|
or die "Could not open $filename: $!"; |
266
|
|
|
|
|
|
|
|
267
|
6
|
|
|
|
|
1176
|
say "Parsing area report $filename"; |
268
|
|
|
|
|
|
|
|
269
|
6
|
|
|
|
|
14
|
my $line; |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# Skip until we enter the 'data' zone |
272
|
6
|
|
|
|
|
164
|
while (<$fh>) { |
273
|
90
|
|
|
|
|
96
|
$line = $_; |
274
|
90
|
100
|
|
|
|
265
|
if ( $line =~ /\-{5}/ ) { |
275
|
6
|
|
|
|
|
12
|
last; |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# Now parse :-) |
280
|
6
|
|
|
|
|
11
|
my $regexp = '(\w+)\s+\w+\s+\d+\s+(\d+)'; |
281
|
|
|
|
|
|
|
|
282
|
6
|
|
|
|
|
24
|
while (<$fh>) { |
283
|
8496
|
|
|
|
|
10857
|
$line = $_; |
284
|
8496
|
100
|
|
|
|
29183
|
if ( $line =~ /^$regexp/ ) { |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
#say "root: $1 \t$2"; |
287
|
6
|
|
|
|
|
32
|
$self->{_data}->{root}->{name} = $1; |
288
|
6
|
|
|
|
|
24
|
$self->{_data}->{root}->{area}->{total} = $2; |
289
|
6
|
|
|
|
|
19
|
$self->{_data}->{root}->{area}->{sum_leaves} = 0; |
290
|
|
|
|
|
|
|
} |
291
|
8496
|
100
|
|
|
|
48268
|
if ( $line =~ /^\s\s$regexp/ ) { |
292
|
654
|
|
|
|
|
1061
|
my $partial_area = $2; |
293
|
654
|
|
|
|
|
4025
|
$self->{_data}->{leaf}->{$1}->{area} = $partial_area; |
294
|
654
|
|
|
|
|
3255
|
$self->{_data}->{root}->{area}->{sum_leaves} += $partial_area; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
6
|
|
|
|
|
136
|
close $fh; |
299
|
|
|
|
|
|
|
|
300
|
6
|
|
|
|
|
60
|
return $filename; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# Parse the power log report, expecting Encouter output file |
305
|
|
|
|
|
|
|
sub _parse_power { |
306
|
7
|
|
|
7
|
|
19
|
my ( $self, %p ) = @_; |
307
|
|
|
|
|
|
|
|
308
|
7
|
|
|
|
|
16
|
my $filename = $self->{power_rpt}; |
309
|
|
|
|
|
|
|
|
310
|
7
|
50
|
|
|
|
342
|
open my $fh, "<", $filename |
311
|
|
|
|
|
|
|
or die "Could not open $filename: $!"; |
312
|
|
|
|
|
|
|
|
313
|
7
|
|
|
|
|
1157
|
say "Parsing power report $filename"; |
314
|
|
|
|
|
|
|
|
315
|
7
|
|
|
|
|
18
|
my $line; |
316
|
|
|
|
|
|
|
|
317
|
7
|
|
|
|
|
14
|
my $file_type = 'normal'; |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# Skip until we enter the 'data' zone |
320
|
7
|
|
|
|
|
146
|
while (<$fh>) { |
321
|
75
|
|
|
|
|
90
|
$line = $_; |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
# Detect if we're reading a file in normal output mode or in verbose mode |
324
|
75
|
100
|
|
|
|
154
|
$file_type = 'verbose' if (/Leakage\s+Internal\s+Net/); |
325
|
|
|
|
|
|
|
|
326
|
75
|
100
|
|
|
|
230
|
if ( $line =~ /\-{5}/ ) { |
327
|
6
|
|
|
|
|
10
|
last; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
# Now parse :-) |
332
|
|
|
|
|
|
|
# Regexp for normal mode parsing |
333
|
7
|
|
|
|
|
16
|
my $regexp = '(\w+)\s+\w+\s+\d+\s+([0-9]*\.?[0-9]+)\s+([0-9]*\.?[0-9]+)'; |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# In case the file is verbose mode output then we need another regexp! |
336
|
7
|
100
|
|
|
|
20
|
$regexp = '(\w+)\s+\w+\s+\d+\s+([0-9]*\.?[0-9]+)\s+[0-9]*\.?[0-9]+\s+[0-9]*\.?[0-9]+\s+([0-9]*\.?[0-9]+)' if ($file_type eq 'verbose'); |
337
|
|
|
|
|
|
|
|
338
|
7
|
|
|
|
|
30
|
while (<$fh>) { |
339
|
5676
|
|
|
|
|
6636
|
$line = $_; |
340
|
5676
|
100
|
|
|
|
18980
|
if ( $line =~ /^$regexp/ ) { |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
# TODO check for same root name here as a test to see if area and power reports match. |
343
|
|
|
|
|
|
|
#say "root: $1 \t$2"; |
344
|
6
|
|
|
|
|
29
|
$self->{_data}->{root}->{name} = $1; |
345
|
6
|
|
|
|
|
32
|
$self->{_data}->{root}->{leakage}->{total} = $2; |
346
|
6
|
|
|
|
|
25
|
$self->{_data}->{root}->{active}->{total} = $3; |
347
|
6
|
|
|
|
|
19
|
$self->{_data}->{root}->{active}->{sum_leaves} = 0; |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
5676
|
100
|
|
|
|
29792
|
if ( $line =~ /^\s\s$regexp/ ) { |
352
|
442
|
|
|
|
|
1833
|
$self->{_data}->{leaf}->{$1}->{leakage} = $2; |
353
|
442
|
|
|
|
|
1593
|
$self->{_data}->{leaf}->{$1}->{active} = $3; |
354
|
442
|
|
|
|
|
1116
|
$self->{_data}->{root}->{leakage}->{sum_leaves} += $2; |
355
|
442
|
|
|
|
|
2222
|
$self->{_data}->{root}->{active}->{sum_leaves} += $3; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
7
|
|
|
|
|
90
|
close $fh; |
360
|
|
|
|
|
|
|
|
361
|
7
|
100
|
|
|
|
86
|
croak "Power input report '$filename' was empty, please check it." if (!defined $self->{_data}->{root}->{active}->{total}); |
362
|
|
|
|
|
|
|
|
363
|
6
|
|
|
|
|
46
|
return $filename; |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
# This function gathers the entries from a single logfile and |
368
|
|
|
|
|
|
|
# puts them in the $msg hash |
369
|
|
|
|
|
|
|
sub _gather_entries { |
370
|
6
|
|
|
6
|
|
14
|
my ( $self, $fname ) = @_; |
371
|
|
|
|
|
|
|
|
372
|
6
|
|
|
|
|
7
|
print "Gathering messages in file '$fname'\n" if DEBUG; |
373
|
|
|
|
|
|
|
|
374
|
6
|
50
|
|
|
|
660
|
open my $fh, '<', $fname |
375
|
|
|
|
|
|
|
or croak "Could not open file '$fname' for reading: $!"; |
376
|
|
|
|
|
|
|
|
377
|
6
|
|
|
|
|
12
|
my $type; |
378
|
|
|
|
|
|
|
my $code; |
379
|
|
|
|
|
|
|
|
380
|
6
|
|
|
|
|
69
|
SKIP_HEADER: while (<$fh>) { |
381
|
28
|
100
|
|
|
|
91
|
last if (/-----/); |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
6
|
|
|
|
|
26
|
PARSE_ENTRIES: while ( my $line = <$fh> ) { |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# Typical line we're looking for looks like this: |
387
|
|
|
|
|
|
|
# ' 2 Warning ENC-6 Problems detected during configuration file' |
388
|
90
|
100
|
|
|
|
381
|
if ( $line =~ /^\s*(\d+)\s(\w+)\s([-\w]+)\s+(.+\S)\s+/ ) { |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# When we encounter such line, make a new entry in the message hash |
391
|
20
|
|
|
|
|
39
|
my $count = $1; |
392
|
20
|
|
|
|
|
35
|
$type = lc($2); |
393
|
20
|
|
|
|
|
35
|
$code = $3; |
394
|
20
|
|
|
|
|
28
|
my $message = $4; |
395
|
20
|
|
|
|
|
24
|
print "$count -- $type -- $code -- $message\n" if DEBUG1; |
396
|
20
|
|
|
|
|
72
|
$self->{_msg}->{$type}->{$code}->{message} = $message; |
397
|
20
|
|
|
|
|
52
|
$self->{_msg}->{$type}->{$code}->{count} = $count; |
398
|
20
|
|
|
|
|
68
|
next; |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
|
401
|
70
|
100
|
|
|
|
254
|
if ( $line =~ /\s*(\S.+\S)\s+/ ) { |
402
|
67
|
50
|
|
|
|
116
|
croak "Parsing error: found text before info line in '$fname'" |
403
|
|
|
|
|
|
|
if ( !defined $type ); |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# Append other lines to the last seen message |
406
|
67
|
|
|
|
|
328
|
$self->{_msg}->{$type}->{$code}->{message} .= " $1"; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
6
|
|
|
|
|
75
|
close $fh; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
# This function gathers the slack entries from a single logfile and |
415
|
|
|
|
|
|
|
# puts them in the $msg hash |
416
|
|
|
|
|
|
|
sub _gather_slack { |
417
|
1
|
|
|
1
|
|
3
|
my ( $self, $fname ) = @_; |
418
|
|
|
|
|
|
|
|
419
|
1
|
|
|
|
|
2
|
print "Gathering messages in file '$fname'\n" if DEBUG; |
420
|
|
|
|
|
|
|
|
421
|
1
|
50
|
|
|
|
34
|
open my $fh, '<', $fname |
422
|
|
|
|
|
|
|
or croak "Could not open file '$fname' for reading: $!"; |
423
|
|
|
|
|
|
|
|
424
|
1
|
|
|
|
|
13
|
SKIP_HEADER: while (<$fh>) { |
425
|
24
|
100
|
|
|
|
59
|
last if (/Slack/); |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# Skip next line |
429
|
1
|
|
|
|
|
2
|
my $line = <$fh>; |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
# Next one is the one we need |
432
|
1
|
|
|
|
|
2
|
$line = <$fh>; |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
# PARSE_ENTRIES: while ( $line = <$fh> ) { |
435
|
1
|
50
|
|
|
|
7
|
if ( $line =~ /\w+\s+(\w+)\s+(-?\d+.?\d*)\s+(\d*)\s/ ) { |
436
|
1
|
|
|
|
|
28
|
my $group = $1; |
437
|
1
|
|
|
|
|
3
|
my $slack = $2; |
438
|
1
|
|
|
|
|
2
|
my $nr_paths = $3; |
439
|
|
|
|
|
|
|
|
440
|
1
|
|
|
|
|
5
|
$self->{_slack}->{$group}->{slack} = $slack; |
441
|
1
|
|
|
|
|
5
|
$self->{_slack}->{$group}->{violators} = $nr_paths; |
442
|
|
|
|
|
|
|
|
443
|
1
|
50
|
|
|
|
9
|
if ( $slack < 0 ) { |
444
|
1
|
|
|
|
|
7
|
$self->{_slack}->{_negative} = 1; |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
} else { |
447
|
0
|
|
|
|
|
0
|
carp "Warning: could not extract slack information from logfiles"; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
# } |
451
|
|
|
|
|
|
|
|
452
|
1
|
|
|
|
|
15
|
close $fh; |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
# Extract the information from the final.rpt file |
457
|
|
|
|
|
|
|
sub _gather_final { |
458
|
2
|
|
|
2
|
|
4
|
my ( $self, $fname ) = @_; |
459
|
|
|
|
|
|
|
|
460
|
2
|
|
|
|
|
4
|
print "Gathering messages in file '$fname'\n" if DEBUG; |
461
|
|
|
|
|
|
|
|
462
|
2
|
50
|
|
|
|
80
|
open my $fh, '<', $fname |
463
|
|
|
|
|
|
|
or croak "Could not open file '$fname' for reading: $!"; |
464
|
|
|
|
|
|
|
|
465
|
2
|
|
|
|
|
3
|
my $match_col; |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
my $line; |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# We want to know in what column the total ('final') data is present. |
470
|
|
|
|
|
|
|
# We need to autodetect this because it differes depending on the flow type that was run |
471
|
2
|
|
|
|
|
41
|
DETECT_COLUMN: while ($line = <$fh>) { |
472
|
12
|
100
|
|
|
|
40
|
if ($line =~ /Metric/) { |
473
|
2
|
|
|
|
|
12
|
my @columns = split /\s+/, $line; |
474
|
|
|
|
|
|
|
# Use an index hash to find what the index is of the column we're looking for |
475
|
2
|
|
|
|
|
4
|
my %indhash; |
476
|
2
|
|
|
|
|
17
|
@indhash{@columns} = (0 .. $#columns); |
477
|
|
|
|
|
|
|
|
478
|
2
|
|
|
|
|
5
|
$match_col = $indhash{'final'}; |
479
|
|
|
|
|
|
|
|
480
|
2
|
|
|
|
|
7
|
last; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
# Data begins until next empty line |
485
|
2
|
|
|
|
|
18
|
FETCH_DATA : while ($line = <$fh>) { |
486
|
62
|
100
|
|
|
|
151
|
if ($line =~ /====/) { |
|
|
100
|
|
|
|
|
|
487
|
|
|
|
|
|
|
# Skip separator lines |
488
|
6
|
|
|
|
|
24
|
next; |
489
|
|
|
|
|
|
|
} elsif ($line eq "\n") { |
490
|
|
|
|
|
|
|
# Stop processing on empty line beacuse we need to switch the handling of the data from here on (other format) |
491
|
2
|
|
|
|
|
3
|
last; |
492
|
|
|
|
|
|
|
} else { |
493
|
|
|
|
|
|
|
# Data -> process it |
494
|
|
|
|
|
|
|
# First the metric (everything before the :) |
495
|
54
|
|
|
|
|
136
|
my @data = split /:/, $line; |
496
|
54
|
|
|
|
|
91
|
my $metric = $data[0]; |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
# Remove leading spaces in the metric; |
499
|
54
|
|
|
|
|
96
|
$metric =~ s/^\s+//; |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
# Then the values |
502
|
54
|
|
|
|
|
175
|
my @columns = split /\s+/, $data[1]; |
503
|
|
|
|
|
|
|
|
504
|
54
|
|
|
|
|
78
|
my $value = $columns[$match_col]; |
505
|
|
|
|
|
|
|
|
506
|
54
|
|
|
|
|
270
|
$self->{_final}->{$metric} = $value; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
# TODO Check if we need to strip the untis from the metric and put them on another place in the hash. |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# skip 3 lines |
513
|
|
|
|
|
|
|
#$line = <$fh>; |
514
|
|
|
|
|
|
|
#$line = <$fh>; |
515
|
|
|
|
|
|
|
#$line = <$fh>; |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
# TODO Fetch the totals and store them too. |
518
|
|
|
|
|
|
|
#PARSE_TOTALS: while ($line = <$fh>) { |
519
|
|
|
|
|
|
|
# if ($line =~ /^([^:]):\s+(\.+)/) { |
520
|
|
|
|
|
|
|
# $self->{_final}->{$1} = $2; |
521
|
|
|
|
|
|
|
# } |
522
|
|
|
|
|
|
|
#} |
523
|
|
|
|
|
|
|
|
524
|
2
|
|
|
|
|
35
|
close $fh; |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
# Nicely print a string |
528
|
|
|
|
|
|
|
sub _format_str { |
529
|
249
|
|
|
249
|
|
340
|
my ( $self, $val ) = @_; |
530
|
249
|
|
|
|
|
360
|
my $len = $self->{_presentation}->{namelength}; |
531
|
249
|
|
|
|
|
13110
|
return sprintf( "%-" . $len . "s", $val ); |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
# nicely print an int |
535
|
|
|
|
|
|
|
sub _format_int { |
536
|
239
|
|
|
239
|
|
350
|
my ( $self, $val ) = @_; |
537
|
239
|
|
100
|
|
|
1021
|
$val = $val || 0; # Catch cases where the val is not initialized |
538
|
239
|
|
|
|
|
960
|
return sprintf( "%2i", $val ); |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
# Sort the data according to the passed key |
542
|
|
|
|
|
|
|
sub _sort_data { |
543
|
|
|
|
|
|
|
|
544
|
7
|
|
|
7
|
|
21
|
my ( $self, %p ) = @_; |
545
|
|
|
|
|
|
|
|
546
|
7
|
|
|
|
|
19
|
my $key = $self->{key}; |
547
|
7
|
|
|
|
|
16
|
my $threshold = $self->{threshold}; |
548
|
|
|
|
|
|
|
|
549
|
7
|
|
|
|
|
35
|
my $total = $self->get($key); |
550
|
7
|
|
|
|
|
35
|
my $threshold_abs = $total * $threshold / 100; |
551
|
|
|
|
|
|
|
|
552
|
7
|
100
|
|
|
|
31
|
my $unit = $self->{key} eq 'area' ? 'um2' : 'mW'; |
553
|
|
|
|
|
|
|
|
554
|
7
|
|
|
|
|
1945
|
say "Sorting on '$key'"; |
555
|
|
|
|
|
|
|
|
556
|
7
|
|
|
|
|
40
|
$self->{_presentation}->{namelength} = 0; |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
# Insert an entry for the toplevel so that it get reported if required |
559
|
7
|
|
|
|
|
12
|
my ($top_area, $top_active, $top_leakage); |
560
|
7
|
100
|
|
|
|
54
|
$top_area = $self->{_data}->{root}->{area}->{total} - $self->{_data}->{root}->{area}->{sum_leaves} if (defined $self->{_data}->{root}->{area}); |
561
|
7
|
100
|
|
|
|
51
|
$top_active = $self->{_data}->{root}->{active}->{total} - $self->{_data}->{root}->{active}->{sum_leaves} if (defined $self->{_data}->{root}->{active}); |
562
|
7
|
100
|
|
|
|
45
|
$top_leakage = $self->{_data}->{root}->{leakage}->{total} - $self->{_data}->{root}->{leakage}->{sum_leaves} if (defined $self->{_data}->{root}->{leakage}); |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
# Ensure the right format is used |
565
|
7
|
100
|
|
|
|
47
|
$top_area = sprintf("%d", $top_area) if (defined $top_area); |
566
|
7
|
100
|
|
|
|
91
|
$top_active = sprintf("%1.3f", $top_active) if (defined $top_active); |
567
|
7
|
100
|
|
|
|
36
|
$top_leakage = sprintf("%1.3f", $top_leakage) if (defined $top_leakage); |
568
|
|
|
|
|
|
|
|
569
|
7
|
|
|
|
|
32
|
$self->{_data}->{leaf}->{'toplevel'}->{area} = $top_area; |
570
|
7
|
|
|
|
|
100
|
$self->{_data}->{leaf}->{'toplevel'}->{active} = $top_active; |
571
|
7
|
|
|
|
|
22
|
$self->{_data}->{leaf}->{'toplevel'}->{leakage} = $top_leakage; |
572
|
|
|
|
|
|
|
|
573
|
7
|
|
|
|
|
12
|
foreach my $entry ( keys %{$self->{_data}->{leaf}} ) { |
|
7
|
|
|
|
|
109
|
|
574
|
558
|
|
|
|
|
1045
|
my $value = $self->{_data}->{leaf}->{$entry}->{$key}; |
575
|
558
|
|
|
|
|
739
|
my $percentage = $value / $total * 100; |
576
|
|
|
|
|
|
|
|
577
|
558
|
100
|
|
|
|
815
|
if ( $percentage >= $threshold ) { |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
# Store in the 'to be printed with details' hash |
580
|
258
|
|
|
|
|
237
|
$percentage = int($percentage); |
581
|
258
|
|
|
|
|
477
|
$self->{_data}->{leaf}->{$entry}->{name} = $entry; |
582
|
258
|
|
|
|
|
801
|
$self->{_data}->{detail}->{$percentage}->{$entry} = $self->{_data}->{leaf}->{$entry}; |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
# Update the length of the name for printing later |
585
|
258
|
|
|
|
|
254
|
my $namelength = length($entry); |
586
|
258
|
100
|
|
|
|
634
|
$self->{_presentation}->{namelength} = $namelength |
587
|
|
|
|
|
|
|
if ( $self->{_presentation}->{namelength} < $namelength ); |
588
|
|
|
|
|
|
|
} else { |
589
|
300
|
|
|
|
|
424
|
$self->{_data}->{stash}->{percent} += $percentage; |
590
|
300
|
|
50
|
|
|
825
|
$self->{_data}->{stash}->{area} += |
591
|
|
|
|
|
|
|
$self->{_data}->{leaf}->{$entry}->{area} || 0; |
592
|
300
|
|
50
|
|
|
855
|
$self->{_data}->{stash}->{active} += |
593
|
|
|
|
|
|
|
$self->{_data}->{leaf}->{$entry}->{active} || 0; |
594
|
300
|
|
50
|
|
|
976
|
$self->{_data}->{stash}->{leakage} += |
595
|
|
|
|
|
|
|
$self->{_data}->{leaf}->{$entry}->{leakage} || 0; |
596
|
|
|
|
|
|
|
} |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
1; |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
# ABSTRACT: Perl module to parse Cadence synthesis tool logfiles |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
__END__ |