3 # Copyright (c) 2003 The Regents of The University of Michigan
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.
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.
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:
39 # rundiff 'm5a --trace:flags=InstExec |' 'm5b --trace:flags=InstExec |'
45 # For the highest-quality (minimal) diffs, we can use the
46 # Algorithm::Diff package. If you don't have this installed, or want
47 # the script to run faster (like 3-4x faster, based on informal
48 # observation), set $use_complexdiff to 0; then a built-in, simple,
49 # and generally quite adequate algorithm will be used instead.
50 my $use_complexdiff = 0;
52 if ($use_complexdiff) {
53 use Algorithm
::Diff
qw(traverse_sequences);
56 my $lookahead_lines = 200;
57 my $precontext_lines = 3;
58 my $postcontext_lines = 3;
63 die "Need two args." if (!(defined($file1) && defined($file2)));
66 open($fh1, $file1) or die "Can't open $file1";
67 open($fh2, $file2) or die "Can't open $file2";
69 # buffer of matching lines for pre-diff context
71 # number of post-diff matching lines remaining to print
74 # lookahead buffers for $file1 and $file2 respectively
78 # Next line number available to print from each file. Generally this
79 # corresponds to the oldest line in @precontext, or the oldest line in
80 # @lines1 and @lines2 if @precontext is empty.
84 # Fill a lookahead buffer to $lookahead_lines lines (or until EOF).
87 my ($fh, $array) = @_;
89 while (@
$array < $lookahead_lines) {
91 last if (!defined($line));
96 # Print and delete n lines from front of given array with given prefix.
99 my ($array, $n, $prefix) = @_;
102 my $line = shift @
$array;
103 last if (!defined($line));
104 print $prefix, $line;
108 # Print a difference region where n1 lines of file1 were replaced by
109 # n2 lines of file2 (where either n1 or n2 could be zero).
114 # If the precontext buffer is full or we're at the beginning of a
115 # file, then this is a new diff region, so we should print a
116 # header indicating the current line numbers. If we're past the
117 # beginning and the precontext buffer isn't full, then whatever
118 # we're about to print is contiguous with the end of the last
119 # region we printed, so we just concatenate them on the output.
120 if (@precontext == $precontext_lines || ($lineno1 == 0 && $lineno2 == 0)) {
121 print "@@ -$lineno1 +$lineno2 @@\n";
124 # Print and clear the precontext buffer.
126 print ' ', join(' ', @precontext);
127 $lineno1 += scalar(@precontext);
128 $lineno2 += scalar(@precontext);
132 # Print the differing lines.
133 printlines
(\
@lines1, $n1, '-');
134 printlines
(\
@lines2, $n2, '+');
138 # Set $postcontext to print the next $postcontext_lines matching lines.
139 $postcontext = $postcontext_lines;
143 ########################
145 # Complex diff algorithm
147 ########################
154 sub match
{ $match_found = 1; }
155 sub discard1
{ $discard_lines1++ unless $match_found; }
156 sub discard2
{ $discard_lines2++ unless $match_found; }
164 # See Diff.pm. Note that even though this call generates a
165 # complete diff of both lookahead buffers, all we use it for
166 # is to figure out how many lines to discard off the front of
167 # each buffer to resync the streams.
168 traverse_sequences
( \
@lines1, \
@lines2,
170 DISCARD_A
=> \
&discard1
,
171 DISCARD_B
=> \
&discard2
});
173 die "Lost sync!" if (!$match_found);
175 # Since we shouldn't get here unless the first lines of the
176 # buffers are different, then we must discard some lines off
177 # at least one of the buffers.
178 die if ($discard_lines1 == 0 && $discard_lines2 == 0);
180 printdiff
($discard_lines1, $discard_lines2);
184 #######################
186 # Simple diff algorithm
188 #######################
190 # Check for a pair of matching lines; if found, generate appropriate
196 # Check if two adjacent lines match, to reduce false resyncs
197 # (particularly on unrelated blank lines). This generates
198 # larger-than-necessary diffs when a single line really should be
199 # treated as common; if that bugs you, use Algorithm::Diff.
200 if ($lines1[$n1] eq $lines2[$n2] && $lines1[$n1+1] eq $lines2[$n2+1]) {
207 # Look for differences of $cnt lines to resync,
208 # increasing $cnt from 1 to $lookahead_lines until we find
210 for (my $cnt = 1; $cnt < $lookahead_lines-1; ++$cnt) {
211 # Check for n lines in one file being replaced by
212 # n lines in the other.
213 return if checkmatch
($cnt, $cnt);
214 # Find differences where n lines in one file were
215 # replaced by m lines in the other. We let m = $cnt
216 # and iterate for n = 0 to $cnt-1.
217 for (my $n = 0; $n < $cnt; ++$n) {
218 return if checkmatch
($n, $cnt);
219 return if checkmatch
($cnt, $n);
225 # Set the pointer to the appropriate diff function.
227 # Note that in either case the function determines how many lines to
228 # discard from the front of each lookahead buffer to resync the
229 # streams, then prints the appropriate diff output and discards them.
230 # After the function returns, it should always be the case that
231 # $lines1[0] eq $lines2[0].
232 my $find_diff = $use_complexdiff ? \
&complex_diff
: \
&simple_diff
;
236 # keep lookahead buffers topped up
237 fill
($fh1, \
@lines1);
238 fill
($fh2, \
@lines2);
240 # peek at first line in each buffer
244 if (!defined($l1) && !defined($l2)) {
245 # reached EOF on both streams: exit
250 # matching lines: delete from lookahead buffer
253 # figure out what to do with this line
254 if ($postcontext > 0) {
255 # we're in the post-context of a diff: print it
262 # we're in the middle of a matching region... save this
263 # line for precontext in case we run into a difference.
264 push @precontext, $l1;
265 # don't let precontext buffer get bigger than needed
266 while (@precontext > $precontext_lines) {
274 # Mismatch. Deal with it.