ENH: add support for bounds checker

This commit is contained in:
Bill Hoffman 2007-07-24 14:43:31 -04:00
parent 919265516e
commit 132cb5d479
3 changed files with 315 additions and 46 deletions

View File

@ -16,7 +16,7 @@
=========================================================================*/ =========================================================================*/
#include "cmCTestMemCheckHandler.h" #include "cmCTestMemCheckHandler.h"
#include "cmXMLParser.h"
#include "cmCTest.h" #include "cmCTest.h"
#include "cmake.h" #include "cmake.h"
#include "cmGeneratedFileStream.h" #include "cmGeneratedFileStream.h"
@ -29,6 +29,110 @@
#include <math.h> #include <math.h>
#include <float.h> #include <float.h>
struct CatToErrorType
{
const char* ErrorCategory;
int ErrorCode;
};
static CatToErrorType cmCTestMemCheckBoundsChecker[] = {
// Error tags
{"Write Overrun", cmCTestMemCheckHandler::ABW},
{"Read Overrun", cmCTestMemCheckHandler::ABR},
{"Memory Overrun", cmCTestMemCheckHandler::ABW},
{"Allocation Conflict", cmCTestMemCheckHandler::FMM},
{"Bad Pointer Use", cmCTestMemCheckHandler::FMW},
{"Dangling Pointer", cmCTestMemCheckHandler::FMR},
{0,0}
};
// parse the xml file storing the installed version of Xcode on
// the machine
class cmBoundsCheckerParser : public cmXMLParser
{
public:
cmBoundsCheckerParser(cmCTest* c) { this->CTest = c;}
void StartElement(const char* name, const char** atts)
{
if(strcmp(name, "MemoryLeak") == 0)
{
this->Errors.push_back(cmCTestMemCheckHandler::MLK);
}
if(strcmp(name, "ResourceLeak") == 0)
{
this->Errors.push_back(cmCTestMemCheckHandler::MLK);
}
if(strcmp(name, "Error") == 0)
{
this->ParseError(atts);
}
if(strcmp(name, "Dangling Pointer") == 0)
{
this->ParseError(atts);
}
// Create the log
cmOStringStream ostr;
ostr << name << ":\n";
int i = 0;
for(; atts[i] != 0; i+=2)
{
ostr << " " << this->CTest->MakeXMLSafe(atts[i]).c_str()
<< " - " << this->CTest->MakeXMLSafe(atts[i+1]).c_str() << "\n";
}
ostr << "\n";
this->Log += ostr.str();
}
void EndElement(const char* )
{
}
const char* GetAttribute(const char* name, const char** atts)
{
int i = 0;
for(; atts[i] != 0; ++i)
{
if(strcmp(name, atts[i]) == 0)
{
return atts[i+1];
}
}
return 0;
}
void ParseError(const char** atts)
{
CatToErrorType* ptr = cmCTestMemCheckBoundsChecker;
const char* cat = this->GetAttribute("ErrorCategory", atts);
if(!cat)
{
this->Errors.push_back(cmCTestMemCheckHandler::ABW); // do not know
cmCTestLog(this->CTest, ERROR_MESSAGE,
"No Category found in Bounds checker XML\n" );
return;
}
while(ptr->ErrorCategory && cat)
{
if(strcmp(ptr->ErrorCategory, cat) == 0)
{
this->Errors.push_back(ptr->ErrorCode);
return; // found it we are done
}
ptr++;
}
if(ptr->ErrorCategory)
{
this->Errors.push_back(cmCTestMemCheckHandler::ABW); // do not know
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Found unknown Bounds Checker error "
<< ptr->ErrorCategory << std::endl);
}
}
cmCTest* CTest;
std::vector<int> Errors;
std::string Log;
};
#define BOUNDS_CHECKER_MARKER "******######*****Begin BOUNDS CHECKER XML******######******"
//---------------------------------------------------------------------- //----------------------------------------------------------------------
static const char* cmCTestMemCheckResultStrings[] = { static const char* cmCTestMemCheckResultStrings[] = {
"ABR", "ABR",
@ -56,6 +160,7 @@ static const char* cmCTestMemCheckResultStrings[] = {
0 0
}; };
//---------------------------------------------------------------------- //----------------------------------------------------------------------
static const char* cmCTestMemCheckResultLongStrings[] = { static const char* cmCTestMemCheckResultLongStrings[] = {
"Threading Problem", "Threading Problem",
@ -332,6 +437,13 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
= cmSystemTools::ConvertToOutputPath(this->CTest->GetCTestConfiguration( = cmSystemTools::ConvertToOutputPath(this->CTest->GetCTestConfiguration(
"ValgrindCommand").c_str()); "ValgrindCommand").c_str());
} }
else if ( cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
"BoundsCheckerCommand").c_str()) )
{
this->MemoryTester
= cmSystemTools::ConvertToOutputPath(this->CTest->GetCTestConfiguration(
"BoundsCheckerCommand").c_str());
}
else else
{ {
cmCTestLog(this->CTest, WARNING, cmCTestLog(this->CTest, WARNING,
@ -364,8 +476,6 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
this->MemoryTesterOutputFile this->MemoryTesterOutputFile
= this->CTest->GetBinaryDir() + "/Testing/Temporary/MemoryChecker.log"; = this->CTest->GetBinaryDir() + "/Testing/Temporary/MemoryChecker.log";
this->MemoryTesterOutputFile
= cmSystemTools::EscapeSpaces(this->MemoryTesterOutputFile.c_str());
if ( this->MemoryTester.find("valgrind") != std::string::npos ) if ( this->MemoryTester.find("valgrind") != std::string::npos )
{ {
@ -395,19 +505,31 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
else if ( this->MemoryTester.find("purify") != std::string::npos ) else if ( this->MemoryTester.find("purify") != std::string::npos )
{ {
this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY; this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
std::string outputFile =
cmSystemTools::EscapeSpaces(this->MemoryTesterOutputFile.c_str());
#ifdef _WIN32 #ifdef _WIN32
this->MemoryTesterOptions += " /SAVETEXTDATA=" + this->MemoryTesterOptions += " /SAVETEXTDATA=" + outputFile;
this->MemoryTesterOutputFile;
#else #else
this->MemoryTesterOptions += " -log-file=" + this->MemoryTesterOutputFile; this->MemoryTesterOptions += " -log-file=" + outputFile;
#endif #endif
} }
else if ( this->MemoryTester.find("boundschecker") != std::string::npos ) else if ( this->MemoryTester.find("BC") != std::string::npos )
{ {
this->BoundsCheckerXMLFile = this->MemoryTesterOutputFile;
std::string outputFile =
cmSystemTools::EscapeSpaces(this->MemoryTesterOutputFile.c_str());
std::string dpbdFile = this->CTest->GetBinaryDir()
+ "/Testing/Temporary/MemoryChecker.DPbd";
std::string errorFile = this->CTest->GetBinaryDir()
+ "/Testing/Temporary/MemoryChecker.error";
errorFile = cmSystemTools::EscapeSpaces(errorFile.c_str());
this->BoundsCheckerDPBDFile = dpbdFile;
dpbdFile = cmSystemTools::EscapeSpaces(dpbdFile.c_str());
this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER; this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
cmCTestLog(this->CTest, ERROR_MESSAGE, this->MemoryTesterOptions += " /B " + dpbdFile;
"Bounds checker not yet implemented" << std::endl); this->MemoryTesterOptions += " /X " + outputFile;
return false; this->MemoryTesterOptions += " /M ";
} }
else else
{ {
@ -448,8 +570,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
else if ( this->MemoryTesterStyle == else if ( this->MemoryTesterStyle ==
cmCTestMemCheckHandler::BOUNDS_CHECKER ) cmCTestMemCheckHandler::BOUNDS_CHECKER )
{ {
log.append("\nMemory checking style used was: "); return this->ProcessMemCheckBoundsCheckerOutput(str, log, results);
log.append("Bounds Checker");
} }
else else
{ {
@ -464,24 +585,11 @@ bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
//---------------------------------------------------------------------- //----------------------------------------------------------------------
bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput( bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
const std::string&, std::string& log, const std::string& str, std::string& log,
int* results) int* results)
{ {
if ( !cmSystemTools::FileExists(this->MemoryTesterOutputFile.c_str()) ) std::vector<cmStdString> lines;
{ cmSystemTools::Split(str.c_str(), lines);
log = "Cannot find Purify output file: " + this->MemoryTesterOutputFile;
cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
return false;
}
std::ifstream ifs(this->MemoryTesterOutputFile.c_str());
if ( !ifs )
{
log = "Cannot read Purify output file: " + this->MemoryTesterOutputFile;
cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
return false;
}
cmOStringStream ostr; cmOStringStream ostr;
log = ""; log = "";
@ -489,11 +597,11 @@ bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
int defects = 0; int defects = 0;
std::string line; for( std::vector<cmStdString>::iterator i = lines.begin();
while ( cmSystemTools::GetLineFromStream(ifs, line) ) i != lines.end(); ++i)
{ {
int failure = cmCTestMemCheckHandler::NO_MEMORY_FAULT; int failure = cmCTestMemCheckHandler::NO_MEMORY_FAULT;
if ( pfW.find(line) ) if ( pfW.find(*i) )
{ {
int cc; int cc;
for ( cc = 0; cc < cmCTestMemCheckHandler::NO_MEMORY_FAULT; cc ++ ) for ( cc = 0; cc < cmCTestMemCheckHandler::NO_MEMORY_FAULT; cc ++ )
@ -518,7 +626,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
results[failure] ++; results[failure] ++;
defects ++; defects ++;
} }
ostr << cmCTest::MakeXMLSafe(line) << std::endl; ostr << cmCTest::MakeXMLSafe(*i) << std::endl;
} }
log = ostr.str(); log = ostr.str();
@ -651,3 +759,152 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
} }
return true; return true;
} }
//----------------------------------------------------------------------
bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
const std::string& str, std::string& log,
int* results)
{
log = "";
double sttime = cmSystemTools::GetTime();
std::vector<cmStdString> lines;
cmSystemTools::Split(str.c_str(), lines);
cmCTestLog(this->CTest, DEBUG, "Start test: " << lines.size() << std::endl);
std::vector<cmStdString>::size_type cc;
for ( cc = 0; cc < lines.size(); cc ++ )
{
if(lines[cc] == BOUNDS_CHECKER_MARKER)
{
break;
}
}
cmBoundsCheckerParser parser(this->CTest);
parser.InitializeParser();
if(cc < lines.size())
{
for(cc++; cc < lines.size(); ++cc)
{
std::string& theLine = lines[cc];
// check for command line arguments that are not escaped
// correctly by BC
if(theLine.find("TargetArgs=") != theLine.npos)
{
// skip this because BC gets it wrong and we can't parse it
}
else if(!parser.ParseChunk(theLine.c_str(), theLine.size()))
{
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Error in ParseChunk: " << theLine.c_str()
<< std::endl);
}
}
}
int defects = 0;
for(cc =0; cc < parser.Errors.size(); ++cc)
{
results[parser.Errors[cc]]++;
defects++;
}
cmCTestLog(this->CTest, DEBUG, "End test (elapsed: "
<< (cmSystemTools::GetTime() - sttime) << std::endl);
if(defects)
{
// only put the output of Bounds Checker if there were
// errors or leaks detected
log = parser.Log;
return false;
}
return true;
}
void
cmCTestMemCheckHandler::ProcessOneTest(cmCTestTestProperties *props,
std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed,
int count, int tmsize)
{
// run parent test
cmCTestTestHandler::ProcessOneTest(props, passed, failed, count, tmsize);
cmCTestTestResult& res = this->TestResults[this->TestResults.size()-1];
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "process test output now: "
<< props->Name.c_str() << " " << res.Name.c_str() << std::endl);
if( this->MemoryTesterStyle == cmCTestMemCheckHandler::BOUNDS_CHECKER)
{
this->PostProcessBoundsCheckerTest(res);
}
else if(this->MemoryTesterStyle == cmCTestMemCheckHandler::PURIFY )
{
this->PostProcessPurifyTest(res);
}
}
// This method puts the bounds checker output file into the output
// for the test
void
cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(cmCTestTestResult& res)
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"PostProcessBoundsCheckerTest for : "
<< res.Name.c_str() << std::endl);
if ( !cmSystemTools::FileExists(this->MemoryTesterOutputFile.c_str()) )
{
std::string log = "Cannot find memory tester output file: "
+ this->MemoryTesterOutputFile;
cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
return;
}
// put a scope around this to close ifs so the file can be removed
{
std::ifstream ifs(this->MemoryTesterOutputFile.c_str());
if ( !ifs )
{
std::string log = "Cannot read memory tester output file: " + this->MemoryTesterOutputFile;
cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
return;
}
res.Output += BOUNDS_CHECKER_MARKER;
res.Output += "\n";
std::string line;
while ( cmSystemTools::GetLineFromStream(ifs, line) )
{
res.Output += line;
res.Output += "\n";
}
}
cmSystemTools::Delay(1000);
cmSystemTools::RemoveFile(this->BoundsCheckerDPBDFile.c_str());
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Remove: "
<< this->BoundsCheckerDPBDFile.c_str() << std::endl);
cmSystemTools::RemoveFile(this->BoundsCheckerXMLFile.c_str());
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Remove: "
<< this->BoundsCheckerXMLFile.c_str() << std::endl);
}
void
cmCTestMemCheckHandler::PostProcessPurifyTest(cmCTestTestResult& res)
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"PostProcessPurifyTest for : "
<< res.Name.c_str() << std::endl);
if ( !cmSystemTools::FileExists(this->MemoryTesterOutputFile.c_str()) )
{
std::string log = "Cannot find memory tester output file: "
+ this->MemoryTesterOutputFile;
cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
return;
}
std::ifstream ifs(this->MemoryTesterOutputFile.c_str());
if ( !ifs )
{
std::string log = "Cannot read memory tester output file: " + this->MemoryTesterOutputFile;
cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
return;
}
std::string line;
while ( cmSystemTools::GetLineFromStream(ifs, line) )
{
res.Output += line;
}
}

