1192 lines
36 KiB
C++
1192 lines
36 KiB
C++
/*============================================================================
|
|
CMake - Cross Platform Makefile Generator
|
|
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
|
|
|
|
Distributed under the OSI-approved BSD License (the "License");
|
|
see accompanying file Copyright.txt for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the License for more information.
|
|
============================================================================*/
|
|
|
|
#include "cmCTestMemCheckHandler.h"
|
|
#include "cmXMLParser.h"
|
|
#include "cmCTest.h"
|
|
#include "cmake.h"
|
|
#include "cmGeneratedFileStream.h"
|
|
#include <cmsys/Process.h>
|
|
#include <cmsys/RegularExpression.hxx>
|
|
#include <cmsys/Base64.h>
|
|
#include <cmsys/Glob.hxx>
|
|
#include <cmsys/FStream.hxx>
|
|
#include "cmMakefile.h"
|
|
#include "cmXMLSafe.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <math.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 containing the results of last BoundsChecker run
|
|
class cmBoundsCheckerParser : public cmXMLParser
|
|
{
|
|
public:
|
|
cmBoundsCheckerParser(cmCTest* c) { this->CTest = c;}
|
|
void StartElement(const std::string& name, const char** atts)
|
|
{
|
|
if(name == "MemoryLeak" ||
|
|
name == "ResourceLeak")
|
|
{
|
|
this->Errors.push_back(cmCTestMemCheckHandler::MLK);
|
|
}
|
|
else if(name == "Error" ||
|
|
name == "Dangling Pointer")
|
|
{
|
|
this->ParseError(atts);
|
|
}
|
|
// Create the log
|
|
cmOStringStream ostr;
|
|
ostr << name << ":\n";
|
|
int i = 0;
|
|
for(; atts[i] != 0; i+=2)
|
|
{
|
|
ostr << " " << cmXMLSafe(atts[i])
|
|
<< " - " << cmXMLSafe(atts[i+1]) << "\n";
|
|
}
|
|
ostr << "\n";
|
|
this->Log += ostr.str();
|
|
}
|
|
void EndElement(const std::string& )
|
|
{
|
|
}
|
|
|
|
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******######******"
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
cmCTestMemCheckHandler::cmCTestMemCheckHandler()
|
|
{
|
|
this->MemCheck = true;
|
|
this->CustomMaximumPassedTestOutputSize = 0;
|
|
this->CustomMaximumFailedTestOutputSize = 0;
|
|
this->LogWithPID = false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void cmCTestMemCheckHandler::Initialize()
|
|
{
|
|
this->Superclass::Initialize();
|
|
this->LogWithPID = false;
|
|
this->CustomMaximumPassedTestOutputSize = 0;
|
|
this->CustomMaximumFailedTestOutputSize = 0;
|
|
this->MemoryTester = "";
|
|
this->MemoryTesterDynamicOptions.clear();
|
|
this->MemoryTesterOptions.clear();
|
|
this->MemoryTesterStyle = UNKNOWN;
|
|
this->MemoryTesterOutputFile = "";
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int cmCTestMemCheckHandler::PreProcessHandler()
|
|
{
|
|
if ( !this->InitializeMemoryChecking() )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if ( !this->ExecuteCommands(this->CustomPreMemCheck) )
|
|
{
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
|
"Problem executing pre-memcheck command(s)." << std::endl);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int cmCTestMemCheckHandler::PostProcessHandler()
|
|
{
|
|
if ( !this->ExecuteCommands(this->CustomPostMemCheck) )
|
|
{
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
|
"Problem executing post-memcheck command(s)." << std::endl);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void cmCTestMemCheckHandler::GenerateTestCommand(
|
|
std::vector<std::string>& args, int test)
|
|
{
|
|
std::vector<std::string>::size_type pp;
|
|
std::string index;
|
|
cmOStringStream stream;
|
|
std::string memcheckcommand
|
|
= cmSystemTools::ConvertToOutputPath(this->MemoryTester.c_str());
|
|
stream << test;
|
|
index = stream.str();
|
|
for ( pp = 0; pp < this->MemoryTesterDynamicOptions.size(); pp ++ )
|
|
{
|
|
std::string arg = this->MemoryTesterDynamicOptions[pp];
|
|
std::string::size_type pos = arg.find("??");
|
|
if (pos != std::string::npos)
|
|
{
|
|
arg.replace(pos, 2, index);
|
|
}
|
|
args.push_back(arg);
|
|
memcheckcommand += " \"";
|
|
memcheckcommand += arg;
|
|
memcheckcommand += "\"";
|
|
}
|
|
// Create a copy of the memory tester environment variable.
|
|
// This is used for memory testing programs that pass options
|
|
// via environment varaibles.
|
|
std::string memTesterEnvironmentVariable =
|
|
this->MemoryTesterEnvironmentVariable;
|
|
for ( pp = 0; pp < this->MemoryTesterOptions.size(); pp ++ )
|
|
{
|
|
if(memTesterEnvironmentVariable.size())
|
|
{
|
|
// If we are using env to pass options, append all the options to
|
|
// this string with space separation.
|
|
memTesterEnvironmentVariable += " " + this->MemoryTesterOptions[pp];
|
|
}
|
|
// for regular options just add them to args and memcheckcommand
|
|
// which is just used for display
|
|
else
|
|
{
|
|
args.push_back(this->MemoryTesterOptions[pp]);
|
|
memcheckcommand += " \"";
|
|
memcheckcommand += this->MemoryTesterOptions[pp];
|
|
memcheckcommand += "\"";
|
|
}
|
|
}
|
|
// if this is an env option type, then add the env string as a single
|
|
// argument.
|
|
if(memTesterEnvironmentVariable.size())
|
|
{
|
|
std::string::size_type pos = memTesterEnvironmentVariable.find("??");
|
|
if (pos != std::string::npos)
|
|
{
|
|
memTesterEnvironmentVariable.replace(pos, 2, index);
|
|
}
|
|
memcheckcommand += " " + memTesterEnvironmentVariable;
|
|
args.push_back(memTesterEnvironmentVariable);
|
|
}
|
|
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Memory check command: "
|
|
<< memcheckcommand << std::endl);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void cmCTestMemCheckHandler::InitializeResultsVectors()
|
|
{
|
|
// fill these members
|
|
// cmsys::vector<std::string> ResultStrings;
|
|
// cmsys::vector<std::string> ResultStringsLong;
|
|
// cmsys::vector<int> GlobalResults;
|
|
this->ResultStringsLong.clear();
|
|
this->ResultStrings.clear();
|
|
this->GlobalResults.clear();
|
|
// If we are working with style checkers that dynamically fill
|
|
// the results strings then return.
|
|
if(this->MemoryTesterStyle > cmCTestMemCheckHandler::BOUNDS_CHECKER)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// define the standard set of errors
|
|
//----------------------------------------------------------------------
|
|
static const char* cmCTestMemCheckResultStrings[] = {
|
|
"ABR",
|
|
"ABW",
|
|
"ABWL",
|
|
"COR",
|
|
"EXU",
|
|
"FFM",
|
|
"FIM",
|
|
"FMM",
|
|
"FMR",
|
|
"FMW",
|
|
"FUM",
|
|
"IPR",
|
|
"IPW",
|
|
"MAF",
|
|
"MLK",
|
|
"MPK",
|
|
"NPR",
|
|
"ODS",
|
|
"PAR",
|
|
"PLK",
|
|
"UMC",
|
|
"UMR",
|
|
0
|
|
};
|
|
//----------------------------------------------------------------------
|
|
static const char* cmCTestMemCheckResultLongStrings[] = {
|
|
"Threading Problem",
|
|
"ABW",
|
|
"ABWL",
|
|
"COR",
|
|
"EXU",
|
|
"FFM",
|
|
"FIM",
|
|
"Mismatched deallocation",
|
|
"FMR",
|
|
"FMW",
|
|
"FUM",
|
|
"IPR",
|
|
"IPW",
|
|
"MAF",
|
|
"Memory Leak",
|
|
"Potential Memory Leak",
|
|
"NPR",
|
|
"ODS",
|
|
"Invalid syscall param",
|
|
"PLK",
|
|
"Uninitialized Memory Conditional",
|
|
"Uninitialized Memory Read",
|
|
0
|
|
};
|
|
this->GlobalResults.clear();
|
|
for(int i =0; cmCTestMemCheckResultStrings[i] != 0; ++i)
|
|
{
|
|
this->ResultStrings.push_back(cmCTestMemCheckResultStrings[i]);
|
|
this->ResultStringsLong.push_back(cmCTestMemCheckResultLongStrings[i]);
|
|
this->GlobalResults.push_back(0);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void cmCTestMemCheckHandler::PopulateCustomVectors(cmMakefile *mf)
|
|
{
|
|
this->cmCTestTestHandler::PopulateCustomVectors(mf);
|
|
this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_PRE_MEMCHECK",
|
|
this->CustomPreMemCheck);
|
|
this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_POST_MEMCHECK",
|
|
this->CustomPostMemCheck);
|
|
|
|
this->CTest->PopulateCustomVector(mf,
|
|
"CTEST_CUSTOM_MEMCHECK_IGNORE",
|
|
this->CustomTestsIgnore);
|
|
this->CTest->SetCTestConfigurationFromCMakeVariable(
|
|
mf, "CMakeCommand", "CMAKE_COMMAND");
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os)
|
|
{
|
|
if ( !this->CTest->GetProduceXML() )
|
|
{
|
|
return;
|
|
}
|
|
this->CTest->StartXML(os, this->AppendXML);
|
|
os << "<DynamicAnalysis Checker=\"";
|
|
switch ( this->MemoryTesterStyle )
|
|
{
|
|
case cmCTestMemCheckHandler::VALGRIND:
|
|
os << "Valgrind";
|
|
break;
|
|
case cmCTestMemCheckHandler::PURIFY:
|
|
os << "Purify";
|
|
break;
|
|
case cmCTestMemCheckHandler::BOUNDS_CHECKER:
|
|
os << "BoundsChecker";
|
|
break;
|
|
case cmCTestMemCheckHandler::THREAD_SANITIZER:
|
|
os << "ThreadSanitizer";
|
|
break;
|
|
default:
|
|
os << "Unknown";
|
|
}
|
|
os << "\">" << std::endl;
|
|
|
|
os << "\t<StartDateTime>" << this->StartTest << "</StartDateTime>\n"
|
|
<< "\t<StartTestTime>" << this->StartTestTime << "</StartTestTime>\n"
|
|
<< "\t<TestList>\n";
|
|
cmCTestMemCheckHandler::TestResultsVector::size_type cc;
|
|
for ( cc = 0; cc < this->TestResults.size(); cc ++ )
|
|
{
|
|
cmCTestTestResult *result = &this->TestResults[cc];
|
|
std::string testPath = result->Path + "/" + result->Name;
|
|
os << "\t\t<Test>" << cmXMLSafe(
|
|
this->CTest->GetShortPathToFile(testPath.c_str()))
|
|
<< "</Test>" << std::endl;
|
|
}
|
|
os << "\t</TestList>\n";
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT,
|
|
"-- Processing memory checking output: ");
|
|
size_t total = this->TestResults.size();
|
|
size_t step = total / 10;
|
|
size_t current = 0;
|
|
for ( cc = 0; cc < this->TestResults.size(); cc ++ )
|
|
{
|
|
cmCTestTestResult *result = &this->TestResults[cc];
|
|
std::string memcheckstr;
|
|
std::vector<int> memcheckresults(this->ResultStrings.size(), 0);
|
|
bool res = this->ProcessMemCheckOutput(result->Output, memcheckstr,
|
|
memcheckresults);
|
|
if ( res && result->Status == cmCTestMemCheckHandler::COMPLETED )
|
|
{
|
|
continue;
|
|
}
|
|
this->CleanTestOutput(memcheckstr,
|
|
static_cast<size_t>(this->CustomMaximumFailedTestOutputSize));
|
|
this->WriteTestResultHeader(os, result);
|
|
os << "\t\t<Results>" << std::endl;
|
|
for(std::vector<int>::size_type kk = 0;
|
|
kk < memcheckresults.size(); ++kk)
|
|
{
|
|
if ( memcheckresults[kk] )
|
|
{
|
|
os << "\t\t\t<Defect type=\"" << this->ResultStringsLong[kk]
|
|
<< "\">"
|
|
<< memcheckresults[kk]
|
|
<< "</Defect>" << std::endl;
|
|
}
|
|
this->GlobalResults[kk] += memcheckresults[kk];
|
|
}
|
|
|
|
std::string logTag;
|
|
if(this->CTest->ShouldCompressMemCheckOutput())
|
|
{
|
|
this->CTest->CompressString(memcheckstr);
|
|
logTag = "\t<Log compression=\"gzip\" encoding=\"base64\">\n";
|
|
}
|
|
else
|
|
{
|
|
logTag = "\t<Log>\n";
|
|
}
|
|
|
|
os
|
|
<< "\t\t</Results>\n"
|
|
<< logTag << cmXMLSafe(memcheckstr) << std::endl
|
|
<< "\t</Log>\n";
|
|
this->WriteTestResultFooter(os, result);
|
|
if ( current < cc )
|
|
{
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT, "#" << std::flush);
|
|
current += step;
|
|
}
|
|
}
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT, "Memory checking results:"
|
|
<< std::endl);
|
|
os << "\t<DefectList>" << std::endl;
|
|
for ( cc = 0; cc < this->GlobalResults.size(); cc ++ )
|
|
{
|
|
if ( this->GlobalResults[cc] )
|
|
{
|
|
#ifdef cerr
|
|
# undef cerr
|
|
#endif
|
|
std::cerr.width(35);
|
|
#define cerr no_cerr
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT,
|
|
this->ResultStringsLong[cc] << " - "
|
|
<< this->GlobalResults[cc] << std::endl);
|
|
os << "\t\t<Defect Type=\"" << this->ResultStringsLong[cc]
|
|
<< "\"/>" << std::endl;
|
|
}
|
|
}
|
|
os << "\t</DefectList>" << std::endl;
|
|
|
|
os << "\t<EndDateTime>" << this->EndTest << "</EndDateTime>" << std::endl;
|
|
os << "\t<EndTestTime>" << this->EndTestTime
|
|
<< "</EndTestTime>" << std::endl;
|
|
os << "<ElapsedMinutes>"
|
|
<< static_cast<int>(this->ElapsedTestingTime/6)/10.0
|
|
<< "</ElapsedMinutes>\n";
|
|
|
|
os << "</DynamicAnalysis>" << std::endl;
|
|
this->CTest->EndXML(os);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool cmCTestMemCheckHandler::InitializeMemoryChecking()
|
|
{
|
|
this->MemoryTesterEnvironmentVariable = "";
|
|
this->MemoryTester = "";
|
|
// Setup the command
|
|
if ( cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
|
|
"MemoryCheckCommand").c_str()) )
|
|
{
|
|
this->MemoryTester
|
|
= this->CTest->GetCTestConfiguration("MemoryCheckCommand").c_str();
|
|
std::string testerName =
|
|
cmSystemTools::GetFilenameName(this->MemoryTester);
|
|
// determine the checker type
|
|
if ( testerName.find("valgrind") != std::string::npos ||
|
|
this->CTest->GetCTestConfiguration("MemoryCheckType")
|
|
== "Valgrind")
|
|
{
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
|
|
}
|
|
else if ( testerName.find("purify") != std::string::npos )
|
|
{
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
|
|
}
|
|
else if ( testerName.find("BC") != std::string::npos )
|
|
{
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
|
|
}
|
|
else
|
|
{
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::UNKNOWN;
|
|
}
|
|
}
|
|
else if ( cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
|
|
"PurifyCommand").c_str()) )
|
|
{
|
|
this->MemoryTester
|
|
= this->CTest->GetCTestConfiguration("PurifyCommand").c_str();
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
|
|
}
|
|
else if ( cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
|
|
"ValgrindCommand").c_str()) )
|
|
{
|
|
this->MemoryTester
|
|
= this->CTest->GetCTestConfiguration("ValgrindCommand").c_str();
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
|
|
}
|
|
else if ( cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
|
|
"BoundsCheckerCommand").c_str()) )
|
|
{
|
|
this->MemoryTester
|
|
= this->CTest->GetCTestConfiguration("BoundsCheckerCommand").c_str();
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
|
|
}
|
|
if ( this->CTest->GetCTestConfiguration("MemoryCheckType")
|
|
== "ThreadSanitizer")
|
|
{
|
|
this->MemoryTester
|
|
= this->CTest->GetCTestConfiguration("CMakeCommand").c_str();
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::THREAD_SANITIZER;
|
|
this->LogWithPID = true; // even if we give the log file the pid is added
|
|
}
|
|
// Check the MemoryCheckType
|
|
if(this->MemoryTesterStyle == cmCTestMemCheckHandler::UNKNOWN)
|
|
{
|
|
std::string checkType =
|
|
this->CTest->GetCTestConfiguration("MemoryCheckType");
|
|
if(checkType == "Purify")
|
|
{
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
|
|
}
|
|
else if(checkType == "BoundsChecker")
|
|
{
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
|
|
}
|
|
else if(checkType == "Valgrind")
|
|
{
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
|
|
}
|
|
}
|
|
if(this->MemoryTester.size() == 0 )
|
|
{
|
|
cmCTestLog(this->CTest, WARNING,
|
|
"Memory checker (MemoryCheckCommand) "
|
|
"not set, or cannot find the specified program."
|
|
<< std::endl);
|
|
return false;
|
|
}
|
|
|
|
// Setup the options
|
|
std::string memoryTesterOptions;
|
|
if ( this->CTest->GetCTestConfiguration(
|
|
"MemoryCheckCommandOptions").size() )
|
|
{
|
|
memoryTesterOptions = this->CTest->GetCTestConfiguration(
|
|
"MemoryCheckCommandOptions");
|
|
}
|
|
else if ( this->CTest->GetCTestConfiguration(
|
|
"ValgrindCommandOptions").size() )
|
|
{
|
|
memoryTesterOptions = this->CTest->GetCTestConfiguration(
|
|
"ValgrindCommandOptions");
|
|
}
|
|
this->MemoryTesterOptions
|
|
= cmSystemTools::ParseArguments(memoryTesterOptions.c_str());
|
|
|
|
this->MemoryTesterOutputFile
|
|
= this->CTest->GetBinaryDir()
|
|
+ "/Testing/Temporary/MemoryChecker.??.log";
|
|
|
|
switch ( this->MemoryTesterStyle )
|
|
{
|
|
case cmCTestMemCheckHandler::VALGRIND:
|
|
{
|
|
if ( this->MemoryTesterOptions.empty() )
|
|
{
|
|
this->MemoryTesterOptions.push_back("-q");
|
|
this->MemoryTesterOptions.push_back("--tool=memcheck");
|
|
this->MemoryTesterOptions.push_back("--leak-check=yes");
|
|
this->MemoryTesterOptions.push_back("--show-reachable=yes");
|
|
this->MemoryTesterOptions.push_back("--num-callers=50");
|
|
}
|
|
if ( this->CTest->GetCTestConfiguration(
|
|
"MemoryCheckSuppressionFile").size() )
|
|
{
|
|
if ( !cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
|
|
"MemoryCheckSuppressionFile").c_str()) )
|
|
{
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
|
"Cannot find memory checker suppression file: "
|
|
<< this->CTest->GetCTestConfiguration(
|
|
"MemoryCheckSuppressionFile") << std::endl);
|
|
return false;
|
|
}
|
|
std::string suppressions = "--suppressions="
|
|
+ this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile");
|
|
this->MemoryTesterOptions.push_back(suppressions);
|
|
}
|
|
std::string outputFile = "--log-file="
|
|
+ this->MemoryTesterOutputFile;
|
|
this->MemoryTesterDynamicOptions.push_back(outputFile);
|
|
break;
|
|
}
|
|
case cmCTestMemCheckHandler::PURIFY:
|
|
{
|
|
std::string outputFile;
|
|
#ifdef _WIN32
|
|
if( this->CTest->GetCTestConfiguration(
|
|
"MemoryCheckSuppressionFile").size() )
|
|
{
|
|
if( !cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
|
|
"MemoryCheckSuppressionFile").c_str()) )
|
|
{
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
|
"Cannot find memory checker suppression file: "
|
|
<< this->CTest->GetCTestConfiguration(
|
|
"MemoryCheckSuppressionFile").c_str() << std::endl);
|
|
return false;
|
|
}
|
|
std::string filterFiles = "/FilterFiles="
|
|
+ this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile");
|
|
this->MemoryTesterOptions.push_back(filterFiles);
|
|
}
|
|
outputFile = "/SAVETEXTDATA=";
|
|
#else
|
|
outputFile = "-log-file=";
|
|
#endif
|
|
outputFile += this->MemoryTesterOutputFile;
|
|
this->MemoryTesterDynamicOptions.push_back(outputFile);
|
|
break;
|
|
}
|
|
case cmCTestMemCheckHandler::BOUNDS_CHECKER:
|
|
{
|
|
this->BoundsCheckerXMLFile = this->MemoryTesterOutputFile;
|
|
std::string dpbdFile = this->CTest->GetBinaryDir()
|
|
+ "/Testing/Temporary/MemoryChecker.??.DPbd";
|
|
this->BoundsCheckerDPBDFile = dpbdFile;
|
|
this->MemoryTesterDynamicOptions.push_back("/B");
|
|
this->MemoryTesterDynamicOptions.push_back(dpbdFile);
|
|
this->MemoryTesterDynamicOptions.push_back("/X");
|
|
this->MemoryTesterDynamicOptions.push_back(this->MemoryTesterOutputFile);
|
|
this->MemoryTesterOptions.push_back("/M");
|
|
break;
|
|
}
|
|
case cmCTestMemCheckHandler::THREAD_SANITIZER:
|
|
{
|
|
// To pass arguments to ThreadSanitizer the environment variable
|
|
// TSAN_OPTIONS is used. This is done with the cmake -E env command.
|
|
// The MemoryTesterDynamicOptions is setup with the -E env
|
|
// Then the MemoryTesterEnvironmentVariable gets the
|
|
// TSAN_OPTIONS string with the log_path in it.
|
|
this->MemoryTesterDynamicOptions.push_back("-E");
|
|
this->MemoryTesterDynamicOptions.push_back("env");
|
|
std::string outputFile = "TSAN_OPTIONS=log_path=\""
|
|
+ this->MemoryTesterOutputFile + "\"";
|
|
this->MemoryTesterEnvironmentVariable = outputFile;
|
|
break;
|
|
}
|
|
default:
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
|
"Do not understand memory checker: " << this->MemoryTester
|
|
<< std::endl);
|
|
return false;
|
|
}
|
|
|
|
this->InitializeResultsVectors();
|
|
// std::vector<std::string>::size_type cc;
|
|
// for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ )
|
|
// {
|
|
// this->MemoryTesterGlobalResults[cc] = 0;
|
|
// }
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool cmCTestMemCheckHandler::
|
|
ProcessMemCheckOutput(const std::string& str,
|
|
std::string& log, std::vector<int>& results)
|
|
{
|
|
if ( this->MemoryTesterStyle == cmCTestMemCheckHandler::VALGRIND )
|
|
{
|
|
return this->ProcessMemCheckValgrindOutput(str, log, results);
|
|
}
|
|
else if ( this->MemoryTesterStyle == cmCTestMemCheckHandler::PURIFY )
|
|
{
|
|
return this->ProcessMemCheckPurifyOutput(str, log, results);
|
|
}
|
|
else if ( this->MemoryTesterStyle ==
|
|
cmCTestMemCheckHandler::THREAD_SANITIZER )
|
|
{
|
|
return this->ProcessMemCheckThreadSanitizerOutput(str, log, results);
|
|
}
|
|
else if ( this->MemoryTesterStyle ==
|
|
cmCTestMemCheckHandler::BOUNDS_CHECKER )
|
|
{
|
|
return this->ProcessMemCheckBoundsCheckerOutput(str, log, results);
|
|
}
|
|
else
|
|
{
|
|
log.append("\nMemory checking style used was: ");
|
|
log.append("None that I know");
|
|
log = str;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::vector<int>::size_type cmCTestMemCheckHandler::FindOrAddWarning(
|
|
const std::string& warning)
|
|
{
|
|
for(std::vector<std::string>::size_type i =0;
|
|
i < this->ResultStrings.size(); ++i)
|
|
{
|
|
if(this->ResultStrings[i] == warning)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
this->GlobalResults.push_back(0); // this must stay the same size
|
|
this->ResultStrings.push_back(warning);
|
|
this->ResultStringsLong.push_back(warning);
|
|
return this->ResultStrings.size()-1;
|
|
}
|
|
//----------------------------------------------------------------------
|
|
bool cmCTestMemCheckHandler::ProcessMemCheckThreadSanitizerOutput(
|
|
const std::string& str, std::string& log,
|
|
std::vector<int>& result)
|
|
{
|
|
cmsys::RegularExpression
|
|
sanitizerWarning("WARNING: ThreadSanitizer: (.*) \\(pid=.*\\)");
|
|
int defects = 0;
|
|
std::vector<std::string> lines;
|
|
cmSystemTools::Split(str.c_str(), lines);
|
|
cmOStringStream ostr;
|
|
log = "";
|
|
for( std::vector<std::string>::iterator i = lines.begin();
|
|
i != lines.end(); ++i)
|
|
{
|
|
if(sanitizerWarning.find(*i))
|
|
{
|
|
std::string warning = sanitizerWarning.match(1);
|
|
std::vector<int>::size_type idx = this->FindOrAddWarning(warning);
|
|
if(result.size() == 0 || idx > result.size()-1)
|
|
{
|
|
result.push_back(1);
|
|
}
|
|
else
|
|
{
|
|
result[idx]++;
|
|
}
|
|
defects++;
|
|
ostr << "<b>" << this->ResultStrings[idx] << "</b> ";
|
|
}
|
|
ostr << cmXMLSafe(*i) << std::endl;
|
|
}
|
|
log = ostr.str();
|
|
if(defects)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//----------------------------------------------------------------------
|
|
bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
|
|
const std::string& str, std::string& log,
|
|
std::vector<int>& results)
|
|
{
|
|
std::vector<std::string> lines;
|
|
cmSystemTools::Split(str.c_str(), lines);
|
|
cmOStringStream ostr;
|
|
log = "";
|
|
|
|
cmsys::RegularExpression pfW("^\\[[WEI]\\] ([A-Z][A-Z][A-Z][A-Z]*): ");
|
|
|
|
int defects = 0;
|
|
|
|
for( std::vector<std::string>::iterator i = lines.begin();
|
|
i != lines.end(); ++i)
|
|
{
|
|
std::vector<int>::size_type failure = this->ResultStrings.size();
|
|
if ( pfW.find(*i) )
|
|
{
|
|
std::vector<int>::size_type cc;
|
|
for ( cc = 0; cc < this->ResultStrings.size(); cc ++ )
|
|
{
|
|
if ( pfW.match(1) == this->ResultStrings[cc] )
|
|
{
|
|
failure = cc;
|
|
break;
|
|
}
|
|
}
|
|
if ( cc == this->ResultStrings.size() )
|
|
{
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown Purify memory fault: "
|
|
<< pfW.match(1) << std::endl);
|
|
ostr << "*** Unknown Purify memory fault: " << pfW.match(1)
|
|
<< std::endl;
|
|
}
|
|
}
|
|
if ( failure != this->ResultStrings.size() )
|
|
{
|
|
ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
|
|
results[failure] ++;
|
|
defects ++;
|
|
}
|
|
ostr << cmXMLSafe(*i) << std::endl;
|
|
}
|
|
|
|
log = ostr.str();
|
|
if ( defects )
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
|
|
const std::string& str, std::string& log,
|
|
std::vector<int>& results)
|
|
{
|
|
std::vector<std::string> lines;
|
|
cmSystemTools::Split(str.c_str(), lines);
|
|
bool unlimitedOutput = false;
|
|
if(str.find("CTEST_FULL_OUTPUT") != str.npos ||
|
|
this->CustomMaximumFailedTestOutputSize == 0)
|
|
{
|
|
unlimitedOutput = true;
|
|
}
|
|
|
|
std::string::size_type cc;
|
|
|
|
cmOStringStream ostr;
|
|
log = "";
|
|
|
|
int defects = 0;
|
|
|
|
cmsys::RegularExpression valgrindLine("^==[0-9][0-9]*==");
|
|
|
|
cmsys::RegularExpression vgFIM(
|
|
"== .*Invalid free\\(\\) / delete / delete\\[\\]");
|
|
cmsys::RegularExpression vgFMM(
|
|
"== .*Mismatched free\\(\\) / delete / delete \\[\\]");
|
|
cmsys::RegularExpression vgMLK1(
|
|
"== .*[0-9,]+ bytes in [0-9,]+ blocks are definitely lost"
|
|
" in loss record [0-9,]+ of [0-9,]+");
|
|
cmsys::RegularExpression vgMLK2(
|
|
"== .*[0-9,]+ \\([0-9,]+ direct, [0-9,]+ indirect\\)"
|
|
" bytes in [0-9,]+ blocks are definitely lost"
|
|
" in loss record [0-9,]+ of [0-9,]+");
|
|
cmsys::RegularExpression vgPAR(
|
|
"== .*Syscall param .* (contains|points to) unaddressable byte\\(s\\)");
|
|
cmsys::RegularExpression vgMPK1(
|
|
"== .*[0-9,]+ bytes in [0-9,]+ blocks are possibly lost in"
|
|
" loss record [0-9,]+ of [0-9,]+");
|
|
cmsys::RegularExpression vgMPK2(
|
|
"== .*[0-9,]+ bytes in [0-9,]+ blocks are still reachable"
|
|
" in loss record [0-9,]+ of [0-9,]+");
|
|
cmsys::RegularExpression vgUMC(
|
|
"== .*Conditional jump or move depends on uninitialised value\\(s\\)");
|
|
cmsys::RegularExpression vgUMR1(
|
|
"== .*Use of uninitialised value of size [0-9,]+");
|
|
cmsys::RegularExpression vgUMR2("== .*Invalid read of size [0-9,]+");
|
|
cmsys::RegularExpression vgUMR3("== .*Jump to the invalid address ");
|
|
cmsys::RegularExpression vgUMR4("== .*Syscall param .* contains "
|
|
"uninitialised or unaddressable byte\\(s\\)");
|
|
cmsys::RegularExpression vgUMR5("== .*Syscall param .* uninitialised");
|
|
cmsys::RegularExpression vgIPW("== .*Invalid write of size [0-9,]+");
|
|
cmsys::RegularExpression vgABR("== .*pthread_mutex_unlock: mutex is "
|
|
"locked by a different thread");
|
|
std::vector<std::string::size_type> nonValGrindOutput;
|
|
double sttime = cmSystemTools::GetTime();
|
|
cmCTestLog(this->CTest, DEBUG, "Start test: " << lines.size() << std::endl);
|
|
std::string::size_type totalOutputSize = 0;
|
|
bool outputFull = false;
|
|
for ( cc = 0; cc < lines.size(); cc ++ )
|
|
{
|
|
cmCTestLog(this->CTest, DEBUG, "test line "
|
|
<< lines[cc] << std::endl);
|
|
|
|
if ( valgrindLine.find(lines[cc]) )
|
|
{
|
|
cmCTestLog(this->CTest, DEBUG, "valgrind line "
|
|
<< lines[cc] << std::endl);
|
|
int failure = cmCTestMemCheckHandler::NO_MEMORY_FAULT;
|
|
if ( vgFIM.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::FIM;
|
|
}
|
|
else if ( vgFMM.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::FMM;
|
|
}
|
|
else if ( vgMLK1.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::MLK;
|
|
}
|
|
else if ( vgMLK2.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::MLK;
|
|
}
|
|
else if ( vgPAR.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::PAR;
|
|
}
|
|
else if ( vgMPK1.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::MPK;
|
|
}
|
|
else if ( vgMPK2.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::MPK;
|
|
}
|
|
else if ( vgUMC.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::UMC;
|
|
}
|
|
else if ( vgUMR1.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::UMR;
|
|
}
|
|
else if ( vgUMR2.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::UMR;
|
|
}
|
|
else if ( vgUMR3.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::UMR;
|
|
}
|
|
else if ( vgUMR4.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::UMR;
|
|
}
|
|
else if ( vgUMR5.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::UMR;
|
|
}
|
|
else if ( vgIPW.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::IPW;
|
|
}
|
|
else if ( vgABR.find(lines[cc]) )
|
|
{
|
|
failure = cmCTestMemCheckHandler::ABR;
|
|
}
|
|
|
|
if ( failure != cmCTestMemCheckHandler::NO_MEMORY_FAULT )
|
|
{
|
|
ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
|
|
results[failure] ++;
|
|
defects ++;
|
|
}
|
|
totalOutputSize += lines[cc].size();
|
|
ostr << cmXMLSafe(lines[cc]) << std::endl;
|
|
}
|
|
else
|
|
{
|
|
nonValGrindOutput.push_back(cc);
|
|
}
|
|
}
|
|
// Now put all all the non valgrind output into the test output
|
|
if(!outputFull)
|
|
{
|
|
for(std::vector<std::string::size_type>::iterator i =
|
|
nonValGrindOutput.begin(); i != nonValGrindOutput.end(); ++i)
|
|
{
|
|
totalOutputSize += lines[*i].size();
|
|
cmCTestLog(this->CTest, DEBUG, "before xml safe "
|
|
<< lines[*i] << std::endl);
|
|
cmCTestLog(this->CTest, DEBUG, "after xml safe "
|
|
<< cmXMLSafe(lines[*i]) << std::endl);
|
|
|
|
ostr << cmXMLSafe(lines[*i]) << std::endl;
|
|
if(!unlimitedOutput && totalOutputSize >
|
|
static_cast<size_t>(this->CustomMaximumFailedTestOutputSize))
|
|
{
|
|
outputFull = true;
|
|
ostr << "....\n";
|
|
ostr << "Test Output for this test has been truncated see testing"
|
|
" machine logs for full output,\n";
|
|
ostr << "or put CTEST_FULL_OUTPUT in the output of "
|
|
"this test program.\n";
|
|
}
|
|
}
|
|
}
|
|
cmCTestLog(this->CTest, DEBUG, "End test (elapsed: "
|
|
<< (cmSystemTools::GetTime() - sttime) << std::endl);
|
|
log = ostr.str();
|
|
if ( defects )
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
|
|
const std::string& str, std::string& log,
|
|
std::vector<int>& results)
|
|
{
|
|
log = "";
|
|
double sttime = cmSystemTools::GetTime();
|
|
std::vector<std::string> lines;
|
|
cmSystemTools::Split(str.c_str(), lines);
|
|
cmCTestLog(this->CTest, DEBUG, "Start test: " << lines.size() << std::endl);
|
|
std::vector<std::string>::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
|
|
<< 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;
|
|
}
|
|
|
|
// PostProcessTest memcheck results
|
|
void
|
|
cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res,
|
|
int test)
|
|
{
|
|
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
|
"PostProcessTest memcheck results for : "
|
|
<< res.Name << std::endl);
|
|
if(this->MemoryTesterStyle
|
|
== cmCTestMemCheckHandler::BOUNDS_CHECKER)
|
|
{
|
|
this->PostProcessBoundsCheckerTest(res, test);
|
|
}
|
|
else
|
|
{
|
|
this->AppendMemTesterOutput(res, test);
|
|
}
|
|
}
|
|
|
|
|
|
// This method puts the bounds checker output file into the output
|
|
// for the test
|
|
void
|
|
cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(cmCTestTestResult& res,
|
|
int test)
|
|
{
|
|
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
|
"PostProcessBoundsCheckerTest for : "
|
|
<< res.Name << std::endl);
|
|
std::string ofile = this->TestOutputFileName(test);
|
|
if ( ofile.empty() )
|
|
{
|
|
return;
|
|
}
|
|
// put a scope around this to close ifs so the file can be removed
|
|
{
|
|
cmsys::ifstream ifs(ofile.c_str());
|
|
if ( !ifs )
|
|
{
|
|
std::string log = "Cannot read memory tester output file: " + ofile;
|
|
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 << std::endl);
|
|
cmSystemTools::RemoveFile(this->BoundsCheckerXMLFile.c_str());
|
|
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Remove: "
|
|
<< this->BoundsCheckerXMLFile << std::endl);
|
|
}
|
|
|
|
void
|
|
cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res,
|
|
int test)
|
|
{
|
|
std::string ofile = this->TestOutputFileName(test);
|
|
if ( ofile.empty() )
|
|
{
|
|
return;
|
|
}
|
|
// put ifs in scope so file can be deleted if needed
|
|
{
|
|
cmsys::ifstream ifs(ofile.c_str());
|
|
if ( !ifs )
|
|
{
|
|
std::string log = "Cannot read memory tester output file: " + ofile;
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
|
|
return;
|
|
}
|
|
std::string line;
|
|
while ( cmSystemTools::GetLineFromStream(ifs, line) )
|
|
{
|
|
res.Output += line;
|
|
res.Output += "\n";
|
|
}
|
|
}
|
|
if(this->LogWithPID)
|
|
{
|
|
cmSystemTools::RemoveFile(ofile.c_str());
|
|
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Remove: "<< ofile <<"\n");
|
|
}
|
|
}
|
|
|
|
std::string
|
|
cmCTestMemCheckHandler::TestOutputFileName(int test)
|
|
{
|
|
std::string index;
|
|
cmOStringStream stream;
|
|
stream << test;
|
|
index = stream.str();
|
|
std::string ofile = this->MemoryTesterOutputFile;
|
|
std::string::size_type pos = ofile.find("??");
|
|
ofile.replace(pos, 2, index);
|
|
if(this->LogWithPID)
|
|
{
|
|
ofile += ".*";
|
|
cmsys::Glob g;
|
|
g.FindFiles(ofile);
|
|
if(g.GetFiles().size() == 0)
|
|
{
|
|
std::string log = "Cannot find memory tester output file: "
|
|
+ ofile;
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
|
|
ofile = "";
|
|
}
|
|
else
|
|
{
|
|
ofile = g.GetFiles()[0];
|
|
}
|
|
}
|
|
else if ( !cmSystemTools::FileExists(ofile.c_str()) )
|
|
{
|
|
std::string log = "Cannot find memory tester output file: "
|
|
+ ofile;
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
|
|
ofile = "";
|
|
}
|
|
return ofile;
|
|
}
|