3 ##--------------------------------------------------------------------##
4 ##--- Cachegrind's differencer. cg_diff.in ---##
5 ##--------------------------------------------------------------------##
7 # This file is part of Cachegrind, a Valgrind tool for cache
10 # Copyright (C) 2002-2017 Nicholas Nethercote
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License as
15 # published by the Free Software Foundation; either version 2 of the
16 # License, or (at your option) any later version.
18 # This program is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, see <http://www.gnu.org/licenses/>.
26 # The GNU General Public License is contained in the file COPYING.
28 #----------------------------------------------------------------------------
29 # This is a very cut-down and modified version of cg_annotate.
30 #----------------------------------------------------------------------------
35 #----------------------------------------------------------------------------
37 #----------------------------------------------------------------------------
40 my $version = "@VERSION@";
44 usage: cg_diff [options] <cachegrind-out-file1> <cachegrind-out-file2>
46 options for the user, with defaults in [ ], are:
47 -h --help show this message
48 -v --version show version
49 --mod-filename=<expr> a Perl search-and-replace expression that is applied
50 to filenames, eg. --mod-filename='s/prog[0-9]/projN/'
51 --mod-funcname=<expr> like --mod-filename, but applied to function names
53 cg_diff is Copyright (C) 2002-2017 Nicholas Nethercote.
54 and licensed under the GNU General Public License, version 2.
55 Bug reports, feedback, admiration, abuse, etc, to: njn\@valgrind.org.
60 # --mod-filename expression
61 my $mod_filename = undef;
63 # --mod-funcname expression
64 my $mod_funcname = undef;
66 #-----------------------------------------------------------------------------
67 # Argument and option handling
68 #-----------------------------------------------------------------------------
69 sub process_cmd_line()
71 my ($file1, $file2) = (undef, undef);
77 if ($arg =~ /^-v$|^--version$/) {
78 die("cg_diff-$version\n");
80 } elsif ($arg =~ /^--mod-filename=(.*)/) {
83 } elsif ($arg =~ /^--mod-funcname=(.*)/) {
86 } else { # -h and --help fall under this case
90 } elsif (not defined($file1)) {
93 } elsif (not defined($file2)) {
101 # Must have specified two input files.
102 if (not defined $file1 or not defined $file2) {
106 return ($file1, $file2);
109 #-----------------------------------------------------------------------------
110 # Reading of input file
111 #-----------------------------------------------------------------------------
115 return ($x > $y ? $x : $y);
118 # Add the two arrays; any '.' entries are ignored. Two tricky things:
119 # 1. If $a2->[$i] is undefined, it defaults to 0 which is what we want; we turn
120 # off warnings to allow this. This makes things about 10% faster than
121 # checking for definedness ourselves.
122 # 2. We don't add an undefined count or a ".", even though it's value is 0,
123 # because we don't want to make an $a2->[$i] that is undef become 0
125 sub add_array_a_to_b ($$)
129 my $n = max(scalar @$a, scalar @$b);
131 foreach my $i (0 .. $n-1) {
132 $b->[$i] += $a->[$i] if (defined $a->[$i] && "." ne $a->[$i]);
137 sub sub_array_b_from_a ($$)
141 my $n = max(scalar @$a, scalar @$b);
143 foreach my $i (0 .. $n-1) {
144 $a->[$i] -= $b->[$i]; # XXX: doesn't handle '.' entries
149 # Add each event count to the CC array. '.' counts become undef, as do
150 # missing entries (implicitly).
153 my ($line, $numEvents) = @_;
155 my @CC = (split /\s+/, $line);
156 (@CC <= $numEvents) or die("Line $.: too many event counts\n");
160 sub read_input_file($)
162 my ($input_file) = @_;
164 open(INPUTFILE, "< $input_file")
165 || die "Cannot open $input_file for reading\n";
167 # Read "desc:" lines.
170 while ($line = <INPUTFILE>) {
171 if ($line =~ s/desc:\s+//) {
178 # Read "cmd:" line (Nb: will already be in $line from "desc:" loop above).
179 ($line =~ s/^cmd:\s+//) or die("Line $.: missing command line\n");
181 chomp($cmd); # Remove newline
183 # Read "events:" line. We make a temporary hash in which the Nth event's
184 # value is N, which is useful for handling --show/--sort options below.
186 (defined $line && $line =~ s/^events:\s+//)
187 or die("Line $.: missing events line\n");
188 my @events = split(/\s+/, $line);
189 my $numEvents = scalar @events;
192 my $currFileFuncName;
194 my %CCs; # hash("$filename#$funcname" => CC array)
195 my $currCC = undef; # CC array
199 # Read body of input file.
200 while (<INPUTFILE>) {
201 s/#.*$//; # remove comments
203 my $CC = line_to_CC($_, $numEvents);
204 defined($currCC) || die;
205 add_array_a_to_b($CC, $currCC);
207 } elsif (s/^fn=(.*)$//) {
208 defined($currFileName) || die;
209 my $tmpFuncName = $1;
210 if (defined $mod_funcname) {
211 eval "\$tmpFuncName =~ $mod_funcname";
213 $currFileFuncName = "$currFileName#$tmpFuncName";
214 $currCC = $CCs{$currFileFuncName};
215 if (not defined $currCC) {
217 $CCs{$currFileFuncName} = $currCC;
220 } elsif (s/^fl=(.*)$//) {
222 if (defined $mod_filename) {
223 eval "\$currFileName =~ $mod_filename";
225 # Assume that a "fn=" line is followed by a "fl=" line.
226 $currFileFuncName = undef;
228 } elsif (s/^\s*$//) {
231 } elsif (s/^summary:\s+//) {
232 $summaryCC = line_to_CC($_, $numEvents);
233 (scalar(@$summaryCC) == @events)
234 or die("Line $.: summary event and total event mismatch\n");
237 warn("WARNING: line $. malformed, ignoring\n");
241 # Check if summary line was present
242 if (not defined $summaryCC) {
243 die("missing final summary line, aborting\n");
248 return ($cmd, \@events, \%CCs, $summaryCC);
251 #----------------------------------------------------------------------------
253 #----------------------------------------------------------------------------
254 # Commands seen in the files. Need not match.
258 # Events seen in the files. They must match.
262 # Individual CCs, organised by filename/funcname/line_num.
263 # hashref("$filename#$funcname", CC array)
267 # Total counts for summary (an arrayref).
271 #----------------------------------------------------------------------------
272 # Read the input files
273 #----------------------------------------------------------------------------
274 my ($file1, $file2) = process_cmd_line();
275 ($cmd1, $events1, $CCs1, $summaryCC1) = read_input_file($file1);
276 ($cmd2, $events2, $CCs2, $summaryCC2) = read_input_file($file2);
278 #----------------------------------------------------------------------------
279 # Check the events match
280 #----------------------------------------------------------------------------
281 my $n = max(scalar @$events1, scalar @$events2);
282 $^W = 0; # turn off warnings, because we might hit undefs
283 foreach my $i (0 .. $n-1) {
284 ($events1->[$i] eq $events2->[$i]) || die "events don't match, aborting\n";
288 #----------------------------------------------------------------------------
289 # Do the subtraction: CCs2 -= CCs1
290 #----------------------------------------------------------------------------
291 while (my ($filefuncname, $CC1) = each(%$CCs1)) {
292 my $CC2 = $CCs2->{$filefuncname};
293 if (not defined $CC2) {
295 sub_array_b_from_a($CC2, $CC1); # CC2 -= CC1
296 $CCs2->{$filefuncname} = $CC2;
298 sub_array_b_from_a($CC2, $CC1); # CC2 -= CC1
301 sub_array_b_from_a($summaryCC2, $summaryCC1);
303 #----------------------------------------------------------------------------
304 # Print the result, in CCs2
305 #----------------------------------------------------------------------------
306 print("desc: Files compared: $file1; $file2\n");
307 print("cmd: $cmd1; $cmd2\n");
309 for my $e (@$events1) {
314 while (my ($filefuncname, $CC) = each(%$CCs2)) {
316 my @x = split(/#/, $filefuncname);
317 (scalar @x == 2) || die;
323 foreach my $n (@$CC) {
330 foreach my $n (@$summaryCC2) {
335 ##--------------------------------------------------------------------##
337 ##--------------------------------------------------------------------##