View File

@ -51,7 +51,7 @@ private:
PURIFY, PURIFY,
BOUNDS_CHECKER BOUNDS_CHECKER
}; };
public:
enum { // Memory faults enum { // Memory faults
ABR = 0, ABR = 0,
ABW, ABW,
@ -77,7 +77,7 @@ private:
UMR, UMR,
NO_MEMORY_FAULT NO_MEMORY_FAULT
}; };
private:
enum { // Program statuses enum { // Program statuses
NOT_RUN = 0, NOT_RUN = 0,
TIMEOUT, TIMEOUT,
@ -90,7 +90,8 @@ private:
BAD_COMMAND, BAD_COMMAND,
COMPLETED COMPLETED
}; };
std::string BoundsCheckerDPBDFile;
std::string BoundsCheckerXMLFile;
std::string MemoryTester; std::string MemoryTester;
std::vector<cmStdString> MemoryTesterOptionsParsed; std::vector<cmStdString> MemoryTesterOptionsParsed;
std::string MemoryTesterOptions; std::string MemoryTesterOptions;
@ -118,7 +119,17 @@ private:
std::string& log, int* results); std::string& log, int* results);
bool ProcessMemCheckPurifyOutput(const std::string& str, bool ProcessMemCheckPurifyOutput(const std::string& str,
std::string& log, int* results); std::string& log, int* results);
bool ProcessMemCheckBoundsCheckerOutput(const std::string& str,
std::string& log, int* results);
/**
* Run one test
*/
virtual void ProcessOneTest(cmCTestTestProperties *props,
std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed,
int count, int tmsize);
void PostProcessPurifyTest(cmCTestTestResult& res);
void PostProcessBoundsCheckerTest(cmCTestTestResult& res);
}; };
#endif #endif

View File

@ -137,6 +137,15 @@ protected:
bool MemCheck; bool MemCheck;
int CustomMaximumPassedTestOutputSize; int CustomMaximumPassedTestOutputSize;
int CustomMaximumFailedTestOutputSize; int CustomMaximumFailedTestOutputSize;
protected:
/**
* Run one test
*/
virtual void ProcessOneTest(cmCTestTestProperties *props,
std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed,
int count, int tmsize);
private: private:
@ -165,14 +174,6 @@ private:
void ProcessDirectory(std::vector<cmStdString> &passed, void ProcessDirectory(std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed); std::vector<cmStdString> &failed);
/**
* Run one test
*/
void ProcessOneTest(cmCTestTestProperties *props,
std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed,
int count, int tmsize);
typedef std::vector<cmCTestTestProperties> ListOfTests; typedef std::vector<cmCTestTestProperties> ListOfTests;
/** /**
* Get the list of tests in directory and subdirectories. * Get the list of tests in directory and subdirectories.