#include "perftest_logger.h" #include #include #if G4_PLATFORM != G4_PLATFORM_PS3_SPU #define APP_LEVEL 1 #define MODULE_LEVEL APP_LEVEL+1 #define CLASS_LEVEL MODULE_LEVEL+1 #define TEST_LEVEL CLASS_LEVEL+1 #define NB_MAX_ATTRIBUTES 2 #define MAX_ATTRIBUTE_CHAR_SIZE 256 #define PERFTEST_LOGGER_XMLFILENAME "perftests_results.xml" char s_attributes[NB_MAX_ATTRIBUTES*2][MAX_ATTRIBUTE_CHAR_SIZE]; namespace G4 { G4_IMPLEMENT_DYNAMIC_SINGLETON(PerfTestLogger); // Set attribute name and value for the nth attribute in the static char * array static void SetAttribute(const char *name, const char *value, const int index) { #if G4_PLATFORM_MS strcpy_s(s_attributes[2*index], MAX_ATTRIBUTE_CHAR_SIZE, name); strcpy_s(s_attributes[2*index+1], MAX_ATTRIBUTE_CHAR_SIZE, value); #else strcpy(s_attributes[2*index], name); strcpy(s_attributes[2*index+1], value); #endif } void PerfTestLogger::SingletonConstructor() { m_currentFunction.Clear(); // initialize xml tree m_xmlDoc.InsertFirstChild( m_xmlDoc.NewDeclaration() ); m_xmlDoc.SetBOM( true ); m_xmlDoc.InsertEndChild(m_xmlDoc.NewComment("PerfTest results are calculated in usec.")); // create root tinyxml2::XMLNode *perfRootNode = m_xmlDoc.NewElement("PerformanceTestsRoot"); m_xmlDoc.InsertEndChild(perfRootNode); m_xmlTree.push_back(perfRootNode); } void PerfTestLogger::SingletonDestructor() { // check if we need to save the xml file if (m_xmlTree.size() > 2) { tinyxml2::XMLPrinter lXmlPrinter; m_xmlDoc.Print(&lXmlPrinter); G4::BaseErrorCode err; G4::EalFile outputXmlFile(PERFTEST_LOGGER_XMLFILENAME, G4::EalFile::MODE_CREATE|G4::EalFile::MODE_WRITE, G4_TEST_EALID, err); if(err == G4::ErrorGearCore::Ok) { G4::U32 writtenSize = 0; err = outputXmlFile.Write(lXmlPrinter.CStr(), lXmlPrinter.CStrSize()-1, writtenSize); if(err == G4::ErrorGearCore::Ok) { outputXmlFile.Close(); // display xml file to console G4::U8 OBaseStreamBuffer[1024]; Private::ITextStream xmlStream(PERFTEST_LOGGER_XMLFILENAME, OBaseStreamBuffer, 1024); if(xmlStream.IsValid()) { G4::Char TextBuffer[256]; while(xmlStream.ReadLine(TextBuffer, 256) != 0) { ::testing::internal::UniversalPrintf("%s\n", TextBuffer); } xmlStream.CloseFile(); } else { ::testing::internal::UniversalPrintf("ERROR: Could not open performance test XML log file \"%s\" for reading\n", PERFTEST_LOGGER_XMLFILENAME); } } else { ::testing::internal::UniversalPrintf("ERROR: Could not write in performance test XML log file \"%s\"\n", PERFTEST_LOGGER_XMLFILENAME); } } else { ::testing::internal::UniversalPrintf("ERROR: Could not open performance test XML log file \"%s\" for writing\n", PERFTEST_LOGGER_XMLFILENAME); } } m_xmlTree.clear(); m_xmlDoc.Clear(); m_currentFunction.Clear(); } Bool PerfTestLogger::AddXmlNode( unsigned char a_level, const char* name, const unsigned int nbAttributes ) { const unsigned char currentHeight = static_cast(m_xmlTree.size()); G4_EAL_ASSERT_MSG(EAL_ID_TG_GEAR_CORE, a_level <= currentHeight, "The max level is %i", currentHeight); if(currentHeight < a_level) return False; // Add it to the tree if it does not exist already if( currentHeight > a_level ) { // Forward search tinyxml2::XMLElement *tmpSibling = (a_level==0)?m_xmlDoc.FirstChildElement():m_xmlTree[a_level-1]->FirstChildElement(); while(tmpSibling) { if(tmpSibling->Attribute(s_attributes[0], s_attributes[1]) != 0) { m_xmlTree[a_level] = tmpSibling; return True; } tmpSibling = tmpSibling->NextSiblingElement(); } } // Create the new node tinyxml2::XMLElement* node = m_xmlDoc.NewElement(name); for(unsigned int i=0; iSetAttribute(s_attributes[2*i], s_attributes[2*i+1]); if(a_level == 0) m_xmlDoc.FirstChildElement()->InsertEndChild(node); else { m_xmlTree[a_level-1]->InsertEndChild(node); } if( currentHeight == a_level ) m_xmlTree.push_back(node); else m_xmlTree[a_level] = node; return True; } // This function modifies classname in a way we obtain the same class name for Gear4 container than STL container // it parses className ("UnitTest >, struct A, class B<> > >::SetUpTestCase") to obtain ("container" or "mapcontainer") void GetTemplateClassname(GearString& className) { className.Replace("UnitTest<", ""); className.Replace("TYPE>::SetUpTestCase() [with TYPE = ", ""); className.Replace("]", ""); className.Replace("class ", ""); className.Replace("struct ", ""); className.Replace("G4::", ""); className.Replace("std::", ""); className.Replace(">::SetUpTestCase", ""); className.Replace("basicstring", "string"); className.ToLower(); // erase everything between <> GearString::Iterator itClassName; bool parseIsDone = false; do { itClassName = className.Begin(); G4::S32 bracketCount = 0; GearString::Iterator eraseBegin, eraseEnd; while(!parseIsDone) { if (*itClassName == '<') { ++bracketCount; if (bracketCount == 2) { eraseBegin = itClassName; } } else if (*itClassName == '>') { --bracketCount; if (bracketCount == 1) { eraseEnd = itClassName; break; } } ++itClassName; parseIsDone = (itClassName == className.End()); } if (!parseIsDone) { className.Erase(eraseBegin, ++eraseEnd); } } while (!parseIsDone); // if className contains 'map', we let two template arguments, otherwise, we keep only one G4::S32 commaIdx = className.Find(','); G4::S32 mapIdx = className.Find("map"); if (mapIdx != GearString::NPOS) { // keep a second arg commaIdx = className.Find(',', ++commaIdx); } if (commaIdx != GearString::NPOS) { className.Erase(commaIdx, className.Size() - commaIdx - 1); } className.Replace(" ", ""); } /// This class is specific to your own needs. You can change it to set your default app name, module name and class name. /// This is useful because these field are mandatory to be able to log a test result. Bool PerfTestLogger::SetTestCase(const GearString& a_testCaseName) { typedef Vector > TestKeys; TestKeys testKeys; a_testCaseName.Split("_", testKeys); TestKeys::const_iterator itKeys = testKeys.cbegin(); if (!itKeys->EndsWith("perf")) return False; ++itKeys; if (*itKeys == "havok") //Havok { SetAppName("Havok", G4_PLATFORM_ACRONYM); SetModuleName("math"); SetClassName(testKeys[2].CStr()); } else if (*itKeys == "stl") { SetAppName("STL", G4_PLATFORM_ACRONYM); ++itKeys; } else // default application { SetAppName("gear_core", G4_PLATFORM_ACRONYM); } SetModuleName(itKeys->CStr()); ++itKeys; // class name is either defined following the module name (testKeys[2]) or between <> after UnitTest GearString className = *itKeys; ++itKeys; while (!itKeys->StartsWith("UnitTest")) { className += "_"; className += *itKeys; ++itKeys; } // check if classname is template if (itKeys->StartsWith("UnitTest<")) { // className is between <> className = *itKeys; while (++itKeys != testKeys.cend()) { className += *itKeys; // we don't add '_' to make the same names between G4 and std } GetTemplateClassname(className); } SetClassName(className.CStr()); return True; } Bool PerfTestLogger::SetAppName(const char* a_appName, const char* a_version) { SetAttribute("name", a_appName, 0); SetAttribute("version", a_version, 1); return AddXmlNode(APP_LEVEL, "application", 2); } Bool PerfTestLogger::SetModuleName(const char* a_module) { SetAttribute("name", a_module, 0); return AddXmlNode(MODULE_LEVEL, "module", 1); } Bool PerfTestLogger::SetClassName(const char* a_class) { SetAttribute("name", a_class, 0); return AddXmlNode(CLASS_LEVEL, "class", 1); } Bool PerfTestLogger::LogTestName(const GearString& a_function) { // check if we are ready to add perftest node (level 4) static const unsigned char level = TEST_LEVEL; const unsigned char currentHeight = static_cast(m_xmlTree.size()); G4_EAL_ASSERT_MSG(EAL_ID_TG_GEAR_CORE, !a_function.Empty(), "Function name is mandatory."); // This means you are trying to log a test without having set the class name for this test G4_EAL_ASSERT_MSG(EAL_ID_TG_GEAR_CORE, currentHeight >= level, "Can't add a node to level %i. Current height is %i. Use AddClassName before", level, currentHeight ); if (currentHeight < level) return False; // set the current function name m_currentFunction = a_function; return True; } Bool PerfTestLogger::LogTestResult(double a_testResult) { SetAttribute("name", m_currentFunction.CStr(), 0); char buffer[MAX_ATTRIBUTE_CHAR_SIZE]; #if G4_PLATFORM_MS sprintf_s(buffer, MAX_ATTRIBUTE_CHAR_SIZE, "%6.6f", a_testResult); #else sprintf(buffer, "%6.6f", a_testResult); #endif SetAttribute("result", buffer, 1); return AddXmlNode(TEST_LEVEL, "perftest", 2); } } #endif // #if G4_PLATFORM != G4_PLATFORM_PS3_SPU