1 // @HEADER
2 // ***********************************************************************
3 //
4 //                    Teuchos: Common Tools Package
5 //                 Copyright (2004) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
38 //
39 // ***********************************************************************
40 // @HEADER
41 
42 
43 #include <iostream>
44 #include <fstream>
45 #include <cstring>
46 #include <cstdlib>
47 #include <Teuchos_XMLObject.hpp>
48 #include <Teuchos_FileInputSource.hpp>
49 #include <Teuchos_XMLPerfTestArchive.hpp>
50 #ifdef _WIN32
51 #include <winsock2.h>
52 #else
53 #include <unistd.h>
54 #endif
55 
56 namespace Teuchos {
57 
ValueTolerance()58 ValueTolerance::ValueTolerance() {
59   value = 0;
60   lower = 0;
61   upper = 0;
62   tolerance = 0;
63   use_tolerance = true;
64 }
65 
ValueTolerance(double val,double tol)66 ValueTolerance::ValueTolerance(double val, double tol) {
67   value = val;
68   lower = 0;
69   upper = 0;
70   tolerance = tol;
71   use_tolerance = true;
72 }
73 
ValueTolerance(double val,double low,double up)74 ValueTolerance::ValueTolerance(double val, double low, double up) {
75   value = val;
76   upper = up;
77   lower = low;
78   tolerance = 0;
79   use_tolerance = false;
80 }
81 
ValueTolerance(std::string str)82 ValueTolerance::ValueTolerance(std::string str) {
83   from_string(str);
84 }
85 
operator ==(ValueTolerance & rhs)86 bool ValueTolerance::operator ==(ValueTolerance& rhs) {
87   return (value == rhs.value) &&
88          (tolerance == rhs.tolerance) &&
89          (lower == rhs.lower) &&
90          (upper == rhs.upper) &&
91          (use_tolerance == rhs.use_tolerance);
92 }
93 
as_string()94 std::string ValueTolerance::as_string(){
95   std::ostringstream strs;
96   if(use_tolerance)
97     strs << value << " , " << tolerance;
98   else
99     strs << value << " , " << lower << " , " << upper;
100   return  strs.str();
101 }
102 
from_string(const std::string & valtol_str)103 void ValueTolerance::from_string(const std::string& valtol_str) {
104   std::string value_str = valtol_str.substr(0,valtol_str.find(","));
105   value = atof(value_str.c_str());
106   std::string tol_str = valtol_str.substr(valtol_str.find(",")+1);
107   if(tol_str.find(",")<=tol_str.length()) {
108     use_tolerance = false;
109     std::string lower_str = tol_str.substr(0,tol_str.find(","));
110     lower = atof(lower_str.c_str());
111     std::string upper_str = tol_str.substr(tol_str.find(",")+1);
112     upper = atof(upper_str.c_str());
113   } else {
114     use_tolerance = true;
115     tolerance = atof(tol_str.c_str());
116   }
117 }
118 
XMLTestNode()119   XMLTestNode::XMLTestNode():XMLObject() {}
120 
XMLTestNode(const std::string & tag)121   XMLTestNode::XMLTestNode(const std::string &tag):XMLObject(tag) {}
122 
XMLTestNode(XMLObjectImplem * ptr)123   XMLTestNode::XMLTestNode(XMLObjectImplem *ptr):XMLObject(ptr) {}
124 
XMLTestNode(XMLObject obj)125   XMLTestNode::XMLTestNode(XMLObject obj):XMLObject(obj) {}
126 
addDouble(const std::string & name,double val)127   void  XMLTestNode::addDouble (const std::string &name, double val) {
128     addAttribute<double>(name,val);
129   }
130 
addInt(const std::string & name,int val)131   void  XMLTestNode::addInt (const std::string &name, int val) {
132     addAttribute<int>(name,val);
133   }
134 
addBool(const std::string & name,bool val)135   void  XMLTestNode::addBool (const std::string &name, bool val) {
136     addAttribute<bool>(name,val);
137   }
138 
addValueTolerance(const std::string & name,ValueTolerance val)139   void XMLTestNode::addValueTolerance(const std::string &name, ValueTolerance val){
140     addAttribute<std::string>(name,val.as_string());
141   }
142 
addString(const std::string & name,std::string val)143   void  XMLTestNode::addString (const std::string &name, std::string val) {
144     addAttribute<std::string>(name,val);
145   }
146 
hasChild(const std::string & name) const147   bool XMLTestNode::hasChild(const std::string &name) const {
148     bool found = false;
149     for(int i = 0; i < numChildren(); i++) {
150       if(name.compare(XMLObject::getChild(i).getTag()) == 0) {
151         found = true;
152         i = numChildren();
153       }
154     }
155     return found;
156   }
157 
appendContentLine(const size_t & i,const std::string & str)158   void XMLTestNode::appendContentLine(const size_t& i, const std::string &str) {
159     ptr_->appendContentLine(i,str);
160   }
161 
getChild(const std::string & name) const162   XMLTestNode XMLTestNode::getChild(const std::string &name) const {
163     XMLTestNode child;
164     for(int i = 0; i < numChildren(); i++) {
165       if(name.compare(XMLObject::getChild(i).getTag()) == 0)
166         child = XMLObject::getChild(i);
167     }
168     return child;
169   }
170 
getChild(const int & i) const171   XMLTestNode XMLTestNode::getChild(const int &i) const {
172     return XMLObject::getChild(i);
173   }
174 
xml_object() const175   const XMLObject* XMLTestNode::xml_object() const {
176     return (XMLObject*) this;
177   }
178 
hasSameElements(XMLTestNode const & lhs) const179   bool XMLTestNode::hasSameElements(XMLTestNode const & lhs) const {
180 
181     if((numChildren()!=lhs.numChildren()) ||
182        (numContentLines()!= lhs.numContentLines()) ||
183        (getTag().compare(lhs.getTag())!=0)) return false;
184 
185     for(int i = 0; i<numChildren(); i++) {
186       const XMLTestNode child = XMLObject::getChild(i);
187       if( (!lhs.hasChild(child.getTag())) ||
188           (!child.hasSameElements(lhs.getChild(child.getTag()))) ) return false;
189     }
190 
191     for(int i = 0; i<numContentLines(); i++)
192       if(getContentLine(i).compare(lhs.getContentLine(i))!=0) return false;
193 
194     return true;
195   }
196 
PerfTest_MachineConfig()197 XMLTestNode PerfTest_MachineConfig() {
198 
199   // Get CPUName, Number of Sockets, Number of Cores, Number of Hyperthreads
200   std::string cpuname("Undefined");
201   unsigned int threads = 0;
202   unsigned int cores_per_socket = 0;
203   unsigned int highest_socketid = 0;
204 
205   {
206     std::ifstream cpuinfo("/proc/cpuinfo");
207     std::string line;
208     if((cpuinfo.rdstate()&cpuinfo.failbit)) std::cout<<"Failed to open filen\n";
209     while (!cpuinfo.eof() && !(cpuinfo.rdstate()&cpuinfo.failbit)) {
210       getline (cpuinfo,line);
211       if (line.find("model name") < line.size()) {
212         cpuname = line.substr(line.find(":")+2);
213         threads++;
214       }
215       if (line.find("physical id") < line.size()) {
216         unsigned int socketid = atoi(line.substr(line.find(":")+2).c_str());
217         highest_socketid = highest_socketid>socketid?highest_socketid:socketid;
218       }
219       if (line.find("cpu cores") < line.size()) {
220         cores_per_socket = atoi(line.substr(line.find(":")+2).c_str());
221       }
222     }
223   }
224 
225 
226   XMLTestNode machine_config("MachineConfiguration");
227 
228   machine_config.addString("Compiler", TEUCHOS_COMPILER_NAME);
229   machine_config.addInt("Compiler_Version",  TEUCHOS_COMPILER_VERSION);
230   machine_config.addString("CPU_Name", cpuname);
231   machine_config.addInt("CPU_Sockets", highest_socketid+1);
232   machine_config.addInt("CPU_Cores_Per_Socket", cores_per_socket);
233   machine_config.addInt("CPU_Total_HyperThreads", threads);
234   return machine_config;
235 }
236 
237 PerfTestResult
PerfTest_CheckOrAdd_Test(XMLTestNode machine_config,XMLTestNode new_test,const std::string filename,const std::string ext_hostname)238 PerfTest_CheckOrAdd_Test (XMLTestNode machine_config,
239                           XMLTestNode new_test,
240                           const std::string filename,
241                           const std::string ext_hostname)
242 {
243   XMLTestNode database;
244   PerfTestResult return_value = PerfTestPassed;
245   bool is_new_config = true;
246 
247   // Open Database File
248   //
249   // FIXME (mfh 09 Apr 2014) This actually opens the file twice.
250   if (std::ifstream (filename.c_str ())) {
251     database = FileInputSource (filename).getObject ();
252   }
253 
254   // Get Current Hostname
255   char hostname[256];
256   memset (hostname, 0, 256);
257   if (ext_hostname.empty ()) {
258     gethostname (hostname, 255);
259   } else {
260     strncat (hostname, ext_hostname.c_str (), 255);
261   }
262 
263   XMLTestNode new_test_entry = new_test.getChild ("TestEntry");
264 
265   if (database.isEmpty ()) {
266     database = XMLTestNode ("PerfTests");
267   }
268   // Does hostname exist?
269   if (database.hasChild (hostname)) {
270     XMLTestNode machine = database.getChild (hostname);
271 
272     // Find matching machine configuration
273     for (int i = 0; i < machine.numChildren (); ++i) {
274       XMLTestNode configuration = machine.getChild (i);
275       TEUCHOS_TEST_FOR_EXCEPTION(
276         configuration.getTag ().compare ("Configuration") != 0,
277         std::runtime_error, "Unexpected Tag \"" << configuration.getTag ()
278         << "\"; only children with Tag = \"Configuration\" are allowed in a "
279         "MachineEntry.");
280 
281       TEUCHOS_TEST_FOR_EXCEPTION(
282         ! configuration.hasChild ("MachineConfiguration") ||
283         ! configuration.hasChild ("Tests"),
284         std::runtime_error,
285         "A Configuration needs to have a child \"MachineConfiguration\" and a "
286         "child \"Tests\".");
287 
288       XMLTestNode machine_configuration = configuration.getChild ("MachineConfiguration");
289       XMLTestNode old_tests = configuration.getChild ("Tests");
290 
291       if (machine_configuration.hasSameElements (machine_config)) {
292         is_new_config = false;
293 
294         // Find existing test with same tag as the new test
295         if (old_tests.hasChild (new_test.getTag ())) {
296 
297           XMLTestNode old_test = old_tests.getChild (new_test.getTag ());
298 
299           int new_test_config = -1;
300           for (int k = 0; k < old_test.numChildren (); ++k) {
301             XMLTestNode old_test_entry = old_test.getChild (k);
302 
303             TEUCHOS_TEST_FOR_EXCEPTION(
304               ! old_test_entry.hasChild ("TestConfiguration") ||
305               ! new_test_entry.hasChild ("TestResults"),
306               std::runtime_error, "A TestEntry needs to have a child "
307               "\"TestConfiguration\" and a child \"TestResults\".");
308 
309             if (old_test_entry.getChild ("TestConfiguration").hasSameElements (new_test_entry.getChild ("TestConfiguration"))) {
310               new_test_config = k;
311             }
312           }
313 
314           if (new_test_config < 0) {
315             old_test.addChild (new_test_entry);
316             return_value = PerfTestNewTestConfiguration;
317           } else {
318             bool deviation = false;
319             XMLTestNode old_test_entry = old_test.getChild (new_test_config);
320             XMLTestNode old_results = old_test_entry.getChild ("TestResults");
321             XMLTestNode new_results = new_test_entry.getChild ("TestResults");
322 
323             // Compare all entries
324             for (int old_r = 0; old_r < old_results.numChildren (); ++old_r) {
325               XMLTestNode result_entry = old_results.getChild (old_r);
326 
327               // Finding entry with same name
328               bool exists = new_results.hasChild (result_entry.getTag ());
329 
330               if (exists) {
331                 std::string oldv_str = result_entry.getContentLine (0);
332 
333                 // If it is a time or result compare numeric values with tolerance
334                 if((result_entry.getTag().find("Time")==0) || (result_entry.getTag().find("Result")==0)) {
335                   ValueTolerance old_valtol(oldv_str);
336                   ValueTolerance new_valtol(new_results.getChild(result_entry.getTag()).getContentLine(0));
337 
338                   if(old_valtol.use_tolerance) {
339                     double diff = old_valtol.value - new_valtol.value;
340                     diff*=diff;
341 
342                     double normalization = old_valtol.value;
343                     normalization*=normalization;
344 
345                     if(normalization==0?diff>0:diff/normalization>old_valtol.tolerance*old_valtol.tolerance) {
346                       deviation = true;
347                       std::cout << std::endl
348                           << "DeviationA in Test: \"" << old_test.getTag()
349                           << "\" for entry \"" <<  result_entry.getTag() << "\"" << std::endl;
350                       std::cout << "  Existing Value: \"" << oldv_str << "\"" << std::endl;
351                       std::cout << "  New Value:      \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
352                     }
353                   } else {
354                     if( (old_valtol.lower>new_valtol.value) || (old_valtol.upper<new_valtol.value)) {
355                       deviation = true;
356                       std::cout << std::endl
357                           << "DeviationB in Test: \"" << old_test.getTag()
358                           << "\" for entry \"" <<  result_entry.getTag() << "\"" << std::endl;
359                       std::cout << "  Existing Value: \"" << oldv_str << "\"" << std::endl;
360                       std::cout << "  New Value:      \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
361                     }
362                   }
363                 } else {
364                   // Compare exact match for every other type of entry
365                   if(oldv_str.compare(new_results.getChild(result_entry.getTag()).getContentLine(0))!=0) {
366                     deviation = true;
367                     std::cout << std::endl
368                         << "DeviationC in Test: \"" << old_test.getTag()
369                         << "\" for entry \"" <<  result_entry.getTag() << "\"" << std::endl;
370                     std::cout << "  Existing Value: \"" << oldv_str << "\"" << std::endl;
371                     std::cout << "  New Value:      \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
372                   }
373                 }
374               }
375               // An old value was not given in the new test: this is an error;
376               if(!exists) {
377                 std::cout << "Error New test has same name as an existing one, but one of the old entries is missing." << std::endl;
378                 deviation = true;
379               }
380             }
381 
382             if(deviation) { return_value = PerfTestFailed; }
383             else {
384               // Did someone add new values to the test?
385               if(new_results.numChildren()!=old_results.numChildren()) {
386                 for(int new_r = 0; new_r < new_results.numChildren() ; new_r++) {
387                   if(!old_results.hasChild(new_results.getChild(new_r).getTag())) {
388                     old_results.addChild(new_results.getChild(new_r));
389                   }
390                 }
391 
392                 return_value = PerfTestUpdatedTest;
393               }
394             }
395           }
396         } else { // End Test Exists
397           // Add new test if no match was found
398           old_tests.addChild(new_test);
399           return_value = PerfTestNewTest;
400         }
401       } // End MachineConfiguration Exists
402     } // End loop over MachineConfigurations
403 
404     // Did not find matching MachineConfiguration
405     if(is_new_config) {
406       XMLTestNode config("Configuration");
407       config.addChild(machine_config);
408       XMLTestNode tests("Tests");
409       tests.addChild(new_test);
410 
411       config.addChild(tests);
412       machine.addChild(config);
413 
414       return_value = PerfTestNewConfiguration;
415     }
416   } else { // Machine Entry does not exist
417     XMLTestNode machine(hostname);
418 
419     XMLTestNode config("Configuration");
420     config.addChild(machine_config);
421     XMLTestNode tests("Tests");
422     tests.addChild(new_test);
423     config.addChild(tests);
424 
425     machine.addChild(config);
426 
427     database.addChild(machine);
428 
429     return_value = PerfTestNewMachine;
430   }
431 
432 
433   if(return_value>PerfTestPassed) {
434     std::ofstream fout(filename.c_str());
435     fout << database << std::endl;
436   }
437 
438   return return_value;
439 }
440 }
441