blob: 951066e40a16828dd97a9863cb5ed5583702ccb8 [file] [log] [blame]
njn69d495d2010-06-30 05:23:34 +00001#! @PERL@
2
3##--------------------------------------------------------------------##
4##--- Cachegrind's differencer. cg_diff.in ---##
5##--------------------------------------------------------------------##
6
7# This file is part of Cachegrind, a Valgrind tool for cache
8# profiling programs.
9#
10# Copyright (C) 2002-2010 Nicholas Nethercote
11# njn@valgrind.org
12#
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.
17#
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.
22#
23# You should have received a copy of the GNU General Public License
24# along with this program; if not, write to the Free Software
25# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26# 02111-1307, USA.
27#
28# The GNU General Public License is contained in the file COPYING.
29
30#----------------------------------------------------------------------------
31# This is a very cut-down and modified version of cg_annotate.
32#----------------------------------------------------------------------------
33
34use warnings;
35use strict;
36
37#----------------------------------------------------------------------------
38# Global variables
39#----------------------------------------------------------------------------
40
41# Version number
42my $version = "@VERSION@";
43
44# Usage message.
45my $usage = <<END
46usage: cg_diff [options] <cachegrind-out-file1> <cachegrind-out-file2>
47
48 options for the user, with defaults in [ ], are:
49 -h --help show this message
50 -v --version show version
51 --mod-filename=<expr> a Perl search-and-replace expression that is applied
52 to filenames, eg. --mod-filename='s/prog[0-9]/projN/'
53
54 cg_diff is Copyright (C) 2010-2010 Nicholas Nethercote.
55 and licensed under the GNU General Public License, version 2.
56 Bug reports, feedback, admiration, abuse, etc, to: njn\@valgrind.org.
57
58END
59;
60
61# --mod-filename expression
62my $mod_filename = undef;
63
64#-----------------------------------------------------------------------------
65# Argument and option handling
66#-----------------------------------------------------------------------------
67sub process_cmd_line()
68{
69 my ($file1, $file2) = (undef, undef);
70
71 for my $arg (@ARGV) {
72
73 if ($arg =~ /^-/) {
74 # --version
75 if ($arg =~ /^-v$|^--version$/) {
76 die("cg_diff-$version\n");
77
78 } elsif ($arg =~ /^--mod-filename=(.*)/) {
79 $mod_filename = $1;
80
81 } else { # -h and --help fall under this case
82 die($usage);
83 }
84
85 } elsif (not defined($file1)) {
86 $file1 = $arg;
87
88 } elsif (not defined($file2)) {
89 $file2 = $arg;
90
91 } else {
92 die($usage);
93 }
94 }
95
96 # Must have specified two input files.
97 if (not defined $file1 or not defined $file2) {
98 die($usage);
99 }
100
101 return ($file1, $file2);
102}
103
104#-----------------------------------------------------------------------------
105# Reading of input file
106#-----------------------------------------------------------------------------
107sub max ($$)
108{
109 my ($x, $y) = @_;
110 return ($x > $y ? $x : $y);
111}
112
113# Add the two arrays; any '.' entries are ignored. Two tricky things:
114# 1. If $a2->[$i] is undefined, it defaults to 0 which is what we want; we turn
115# off warnings to allow this. This makes things about 10% faster than
116# checking for definedness ourselves.
117# 2. We don't add an undefined count or a ".", even though it's value is 0,
118# because we don't want to make an $a2->[$i] that is undef become 0
119# unnecessarily.
120sub add_array_a_to_b ($$)
121{
122 my ($a, $b) = @_;
123
124 my $n = max(scalar @$a, scalar @$b);
125 $^W = 0;
126 foreach my $i (0 .. $n-1) {
127 $b->[$i] += $a->[$i] if (defined $a->[$i] && "." ne $a->[$i]);
128 }
129 $^W = 1;
130}
131
132sub sub_array_b_from_a ($$)
133{
134 my ($a, $b) = @_;
135
136 my $n = max(scalar @$a, scalar @$b);
137 $^W = 0;
138 foreach my $i (0 .. $n-1) {
139 $a->[$i] -= $b->[$i]; # XXX: doesn't handle '.' entries
140 }
141 $^W = 1;
142}
143
144# Add each event count to the CC array. '.' counts become undef, as do
145# missing entries (implicitly).
146sub line_to_CC ($$)
147{
148 my ($line, $numEvents) = @_;
149
150 my @CC = (split /\s+/, $line);
151 (@CC <= $numEvents) or die("Line $.: too many event counts\n");
152 return \@CC;
153}
154
155sub read_input_file($)
156{
157 my ($input_file) = @_;
158
159 open(INPUTFILE, "< $input_file")
160 || die "Cannot open $input_file for reading\n";
161
162 # Read "desc:" lines.
163 my $desc;
164 my $line;
165 while ($line = <INPUTFILE>) {
166 if ($line =~ s/desc:\s+//) {
167 $desc .= $line;
168 } else {
169 last;
170 }
171 }
172
173 # Read "cmd:" line (Nb: will already be in $line from "desc:" loop above).
174 ($line =~ s/^cmd:\s+//) or die("Line $.: missing command line\n");
175 my $cmd = $line;
176 chomp($cmd); # Remove newline
177
178 # Read "events:" line. We make a temporary hash in which the Nth event's
179 # value is N, which is useful for handling --show/--sort options below.
180 $line = <INPUTFILE>;
181 (defined $line && $line =~ s/^events:\s+//)
182 or die("Line $.: missing events line\n");
183 my @events = split(/\s+/, $line);
184 my $numEvents = scalar @events;
185
186 my $currFileName;
187 my $currFileFuncName;
188
189 my %CCs; # hash("$filename#$funcname" => CC array)
190 my $currCC = undef; # CC array
191
192 my $summaryCC;
193
194 # Read body of input file.
195 while (<INPUTFILE>) {
196 s/#.*$//; # remove comments
197 if (s/^(\d+)\s+//) {
198 my $CC = line_to_CC($_, $numEvents);
199 defined($currCC) || die;
200 add_array_a_to_b($CC, $currCC);
201
202 } elsif (s/^fn=(.*)$//) {
203 defined($currFileName) || die;
204 $currFileFuncName = "$currFileName#$1";
205 $currCC = $CCs{$currFileFuncName};
206 if (not defined $currCC) {
207 $currCC = [];
208 $CCs{$currFileFuncName} = $currCC;
209 }
210
211 } elsif (s/^fl=(.*)$//) {
212 $currFileName = $1;
213 if (defined $mod_filename) {
214 eval "\$currFileName =~ $mod_filename";
215 }
216 # Assume that a "fn=" line is followed by a "fl=" line.
217 $currFileFuncName = undef;
218
219 } elsif (s/^\s*$//) {
220 # blank, do nothing
221
222 } elsif (s/^summary:\s+//) {
223 $summaryCC = line_to_CC($_, $numEvents);
224 (scalar(@$summaryCC) == @events)
225 or die("Line $.: summary event and total event mismatch\n");
226
227 } else {
228 warn("WARNING: line $. malformed, ignoring\n");
229 }
230 }
231
232 # Check if summary line was present
233 if (not defined $summaryCC) {
234 die("missing final summary line, aborting\n");
235 }
236
237 close(INPUTFILE);
238
239 return ($cmd, \@events, \%CCs, $summaryCC);
240}
241
242#----------------------------------------------------------------------------
243# "main()"
244#----------------------------------------------------------------------------
245# Commands seen in the files. Need not match.
246my $cmd1;
247my $cmd2;
248
249# Events seen in the files. They must match.
250my $events1;
251my $events2;
252
253# Individual CCs, organised by filename/funcname/line_num.
254# hashref("$filename#$funcname", CC array)
255my $CCs1;
256my $CCs2;
257
258# Total counts for summary (an arrayref).
259my $summaryCC1;
260my $summaryCC2;
261
262#----------------------------------------------------------------------------
263# Read the input files
264#----------------------------------------------------------------------------
265my ($file1, $file2) = process_cmd_line();
266($cmd1, $events1, $CCs1, $summaryCC1) = read_input_file($file1);
267($cmd2, $events2, $CCs2, $summaryCC2) = read_input_file($file2);
268
269#----------------------------------------------------------------------------
270# Check the events match
271#----------------------------------------------------------------------------
272my $n = max(scalar @$events1, scalar @$events2);
273$^W = 0; # turn off warnings, because we might hit undefs
274foreach my $i (0 .. $n-1) {
275 ($events1->[$i] eq $events2->[$i]) || die "events don't match, aborting\n";
276}
277$^W = 1;
278
279#----------------------------------------------------------------------------
280# Do the subtraction: CCs2 -= CCs1
281#----------------------------------------------------------------------------
282while (my ($filefuncname, $CC1) = each(%$CCs1)) {
283 my $CC2 = $CCs2->{$filefuncname};
284 if (not defined $CC2) {
285 $CC2 = [];
286 sub_array_b_from_a($CC2, $CC1); # CC2 -= CC1
287 $CCs2->{$filefuncname} = $CC2;
288 } else {
289 sub_array_b_from_a($CC2, $CC1); # CC2 -= CC1
290 }
291}
292sub_array_b_from_a($summaryCC2, $summaryCC1);
293
294#----------------------------------------------------------------------------
295# Print the result, in CCs2
296#----------------------------------------------------------------------------
297print("desc: Files compared: $file1; $file2\n");
298print("cmd: $cmd1; $cmd2\n");
299print("events: ");
300for my $e (@$events1) {
301 print(" $e");
302}
303print("\n");
304
305while (my ($filefuncname, $CC) = each(%$CCs2)) {
306
307 my @x = split(/#/, $filefuncname);
308 (scalar @x == 2) || die;
309
310 print("fl=$x[0]\n");
311 print("fn=$x[1]\n");
312
313 print("0");
314 foreach my $n (@$CC) {
315 print(" $n");
316 }
317 print("\n");
318}
319
320print("summary:");
321foreach my $n (@$summaryCC2) {
322 print(" $n");
323}
324print("\n");
325
326##--------------------------------------------------------------------##
327##--- end ---##
328##--------------------------------------------------------------------##