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