JD2022-TU1/main/extern/gear4/gear_testing/performance/perftest_logger.cpp

311 lines
11 KiB
C++

#include "perftest_logger.h"
#include <gear_core/eal_utils/private/itextstream.h>
#include <gear_testing/core/stddef.h>
#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<unsigned char>(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; i<nbAttributes; i++)
node->SetAttribute(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 G4::Container<class T<char, struct Tr<char> >, struct A, class B<> > >::SetUpTestCase") to obtain ("container<T>" or "mapcontainer<T,A>")
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<GearString, EalAllocator<EAL_ID_TG_GEAR_TESTING> > 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<unsigned char>(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