Util: Added script to semantically diff two config.ini files
authorSascha Bischoff <sascha.bischoff@arm.com>
Tue, 25 Sep 2012 16:49:40 +0000 (11:49 -0500)
committerSascha Bischoff <sascha.bischoff@arm.com>
Tue, 25 Sep 2012 16:49:40 +0000 (11:49 -0500)
This script (util/diff_config.pl) takes two config.ini files and compares them.
It highlights value changes, as well as displaying which parts are unique to
a specific config.ini file. This is useful when trying to replicate an earlier
experiment and when trying to make small changes to an existing configuration.

util/diff_config.pl [new file with mode: 0755]

diff --git a/util/diff_config.pl b/util/diff_config.pl
new file mode 100755 (executable)
index 0000000..0eb9906
--- /dev/null
@@ -0,0 +1,231 @@
+# Copyright (c) 2012 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Author: Uri Wiener
+#
+
+# Script which takes two config.ini files and generates a semantic diff. The
+# resulting diff shows which parts of the configurations differed, and in the
+# case that there is a difference it displays it. This allows rapid comparision
+# of two gem5 runs and therefore provides an easy method to ensure that
+# configurations are similar, or not.
+
+#!/usr/bin/perl
+use strict;
+
+die "Please check args... " unless ($#ARGV == 1);
+my $config1FileName = $ARGV[0];
+my $config2FileName = $ARGV[1];
+
+# Get just the name of the file, rather than the full path
+my $config1ShortName = getFilenameFromPath($config1FileName);
+my $config2ShortName = getFilenameFromPath($config2FileName);
+
+# If the file names are the same, use the full path
+if ($config1ShortName == $config2ShortName) {
+    $config1ShortName = $config1FileName;
+    $config2ShortName = $config2FileName;
+}
+
+print "\nComparing the following files:\n",
+    "\t$config1FileName\n",
+    "\tvs.\n",
+    "\t$config2FileName\n\n";
+
+# Read in the two config files
+my %config1 = readConfig($config1FileName);
+my %config2 = readConfig($config2FileName);
+
+# Compare the two config files. For the first comparision we also compare the
+# values (setting the first parameter to 1). There is little point doing this
+# for the second comparison as it will yield the same information.
+compareConfigs( 1, \%config1, $config1ShortName, \%config2, $config2ShortName );
+compareConfigs( 0, \%config2, $config2ShortName, \%config1, $config1ShortName );
+
+
+########################################################
+# Compare values and return unique values
+########################################################
+sub compareValues {
+    my $values1 = shift;
+    my $values2 = shift;
+    my @splitValues1 = split(/ /, $values1);
+    my @splitValues2 = split(/ /, $values2);
+    my @uniqueValues;
+
+    foreach my $val1 (@splitValues1) {
+        my $foundMatch = 0;
+
+        # if both values equal set match flag, then break loop
+        foreach my $val2 (@splitValues2) {
+            if ($val1 eq $val2) {
+                $foundMatch = 1;
+                last;
+            }
+
+            # in case of ports, ignore port number and match port name only
+            if ($val1 =~ /\[/ and $val2 =~ /\[/) {
+                $val1 =~ m/^(.*)\[.*\]/;
+                my $val1Name = $1;
+                $val2 =~ m/^(.*)\[.*\]/;
+                my $val2Name = $1;
+
+                # if both values equal set match flag, then break loop
+                if ($val1Name eq $val2Name) {
+                    $foundMatch = 1;
+                    last;
+                }
+            }
+        }
+
+        # Otherwise, the value is unique.
+        if (not $foundMatch) {
+            push(@uniqueValues, $val1);
+        }
+    }
+
+    return join(", ", @uniqueValues);
+}
+
+
+########################################################
+# Compare two config files. Print differences.
+########################################################
+sub compareConfigs {
+    my $compareFields   = shift; # Specfy if the fields should be compared
+    my $config1Ref      = shift; # Config 1
+    my $config1Name     = shift; # Config 1 name
+    my $config2Ref      = shift; # Config 2
+    my $config2Name     = shift; # Config 2 name
+    my @uniqueSections;
+
+    foreach my $sectionName ( sort keys %$config1Ref ) {
+        # check if section exists in config2
+        if ( not exists $config2Ref->{$sectionName} ) {
+            push(@uniqueSections, $sectionName);
+            next;
+        }
+        my %section1 = %{ $config1Ref->{$sectionName} };
+        my %section2 = %{ $config2Ref->{$sectionName} };
+        my $firstDifInSection = 1;
+
+        if (not $compareFields) {
+            next;
+        }
+
+        # Compare the values of each field; print any differences
+        foreach my $field ( sort keys %section1 ) {
+            if ($section1{$field} ne $section2{$field}) {
+                   my $diff1 = compareValues($section1{$field}, $section2{$field});
+                   my $diff2 = compareValues($section2{$field}, $section1{$field});
+
+                # If same, skip to next iteration
+                if ($diff1 eq "" and $diff2 eq "") {
+                    next;
+                }
+
+                # If it is the first difference in this section, print section
+                # name
+                if ($firstDifInSection) {
+                    print "$sectionName\n";
+                    $firstDifInSection = 0;
+                }
+
+                # Print the actual differences
+                   print "\t$field\n";
+                   if ($diff1 ne "") {
+                       print "\t\t$config1Name: ", $diff1, "\n";
+                   }
+                   if ($diff2 ne "") {
+                       print "\t\t$config2Name: ", $diff2, "\n";
+                   }
+            } # end if
+        } # end foreach field
+    } # end foreach section
+
+    # If there are unique sections, print them
+    if ($#uniqueSections != -1) {
+        print "Sections which exist only in $config1Name: ",
+            join(", ", @uniqueSections), "\n";
+    }
+}
+
+
+########################################################
+# Split the path to get just the filename
+########################################################
+sub getFilenameFromPath {
+    my $filename = shift; # the input filename including path
+    my @splitName = split(/\//, $filename);
+    return $splitName[$#splitName]; # return just the filename, without path
+}
+
+
+########################################################
+# Read in the config file section by section.
+########################################################
+sub readConfig {
+    my $filename = shift;
+    my %config;
+    open CONFIG, "<$filename" or die $!;
+    while ( my $line = <CONFIG> ) {
+        if ( $line =~ /^\[.*\]$/ ) {
+            readSection( $line, \%config );
+        }
+    }
+    close CONFIG;
+    return %config;
+}
+
+
+########################################################
+# Read in each section of the config file.
+########################################################
+sub readSection {
+    my $line       = shift;
+    my $config_ref = shift;
+    $line =~ m/\[(.*)\]/;
+    my $sectionName = $1;
+    while ( my $line = <CONFIG> ) {
+        if ( $line =~ /^$/ ) {
+            last;
+        }
+        my @field     = split( /=/, $line );
+        my $fieldName = $field[0];
+        my $value     = $field[1];
+        chomp $value;
+        $config_ref->{$sectionName}{$fieldName} = $value;
+    }
+}