Teuchos - Trilinos Tools Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_XMLPerfTestArchive.cpp
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>
50 #ifdef _WIN32
51 #include <winsock2.h>
52 #else
53 #include <unistd.h>
54 #endif
55 
56 namespace Teuchos {
57 
58 ValueTolerance::ValueTolerance() {
59  value = 0;
60  lower = 0;
61  upper = 0;
62  tolerance = 0;
63  use_tolerance = true;
64 }
65 
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 
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 
82 ValueTolerance::ValueTolerance(std::string str) {
83  from_string(str);
84 }
85 
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 
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 
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 
119  XMLTestNode::XMLTestNode():XMLObject() {}
120 
121  XMLTestNode::XMLTestNode(const std::string &tag):XMLObject(tag) {}
122 
123  XMLTestNode::XMLTestNode(XMLObjectImplem *ptr):XMLObject(ptr) {}
124 
125  XMLTestNode::XMLTestNode(XMLObject obj):XMLObject(obj) {}
126 
127  void XMLTestNode::addDouble (const std::string &name, double val) {
128  addAttribute<double>(name,val);
129  }
130 
131  void XMLTestNode::addInt (const std::string &name, int val) {
132  addAttribute<int>(name,val);
133  }
134 
135  void XMLTestNode::addBool (const std::string &name, bool val) {
136  addAttribute<bool>(name,val);
137  }
138 
139  void XMLTestNode::addValueTolerance(const std::string &name, ValueTolerance val){
140  addAttribute<std::string>(name,val.as_string());
141  }
142 
143  void XMLTestNode::addString (const std::string &name, std::string val) {
144  addAttribute<std::string>(name,val);
145  }
146 
147  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 
158  void XMLTestNode::appendContentLine(const size_t& i, const std::string &str) {
159  ptr_->appendContentLine(i,str);
160  }
161 
162  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 
171  XMLTestNode XMLTestNode::getChild(const int &i) const {
172  return XMLObject::getChild(i);
173  }
174 
175  const XMLObject* XMLTestNode::xml_object() const {
176  return (XMLObject*) this;
177  }
178 
179  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 
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 
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);
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 
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 
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 }
const std::string & getTag() const
Return the tag of the current node.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
void addChild(const XMLObject &child)
Add a child node to the node.
bool isEmpty() const
Find out if a node is empty.
XMLObject()
Empty constructor.
ValueTolerance is a struct to keep a tuple of value and a tolerance. The tolerance can be either expr...
Tools for an XML-based performance test archive.
XMLObject getObject() const
Get an object by invoking the TreeBuildingXMLHandler on the input data.
Instantiation of XMLInputSource class for reading XML from a file.
PerfTestResult
ReturnValues for PerfTest_CheckOrAdd_Test.
int numChildren() const
Return the number of child nodes owned by this node.
Subclass of XMLObject used by the performance archive.
int numContentLines() const
Return the number of lines of character content stored in this node.
Definition of XMLInputSource derived class for reading XML from a file.
const XMLObject & getChild(int i) const
Return the i-th child node.
const std::string & getContentLine(int i) const
Return the i-th line of character content stored in this node.
PerfTestResult PerfTest_CheckOrAdd_Test(XMLTestNode machine_config, XMLTestNode new_test, const std::string filename, const std::string ext_hostname)
Check whether a test is present and match an existing test in an archive.
XMLTestNode PerfTest_MachineConfig()
PerfTest_MachineConfig generates a basic machine configuration XMLTestNode.
An object representation of a subset of XML data.