util: Install scons 3.1 from pip in gcn-gpu dockerfile
[gem5.git] / util / rundiff
1 #! /usr/bin/env perl
2
3 # Copyright (c) 2003 The Regents of The University of Michigan
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are
8 # met: redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer;
10 # redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution;
13 # neither the name of the copyright holders nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 # Diff two streams.
30 #
31 # Unlike regular diff, this script does not read in the entire input
32 # before doing a diff, so it can be used on lengthy outputs piped from
33 # other programs (e.g., M5 traces). The best way to do this is to
34 # take advantage of the power of Perl's open function, which will
35 # automatically fork a subprocess if the last character in the
36 # "filename" is a pipe (|). Thus to compare the instruction traces
37 # from two versions of m5 (m5a and m5b), you can do this:
38 #
39 # rundiff 'm5a --traceflags=InstExec |' 'm5b --traceflags=InstExec |'
40 #
41
42 use strict;
43 use FileHandle;
44 use Getopt::Std;
45
46 #
47 # Options:
48 # -c <n> : print n lines of context before & after changes
49 # -l <n> : use n lines of lookahead
50 # -x : use "complex" diff from Algorithm::Diff (see below)
51 #
52 our ($opt_c, $opt_l, $opt_x);
53 getopts('c:l:x');
54
55 #
56 # For the highest-quality (minimal) diffs, we can use the
57 # Algorithm::Diff package. By default, a built-in, simple, and
58 # generally quite adequate algorithm will be used. If you have
59 # Algorithm::Diff installed on your system, and don't mind having the
60 # script go slower (like 3-4x slower, based on informal observation),
61 # then specify '-x' on the command line to use it.
62 my $use_complexdiff = defined($opt_x);
63
64 if ($use_complexdiff) {
65 # Don't use 'use', as that's a compile-time option and will fail
66 # on systems that don't have Algorithm::Diff installed even if
67 # $use_complexdiff is false. 'require' is evaluated at runtime,
68 # so it's OK.
69 require Algorithm::Diff;
70 import Algorithm::Diff qw(traverse_sequences);
71 };
72
73 my $lookahead_lines = $opt_l || 200;
74
75 # in theory you could have different amounts of context before and
76 # after a diff, but until someone needs that there's only one arg to
77 # set both.
78 my $precontext_lines = $opt_c || 3;
79 my $postcontext_lines = $precontext_lines;
80
81 my $file1 = $ARGV[0];
82 my $file2 = $ARGV[1];
83
84 die "Need two args." if (!(defined($file1) && defined($file2)));
85
86 my ($fh1, $fh2);
87 open($fh1, $file1) or die "Can't open $file1";
88 open($fh2, $file2) or die "Can't open $file2";
89
90 # print files to output so we know which is which
91 print "-$file1\n";
92 print "+$file2\n";
93
94 # buffer of matching lines for pre-diff context
95 my @precontext = ();
96 # number of post-diff matching lines remaining to print
97 my $postcontext = 0;
98
99 # lookahead buffers for $file1 and $file2 respectively
100 my @lines1 = ();
101 my @lines2 = ();
102
103 # Next line number available to print from each file. Generally this
104 # corresponds to the oldest line in @precontext, or the oldest line in
105 # @lines1 and @lines2 if @precontext is empty.
106 my $lineno1 = 1;
107 my $lineno2 = 1;
108
109 # Fill a lookahead buffer to $lookahead_lines lines (or until EOF).
110 sub fill
111 {
112 my ($fh, $array) = @_;
113
114 while (@$array < $lookahead_lines) {
115 my $line = <$fh>;
116 last if (!defined($line));
117 push @$array, $line;
118 }
119 }
120
121 # Print and delete n lines from front of given array with given prefix.
122 sub printlines
123 {
124 my ($array, $n, $prefix) = @_;
125
126 while ($n--) {
127 my $line = shift @$array;
128 last if (!defined($line));
129 print $prefix, $line;
130 }
131 }
132
133 # Print a difference region where n1 lines of file1 were replaced by
134 # n2 lines of file2 (where either n1 or n2 could be zero).
135 sub printdiff
136 {
137 my ($n1, $n2)= @_;
138
139 # If the precontext buffer is full or we're at the beginning of a
140 # file, then this is a new diff region, so we should print a
141 # header indicating the current line numbers. If we're past the
142 # beginning and the precontext buffer isn't full, then whatever
143 # we're about to print is contiguous with the end of the last
144 # region we printed, so we just concatenate them on the output.
145 if (@precontext == $precontext_lines || ($lineno1 == 0 && $lineno2 == 0)) {
146 print "@@ -$lineno1 +$lineno2 @@\n";
147 }
148
149 # Print and clear the precontext buffer.
150 if (@precontext) {
151 print ' ', join(' ', @precontext);
152 $lineno1 += scalar(@precontext);
153 $lineno2 += scalar(@precontext);
154 @precontext = ();
155 }
156
157 # Print the differing lines.
158 printlines(\@lines1, $n1, '-');
159 printlines(\@lines2, $n2, '+');
160 $lineno1 += $n1;
161 $lineno2 += $n2;
162
163 # Set $postcontext to print the next $postcontext_lines matching lines.
164 $postcontext = $postcontext_lines;
165
166 # Normally we flush after the postcontext lines are printed, but if
167 # the user has decreed that there aren't any we need to flush now
168 if ($postcontext == 0) {
169 STDOUT->flush();
170 }
171 }
172
173
174 ########################
175 #
176 # Complex diff algorithm
177 #
178 ########################
179
180 {
181 my $match_found;
182 my $discard_lines1;
183 my $discard_lines2;
184
185 sub match { $match_found = 1; }
186 sub discard1 { $discard_lines1++ unless $match_found; }
187 sub discard2 { $discard_lines2++ unless $match_found; }
188
189 sub complex_diff
190 {
191 $match_found = 0;
192 $discard_lines1 = 0;
193 $discard_lines2 = 0;
194
195 # See Diff.pm. Note that even though this call generates a
196 # complete diff of both lookahead buffers, all we use it for
197 # is to figure out how many lines to discard off the front of
198 # each buffer to resync the streams.
199 traverse_sequences( \@lines1, \@lines2,
200 { MATCH => \&match,
201 DISCARD_A => \&discard1,
202 DISCARD_B => \&discard2 });
203
204 if (!$match_found) {
205 printdiff(scalar(@lines1), scalar(@lines2));
206 die "Lost sync!";
207 }
208
209 # Since we shouldn't get here unless the first lines of the
210 # buffers are different, then we must discard some lines off
211 # at least one of the buffers.
212 die if ($discard_lines1 == 0 && $discard_lines2 == 0);
213
214 printdiff($discard_lines1, $discard_lines2);
215 }
216 }
217
218 #######################
219 #
220 # Simple diff algorithm
221 #
222 #######################
223
224 # Check for a pair of matching lines; if found, generate appropriate
225 # diff output.
226 sub checkmatch
227 {
228 my ($n1, $n2) = @_;
229
230 # Check if two adjacent lines match, to reduce false resyncs
231 # (particularly on unrelated blank lines). This generates
232 # larger-than-necessary diffs when a single line really should be
233 # treated as common; if that bugs you, use Algorithm::Diff.
234 if ($lines1[$n1] eq $lines2[$n2] && $lines1[$n1+1] eq $lines2[$n2+1]) {
235 printdiff($n1, $n2);
236 return 1;
237 }
238
239 return 0;
240 }
241
242 sub simple_diff
243 {
244 # Look for differences of $cnt lines to resync,
245 # increasing $cnt from 1 to $lookahead_lines until we find
246 # something.
247 for (my $cnt = 1; $cnt < $lookahead_lines-1; ++$cnt) {
248 # Check for n lines in one file being replaced by
249 # n lines in the other.
250 return if checkmatch($cnt, $cnt);
251 # Find differences where n lines in one file were
252 # replaced by m lines in the other. We let m = $cnt
253 # and iterate for n = 0 to $cnt-1.
254 for (my $n = 0; $n < $cnt; ++$n) {
255 return if checkmatch($n, $cnt);
256 return if checkmatch($cnt, $n);
257 }
258 }
259
260 printdiff(scalar(@lines1), scalar(@lines2));
261 die "Lost sync!";
262 }
263
264 # Set the pointer to the appropriate diff function.
265 #
266 # Note that in either case the function determines how many lines to
267 # discard from the front of each lookahead buffer to resync the
268 # streams, then prints the appropriate diff output and discards them.
269 # After the function returns, it should always be the case that
270 # $lines1[0] eq $lines2[0].
271 my $find_diff = $use_complexdiff ? \&complex_diff : \&simple_diff;
272
273 # The main loop.
274 while (1) {
275 # keep lookahead buffers topped up
276 fill($fh1, \@lines1);
277 fill($fh2, \@lines2);
278
279 # peek at first line in each buffer
280 my $l1 = $lines1[0];
281 my $l2 = $lines2[0];
282
283 if (!defined($l1) && !defined($l2)) {
284 # reached EOF on both streams: exit
285 exit(1);
286 }
287
288 if ($l1 eq $l2) {
289 # matching lines: delete from lookahead buffer
290 shift @lines1;
291 shift @lines2;
292 # figure out what to do with this line
293 if ($postcontext > 0) {
294 # we're in the post-context of a diff: print it
295 print ' ', $l1;
296 $lineno1++;
297 $lineno2++;
298 if (--$postcontext == 0) {
299 STDOUT->flush();
300 }
301 }
302 else {
303 # we're in the middle of a matching region... save this
304 # line for precontext in case we run into a difference.
305 push @precontext, $l1;
306 # don't let precontext buffer get bigger than needed
307 while (@precontext > $precontext_lines) {
308 shift @precontext;
309 $lineno1++;
310 $lineno2++;
311 }
312 }
313 }
314 else {
315 # Mismatch. Deal with it.
316 &$find_diff();
317 }
318 }