Index: lib/MTT/Test/Build.pm =================================================================== --- lib/MTT/Test/Build.pm (revision 1360) +++ lib/MTT/Test/Build.pm (working copy) @@ -2,7 +2,7 @@ # # Copyright (c) 2005-2006 The Trustees of Indiana University. # All rights reserved. -# Copyright (c) 2006-2008 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2006-2010 Cisco Systems, Inc. All rights reserved. # Copyright (c) 2007-2009 Sun Microsystems, Inc. All rights reserved. # Copyright (c) 2008 Mellanox Technologies. All rights reserved. # $COPYRIGHT$ @@ -554,12 +554,11 @@ # Save it $MTT::Test::builds->{$mpi_install->{mpi_get_simple_section_name}}->{$mpi_install->{mpi_version}}->{$mpi_install->{simple_section_name}}->{$simple_section} = $ret; - MTT::Test::SaveBuilds($build_base, - $MTT::Globals::Internals->{mpi_get_name} . "." . - $MTT::Globals::Internals->{mpi_install_name} . "." . - $MTT::Globals::Internals->{test_get_name} . "." . - $MTT::Globals::Internals->{test_build_name} - ); + MTT::Test::SaveBuilds($build_base, + $mpi_install->{mpi_get_simple_section_name}, + $mpi_install->{mpi_version}, + $mpi_install->{simple_section_name}, + $simple_section); # Print if (MTT::Values::PASS == $ret->{test_result}) { Index: lib/MTT/Test/Get.pm =================================================================== --- lib/MTT/Test/Get.pm (revision 1360) +++ lib/MTT/Test/Get.pm (working copy) @@ -2,7 +2,7 @@ # # Copyright (c) 2005-2006 The Trustees of Indiana University. # All rights reserved. -# Copyright (c) 2006-2008 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2006-2010 Cisco Systems, Inc. All rights reserved. # Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved. # $COPYRIGHT$ # @@ -129,7 +129,9 @@ $MTT::Test::sources->{$simple_section} = $ret; # Save the data file recording all the sources - MTT::Test::SaveSources($source_dir, $MTT::Globals::Internals->{test_get_name}); + MTT::Test::SaveSources($source_dir, + $MTT::Globals::Internals->{test_get_name}, + $MTT::Globals::Internals->{test_get_name}); } else { Verbose(" No new test sources\n"); } Index: lib/MTT/MPI/Install.pm =================================================================== --- lib/MTT/MPI/Install.pm (revision 1360) +++ lib/MTT/MPI/Install.pm (working copy) @@ -2,7 +2,7 @@ # # Copyright (c) 2005-2006 The Trustees of Indiana University. # All rights reserved. -# Copyright (c) 2006-2008 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2006-2010 Cisco Systems, Inc. All rights reserved. # Copyright (c) 2007-2009 Sun Microsystems, Inc. All rights reserved. # $COPYRIGHT$ # @@ -795,7 +795,8 @@ } MTT::MPI::SaveInstalls($install_base, - $MTT::Globals::Internals->{mpi_get_name} . "." . + $MTT::Globals::Internals->{mpi_get_name}, + $mpi_get->{version}, $MTT::Globals::Internals->{mpi_install_name}); # Successful build? Index: lib/MTT/MPI/Get.pm =================================================================== --- lib/MTT/MPI/Get.pm (revision 1360) +++ lib/MTT/MPI/Get.pm (working copy) @@ -2,7 +2,7 @@ # # Copyright (c) 2005-2006 The Trustees of Indiana University. # All rights reserved. -# Copyright (c) 2006-2008 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2006-2010 Cisco Systems, Inc. All rights reserved. # Copyright (c) 2007-2009 Sun Microsystems, Inc. All rights reserved. # $COPYRIGHT$ # @@ -187,7 +187,10 @@ $MTT::MPI::sources->{$simple_section}->{$ret->{version}} = $ret; # Save the data file recording all the sources - MTT::MPI::SaveSources($source_dir, $MTT::Globals::Internals->{mpi_get_name}); + MTT::MPI::SaveSources($source_dir, + $MTT::Globals::Internals->{mpi_get_name}, + $MTT::Globals::Internals->{mpi_get_name} . + "." . $ret->{version}); } else { Verbose(" No new MPI sources\n"); } Index: lib/MTT/Test.pm =================================================================== --- lib/MTT/Test.pm (revision 1362) +++ lib/MTT/Test.pm (working copy) @@ -59,7 +59,7 @@ # Explicitly delete/replace anything that was there $MTT::Test::sources = - MTT::Files::load_dumpfiles(glob("$dir/$sources_data_filename-*.$data_filename_extension")); + MTT::Files::load_dumpfiles(1, glob("$dir/$sources_data_filename-*.$data_filename_extension")); # Rebuild the refcounts foreach my $test_key (keys(%{$MTT::Test::sources})) { @@ -73,14 +73,14 @@ #-------------------------------------------------------------------------- sub SaveSources { - my ($dir, $name) = @_; + my ($dir, $key, $name) = @_; # We write individual dump files for each section so that multiple # readers / writers can be active in the scratch tree # simultaneously. So write *just the desired section* to the dump # file. my $d; - $d->{$name} = $MTT::Test::sources->{$name}; + $d->{$key} = $MTT::Test::sources->{$key}; my $file = "$dir/$sources_data_filename-$name.$data_filename_extension"; MTT::Files::save_dumpfile($file, $d); @@ -93,7 +93,7 @@ # Explicitly delete/replace anything that was there $MTT::Test::builds = - MTT::Files::load_dumpfiles(glob("$dir/$builds_data_filename-*.$data_filename_extension")); + MTT::Files::load_dumpfiles(4, glob("$dir/$builds_data_filename-*.$data_filename_extension")); # Rebuild the refcounts foreach my $get_key (keys(%{$MTT::Test::builds})) { @@ -131,21 +131,18 @@ #-------------------------------------------------------------------------- sub SaveBuilds { - my ($dir, $name) = @_; + my ($dir, $mpi_name, $mpi_version, $install_name, $test_name) = @_; # We write individual dump files for each section so that multiple # readers / writers can be active in the scratch tree # simultaneously. So write *just the desired section* to the dump # file. my $d; - $d->{$name} = $MTT::Test::builds->{$name}; + $d->{$mpi_name}->{$mpi_version}->{$install_name}->{$test_name} = + $MTT::Test::builds->{$mpi_name}->{$mpi_version}->{$install_name}->{$test_name}; - # We write the entire Test::builds hash to file, even - # though the filename indicates a single INI section - # MTT::Util::hashes_merge will take care of duplicate - # hash keys. The reason for splitting up the .dump files - # is to keep them read and write safe across INI sections - my $file = "$dir/$builds_data_filename-$name.$data_filename_extension"; + my $f = "$mpi_name.$mpi_version.$install_name.$test_name"; + my $file = "$dir/$builds_data_filename-$f.$data_filename_extension"; MTT::Files::save_dumpfile($file, $d); } Index: lib/MTT/MPI.pm =================================================================== --- lib/MTT/MPI.pm (revision 1362) +++ lib/MTT/MPI.pm (working copy) @@ -46,7 +46,7 @@ # Explicitly delete/replace anything that was there $MTT::MPI::sources = - MTT::Files::load_dumpfiles(glob("$dir/$sources_data_filename-*.$data_filename_extension")); + MTT::Files::load_dumpfiles(2, glob("$dir/$sources_data_filename-*.$data_filename_extension")); # Rebuild the refcounts foreach my $get_key (keys(%{$MTT::MPI::sources})) { @@ -63,14 +63,14 @@ #-------------------------------------------------------------------------- sub SaveSources { - my ($dir, $name) = @_; + my ($dir, $key, $name) = @_; # We write individual dump files for each section so that multiple # readers / writers can be active in the scratch tree # simultaneously. So write *just the desired section* to the dump # file. my $d; - $d->{$name} = $MTT::MPI::sources->{$name}; + $d->{$key} = $MTT::MPI::sources->{$key}; my $file = "$dir/$sources_data_filename-$name.$data_filename_extension"; MTT::Files::save_dumpfile($file, $d); @@ -83,7 +83,7 @@ # Explicitly delete/replace anything that was there $MTT::MPI::installs = - MTT::Files::load_dumpfiles(glob("$dir/$installs_data_filename-*.$data_filename_extension")); + MTT::Files::load_dumpfiles(3, glob("$dir/$installs_data_filename-*.$data_filename_extension")); # Rebuild the refcounts foreach my $get_key (keys(%{$MTT::MPI::installs})) { @@ -110,16 +110,18 @@ #-------------------------------------------------------------------------- sub SaveInstalls { - my ($dir, $name) = @_; + my ($dir, $mpi_name, $mpi_version, $install_name) = @_; # We write individual dump files for each section so that multiple # readers / writers can be active in the scratch tree # simultaneously. So write *just the desired section* to the dump # file. my $d; - $d->{$name} = $MTT::MPI::installs->{$name}; + $d->{$mpi_name}->{$mpi_version}->{$install_name} = + $MTT::MPI::installs->{$mpi_name}->{$mpi_version}->{$install_name}; - my $file = "$dir/$installs_data_filename-$name.$data_filename_extension"; + my $f = "$mpi_name.$mpi_version.$install_name"; + my $file = "$dir/$installs_data_filename-$f.$data_filename_extension"; MTT::Files::save_dumpfile($file, $d); } Index: lib/MTT/Files.pm =================================================================== --- lib/MTT/Files.pm (revision 1362) +++ lib/MTT/Files.pm (working copy) @@ -455,10 +455,13 @@ #-------------------------------------------------------------------------- +our $s = 0; + sub load_dumpfiles { + my $unique_depth = shift; my @files = @_; - my $all; + my $all = undef; my $i = 1; foreach my $f (@files) { # If the file exists, read it in @@ -467,19 +470,96 @@ ++$i; load_dumpfile($f, \$data); - my @keys = keys(%{$data->{"VAR1"}}); - my $k = $keys[0]; - Debug("Found dump key: $k\n"); - Error("An identical key already exists in memory when MTT tried to read the file $f. This should not happen. It likely indicates that multiple MTT clients are incorrectly operating in the same scratch tree.") - if (exists($all->{$k})); - $all->{$k} = $data->{"VAR1"}->{$k}; + # Skip the "VAR1" key -- all dump files have that at the top. + $data = $data->{VAR1} + if (exists($data->{VAR1})); + if ($s) { + Verbose("READ IN FILE: "); + DebugDump($data); + } + + # These dump files are structured to have the "interesting" + # data several keys deep. That is, there will be several + # "outer" keys before any real data is stored in a hash. For + # example: $foo->{bar}->{baz}->...real_data... The "outer" + # keys are defines as having exactly one sub key. The file + # that was just read in must have a unique queue in the target + # hash at the specific depth $unique_depth. If we don't have + # a unique key there, Badness has happened. However, keys + # above this depth may well be non-unique. + if (defined($all)) { + _merge_hash($f, $unique_depth - 1, \$all, \$data); + } else { + $all = $data; + } } - Debug("Read in dump file keys: " . join(" ", keys(%{$all})) . "\n"); $all; } +sub _merge_hash { + my ($file, $depth, $a, $b) = @_; + + if ($s) { + Verbose("=========================================\n"); + Verbose("MERGING: depth=$depth\n"); + Verbose("a = "); + DebugDump($$a); + Verbose("-----------------------------------------\n"); + Verbose("b = "); + DebugDump($$b); + Verbose("-----------------------------------------\n"); + } + + # If our depth is < 0, we don't care about uniqueness, so just copy + if ($depth < 0) { + $$a = $$b; + } + + # Otherwise, we need to examine individual keys. + else { + # This function is only ever called with 2 hashes, so we know + # we can traverse keys of both $a and $b. + foreach my $k (keys(%{$$b})) { + Verbose("Got key: $k\n") + if ($s); + # Key collision! + if (exists(${$a}->{$k})) { + Verbose("Collision\n") + if ($s); + + # If we're at 0 depth, then this is the level where + # uniqueness matters -- so error out because we got a + # collision. + if (0 == $depth) { + Error("An identical key already exists in memory when MTT tried to read the file $file (key=$k). This should not happen. It likely indicates that multiple MTT clients are incorrectly operating in the same scratch tree."); + } + + # Otherwise we try to copy. If both are hashes, recurse. + if (ref(${$a}->{$k}) =~ /hash/i && + ref(${$b}->{$k}) =~ /hash/i) { + _merge_hash($file, $depth - 1, \${$a}->{$k}, \${$b}->{$k}); + } + # Otherwise, $a wins + } + + # No collision, so just copy $b->{$k} over + else { + Verbose("No collision\n") + if ($s); + ${$a}->{$k} = ${$b}->{$k}; + } + } + } + + if ($s) { + Verbose("MERGED: depth=$depth\n"); + DebugDump($a); + Verbose("-----------------------------------------\n"); + } +} + #-------------------------------------------------------------------------- sub save_dumpfile {