Teuchos - Trilinos Tools Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Teuchos_XMLPerfTestArchive.cpp
00001 // @HEADER
00002 // ***********************************************************************
00003 //
00004 //                    Teuchos: Common Tools Package
00005 //                 Copyright (2004) Sandia Corporation
00006 //
00007 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
00008 // license for use of this work by or on behalf of the U.S. Government.
00009 //
00010 // Redistribution and use in source and binary forms, with or without
00011 // modification, are permitted provided that the following conditions are
00012 // met:
00013 //
00014 // 1. Redistributions of source code must retain the above copyright
00015 // notice, this list of conditions and the following disclaimer.
00016 //
00017 // 2. Redistributions in binary form must reproduce the above copyright
00018 // notice, this list of conditions and the following disclaimer in the
00019 // documentation and/or other materials provided with the distribution.
00020 //
00021 // 3. Neither the name of the Corporation nor the names of the
00022 // contributors may be used to endorse or promote products derived from
00023 // this software without specific prior written permission.
00024 //
00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00036 //
00037 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
00038 //
00039 // ***********************************************************************
00040 // @HEADER
00041 
00042 
00043 #include <iostream>
00044 #include <fstream>
00045 #include <cstring>
00046 #include <cstdlib>
00047 #include <Teuchos_XMLObject.hpp>
00048 #include <Teuchos_FileInputSource.hpp>
00049 #include <Teuchos_XMLPerfTestArchive.hpp>
00050 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
00051 #include <Winsock2.h>
00052 #pragma comment(lib, "ws2_32.lib")
00053 #else
00054 #include <unistd.h>
00055 #endif
00056 
00057 namespace Teuchos {
00058 
00059 ValueTolerance::ValueTolerance() {
00060   value = 0;
00061   lower = 0;
00062   upper = 0;
00063   tolerance = 0;
00064   use_tolerance = true;
00065 }
00066 
00067 ValueTolerance::ValueTolerance(double val, double tol) {
00068   value = val;
00069   lower = 0;
00070   upper = 0;
00071   tolerance = tol;
00072   use_tolerance = true;
00073 }
00074 
00075 ValueTolerance::ValueTolerance(double val, double low, double up) {
00076   value = val;
00077   upper = up;
00078   lower = low;
00079   tolerance = 0;
00080   use_tolerance = false;
00081 }
00082 
00083 ValueTolerance::ValueTolerance(std::string str) {
00084   from_string(str);
00085 }
00086 
00087 bool ValueTolerance::operator ==(ValueTolerance& rhs) {
00088   return (value == rhs.value) &&
00089          (tolerance == rhs.tolerance) &&
00090          (lower == rhs.lower) &&
00091          (upper == rhs.upper) &&
00092          (use_tolerance == rhs.use_tolerance);
00093 }
00094 
00095 std::string ValueTolerance::as_string(){
00096   std::ostringstream strs;
00097   if(use_tolerance)
00098     strs << value << " , " << tolerance;
00099   else
00100     strs << value << " , " << lower << " , " << upper;
00101   return  strs.str();
00102 }
00103 
00104 void ValueTolerance::from_string(const std::string& valtol_str) {
00105   std::string value_str = valtol_str.substr(0,valtol_str.find(","));
00106   value = atof(value_str.c_str());
00107   std::string tol_str = valtol_str.substr(valtol_str.find(",")+1);
00108   if(tol_str.find(",")<=tol_str.length()) {
00109     use_tolerance = false;
00110     std::string lower_str = tol_str.substr(0,tol_str.find(","));
00111     lower = atof(lower_str.c_str());
00112     std::string upper_str = tol_str.substr(tol_str.find(",")+1);
00113     upper = atof(upper_str.c_str());
00114   } else {
00115     use_tolerance = true;
00116     tolerance = atof(tol_str.c_str());
00117   }
00118 }
00119 
00120   XMLTestNode::XMLTestNode():XMLObject() {}
00121 
00122   XMLTestNode::XMLTestNode(const std::string &tag):XMLObject(tag) {}
00123 
00124   XMLTestNode::XMLTestNode(XMLObjectImplem *ptr):XMLObject(ptr) {}
00125 
00126   XMLTestNode::XMLTestNode(XMLObject obj):XMLObject(obj) {}
00127 
00128   void  XMLTestNode::addDouble (const std::string &name, double val) {
00129     addAttribute<double>(name,val);
00130   }
00131 
00132   void  XMLTestNode::addInt (const std::string &name, int val) {
00133     addAttribute<int>(name,val);
00134   }
00135 
00136   void  XMLTestNode::addBool (const std::string &name, bool val) {
00137     addAttribute<bool>(name,val);
00138   }
00139 
00140   void XMLTestNode::addValueTolerance(const std::string &name, ValueTolerance val){
00141     addAttribute<std::string>(name,val.as_string());
00142   }
00143 
00144   void  XMLTestNode::addString (const std::string &name, std::string val) {
00145     addAttribute<std::string>(name,val);
00146   }
00147 
00148   bool XMLTestNode::hasChild(const std::string &name) const {
00149     bool found = false;
00150     for(int i = 0; i < numChildren(); i++) {
00151       if(name.compare(XMLObject::getChild(i).getTag()) == 0) {
00152         found = true;
00153         i = numChildren();
00154       }
00155     }
00156     return found;
00157   }
00158 
00159   void XMLTestNode::appendContentLine(const size_t& i, const std::string &str) {
00160     ptr_->appendContentLine(i,str);
00161   }
00162 
00163   XMLTestNode XMLTestNode::getChild(const std::string &name) const {
00164     XMLTestNode child;
00165     for(int i = 0; i < numChildren(); i++) {
00166       if(name.compare(XMLObject::getChild(i).getTag()) == 0)
00167         child = XMLObject::getChild(i);
00168     }
00169     return child;
00170   }
00171 
00172   XMLTestNode XMLTestNode::getChild(const int &i) const {
00173     return XMLObject::getChild(i);
00174   }
00175 
00176   const XMLObject* XMLTestNode::xml_object() const {
00177     return (XMLObject*) this;
00178   }
00179 
00180   bool XMLTestNode::hasSameElements(XMLTestNode const & lhs) const {
00181 
00182     if((numChildren()!=lhs.numChildren()) ||
00183        (numContentLines()!= lhs.numContentLines()) ||
00184        (getTag().compare(lhs.getTag())!=0)) return false;
00185 
00186     for(int i = 0; i<numChildren(); i++) {
00187       const XMLTestNode child = XMLObject::getChild(i);
00188       if( (!lhs.hasChild(child.getTag())) ||
00189           (!child.hasSameElements(lhs.getChild(child.getTag()))) ) return false;
00190     }
00191 
00192     for(int i = 0; i<numContentLines(); i++)
00193       if(getContentLine(i).compare(lhs.getContentLine(i))!=0) return false;
00194 
00195     return true;
00196   }
00197 
00198 XMLTestNode PerfTest_MachineConfig() {
00199 
00200   // Get CPUName, Number of Sockets, Number of Cores, Number of Hyperthreads
00201   std::string cpuname("Undefined");
00202   unsigned int threads = 0;
00203   unsigned int cores_per_socket = 0;
00204   unsigned int highest_socketid = 0;
00205 
00206   {
00207     std::ifstream cpuinfo("/proc/cpuinfo");
00208     std::string line;
00209     if((cpuinfo.rdstate()&cpuinfo.failbit)) std::cout<<"Failed to open filen\n";
00210     while (!cpuinfo.eof() && !(cpuinfo.rdstate()&cpuinfo.failbit)) {
00211       getline (cpuinfo,line);
00212       if (line.find("model name") < line.size()) {
00213         cpuname = line.substr(line.find(":")+2);
00214         threads++;
00215       }
00216       if (line.find("physical id") < line.size()) {
00217         unsigned int socketid = atoi(line.substr(line.find(":")+2).c_str());
00218         highest_socketid = highest_socketid>socketid?highest_socketid:socketid;
00219       }
00220       if (line.find("cpu cores") < line.size()) {
00221         cores_per_socket = atoi(line.substr(line.find(":")+2).c_str());
00222       }
00223     }
00224   }
00225 
00226 
00227   XMLTestNode machine_config("MachineConfiguration");
00228 
00229   machine_config.addString("Compiler", TEUCHOS_COMPILER_NAME);
00230   machine_config.addInt("Compiler_Version",  TEUCHOS_COMPILER_VERSION);
00231   machine_config.addString("CPU_Name", cpuname);
00232   machine_config.addInt("CPU_Sockets", highest_socketid+1);
00233   machine_config.addInt("CPU_Cores_Per_Socket", cores_per_socket);
00234   machine_config.addInt("CPU_Total_HyperThreads", threads);
00235   return machine_config;
00236 }
00237 
00238 PerfTestResult
00239 PerfTest_CheckOrAdd_Test (XMLTestNode machine_config,
00240                           XMLTestNode new_test,
00241                           const std::string filename,
00242                           const std::string ext_hostname)
00243 {
00244   XMLTestNode database;
00245   PerfTestResult return_value = PerfTestPassed;
00246   bool is_new_config = true;
00247 
00248   // Open Database File
00249   //
00250   // FIXME (mfh 09 Apr 2014) This actually opens the file twice.
00251   if (std::ifstream (filename.c_str ())) {
00252     database = FileInputSource (filename).getObject ();
00253   }
00254 
00255   // Get Current Hostname
00256   char hostname[256];
00257   memset (hostname, 0, 256);
00258   if (ext_hostname.empty ()) {
00259     gethostname (hostname, 255);
00260   } else {
00261     strncat (hostname, ext_hostname.c_str (), 255);
00262   }
00263 
00264   XMLTestNode new_test_entry = new_test.getChild ("TestEntry");
00265 
00266   if (database.isEmpty ()) {
00267     database = XMLTestNode ("PerfTests");
00268   }
00269   // Does hostname exist?
00270   if (database.hasChild (hostname)) {
00271     XMLTestNode machine = database.getChild (hostname);
00272 
00273     // Find matching machine configuration
00274     for (int i = 0; i < machine.numChildren (); ++i) {
00275       XMLTestNode configuration = machine.getChild (i);
00276       TEUCHOS_TEST_FOR_EXCEPTION(
00277         configuration.getTag ().compare ("Configuration") != 0,
00278         std::runtime_error, "Unexpected Tag \"" << configuration.getTag ()
00279         << "\"; only children with Tag = \"Configuration\" are allowed in a "
00280         "MachineEntry.");
00281 
00282       TEUCHOS_TEST_FOR_EXCEPTION(
00283         ! configuration.hasChild ("MachineConfiguration") ||
00284         ! configuration.hasChild ("Tests"),
00285         std::runtime_error,
00286         "A Configuration needs to have a child \"MachineConfiguration\" and a "
00287         "child \"Tests\".");
00288 
00289       XMLTestNode machine_configuration = configuration.getChild ("MachineConfiguration");
00290       XMLTestNode old_tests = configuration.getChild ("Tests");
00291 
00292       if (machine_configuration.hasSameElements (machine_config)) {
00293         is_new_config = false;
00294 
00295         // Find existing test with same tag as the new test
00296         if (old_tests.hasChild (new_test.getTag ())) {
00297 
00298           XMLTestNode old_test = old_tests.getChild (new_test.getTag ());
00299 
00300           int new_test_config = -1;
00301           for (int k = 0; k < old_test.numChildren (); ++k) {
00302             XMLTestNode old_test_entry = old_test.getChild (k);
00303 
00304             TEUCHOS_TEST_FOR_EXCEPTION(
00305               ! old_test_entry.hasChild ("TestConfiguration") ||
00306               ! new_test_entry.hasChild ("TestResults"),
00307               std::runtime_error, "A TestEntry needs to have a child "
00308               "\"TestConfiguration\" and a child \"TestResults\".");
00309 
00310             if (old_test_entry.getChild ("TestConfiguration").hasSameElements (new_test_entry.getChild ("TestConfiguration"))) {
00311               new_test_config = k;
00312             }
00313           }
00314 
00315           if (new_test_config < 0) {
00316             old_test.addChild (new_test_entry);
00317             return_value = PerfTestNewTestConfiguration;
00318           } else {
00319             bool deviation = false;
00320             XMLTestNode old_test_entry = old_test.getChild (new_test_config);
00321             XMLTestNode old_results = old_test_entry.getChild ("TestResults");
00322             XMLTestNode new_results = new_test_entry.getChild ("TestResults");
00323 
00324             // Compare all entries
00325             for (int old_r = 0; old_r < old_results.numChildren (); ++old_r) {
00326               XMLTestNode result_entry = old_results.getChild (old_r);
00327 
00328               // Finding entry with same name
00329               bool exists = new_results.hasChild (result_entry.getTag ());
00330 
00331               if (exists) {
00332                 std::string oldv_str = result_entry.getContentLine (0);
00333 
00334                 // If it is a time or result compare numeric values with tolerance
00335                 if((result_entry.getTag().find("Time")==0) || (result_entry.getTag().find("Result")==0)) {
00336                   ValueTolerance old_valtol(oldv_str);
00337                   ValueTolerance new_valtol(new_results.getChild(result_entry.getTag()).getContentLine(0));
00338 
00339                   if(old_valtol.use_tolerance) {
00340                     double diff = old_valtol.value - new_valtol.value;
00341                     diff*=diff;
00342 
00343                     double normalization = old_valtol.value;
00344                     normalization*=normalization;
00345 
00346                     if(normalization==0?diff>0:diff/normalization>old_valtol.tolerance*old_valtol.tolerance) {
00347                       deviation = true;
00348                       std::cout << std::endl
00349                           << "DeviationA in Test: \"" << old_test.getTag()
00350                           << "\" for entry \"" <<  result_entry.getTag() << "\"" << std::endl;
00351                       std::cout << "  Existing Value: \"" << oldv_str << "\"" << std::endl;
00352                       std::cout << "  New Value:      \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
00353                     }
00354                   } else {
00355                     if( (old_valtol.lower>new_valtol.value) || (old_valtol.upper<new_valtol.value)) {
00356                       deviation = true;
00357                       std::cout << std::endl
00358                           << "DeviationB in Test: \"" << old_test.getTag()
00359                           << "\" for entry \"" <<  result_entry.getTag() << "\"" << std::endl;
00360                       std::cout << "  Existing Value: \"" << oldv_str << "\"" << std::endl;
00361                       std::cout << "  New Value:      \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
00362                     }
00363                   }
00364                 } else {
00365                   // Compare exact match for every other type of entry
00366                   if(oldv_str.compare(new_results.getChild(result_entry.getTag()).getContentLine(0))!=0) {
00367                     deviation = true;
00368                     std::cout << std::endl
00369                         << "DeviationC in Test: \"" << old_test.getTag()
00370                         << "\" for entry \"" <<  result_entry.getTag() << "\"" << std::endl;
00371                     std::cout << "  Existing Value: \"" << oldv_str << "\"" << std::endl;
00372                     std::cout << "  New Value:      \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
00373                   }
00374                 }
00375               }
00376               // An old value was not given in the new test: this is an error;
00377               if(!exists) {
00378                 std::cout << "Error New test has same name as an existing one, but one of the old entries is missing." << std::endl;
00379                 deviation = true;
00380               }
00381             }
00382 
00383             if(deviation) { return_value = PerfTestFailed; }
00384             else {
00385               // Did someone add new values to the test?
00386               if(new_results.numChildren()!=old_results.numChildren()) {
00387                 for(int new_r = 0; new_r < new_results.numChildren() ; new_r++) {
00388                   if(!old_results.hasChild(new_results.getChild(new_r).getTag())) {
00389                     old_results.addChild(new_results.getChild(new_r));
00390                   }
00391                 }
00392 
00393                 return_value = PerfTestUpdatedTest;
00394               }
00395             }
00396           }
00397         } else { // End Test Exists
00398           // Add new test if no match was found
00399           old_tests.addChild(new_test);
00400           return_value = PerfTestNewTest;
00401         }
00402       } // End MachineConfiguration Exists
00403     } // End loop over MachineConfigurations
00404 
00405     // Did not find matching MachineConfiguration
00406     if(is_new_config) {
00407       XMLTestNode config("Configuration");
00408       config.addChild(machine_config);
00409       XMLTestNode tests("Tests");
00410       tests.addChild(new_test);
00411 
00412       config.addChild(tests);
00413       machine.addChild(config);
00414 
00415       return_value = PerfTestNewConfiguration;
00416     }
00417   } else { // Machine Entry does not exist
00418     XMLTestNode machine(hostname);
00419 
00420     XMLTestNode config("Configuration");
00421     config.addChild(machine_config);
00422     XMLTestNode tests("Tests");
00423     tests.addChild(new_test);
00424     config.addChild(tests);
00425 
00426     machine.addChild(config);
00427 
00428     database.addChild(machine);
00429 
00430     return_value = PerfTestNewMachine;
00431   }
00432 
00433 
00434   if(return_value>PerfTestPassed) {
00435     std::ofstream fout(filename.c_str());
00436     fout << database << std::endl;
00437   }
00438 
00439   return return_value;
00440 }
00441 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines