Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_XMLPerfTestArchive.cpp
Go to the documentation of this file.
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 
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 
83  from_string(str);
84 }
85 
87  return (value == rhs.value) &&
88  (tolerance == rhs.tolerance) &&
89  (lower == rhs.lower) &&
90  (upper == rhs.upper) &&
92 }
93 
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 
120 
121  XMLTestNode::XMLTestNode(const std::string &tag):XMLObject(tag) {}
122 
124 
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 
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.
XMLTestNode getChild(const std::string &name) const
#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.
void addDouble(const std::string &name, double val)
bool hasChild(const std::string &name) const
bool isEmpty() const
Find out if a node is empty.
ValueTolerance is a struct to keep a tuple of value and a tolerance. The tolerance can be either expr...
void addValueTolerance(const std::string &name, ValueTolerance val)
Tools for an XML-based performance test archive.
void addInt(const std::string &name, int val)
RCP< XMLObjectImplem > ptr_
Representation of an XML data tree. XMLObject is a ref-counted handle to a XMLObjectImplem object...
XMLObject getObject() const
Get an object by invoking the TreeBuildingXMLHandler on the input data.
void appendContentLine(const size_t &i, const std::string &str)
#define TEUCHOS_COMPILER_NAME
Instantiation of XMLInputSource class for reading XML from a file.
void from_string(const std::string &valtol_str)
PerfTestResult
ReturnValues for PerfTest_CheckOrAdd_Test.
int numChildren() const
Return the number of child nodes owned by this node.
void addBool(const std::string &name, bool val)
Subclass of XMLObject used by the performance archive.
void addString(const std::string &name, std::string val)
int numContentLines() const
Return the number of lines of character content stored in this node.
bool hasSameElements(XMLTestNode const &lhs) const
The XMLObjectImplem class takes care of the low-level implementation details of XMLObject.
Definition of XMLInputSource derived class for reading XML from a file.
bool operator==(ValueTolerance &rhs)
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.
const XMLObject * xml_object() const
#define TEUCHOS_COMPILER_VERSION
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.