CMake/Source/CTest/cmCTestMemCheckHandler.cxx

1055 lines
38 KiB
C++
Raw Normal View History

Simplify CMake per-source license notices Per-source copyright/license notice headers that spell out copyright holder names and years are hard to maintain and often out-of-date or plain wrong. Precise contributor information is already maintained automatically by the version control tool. Ultimately it is the receiver of a file who is responsible for determining its licensing status, and per-source notices are merely a convenience. Therefore it is simpler and more accurate for each source to have a generic notice of the license name and references to more detailed information on copyright holders and full license terms. Our `Copyright.txt` file now contains a list of Contributors whose names appeared source-level copyright notices. It also references version control history for more precise information. Therefore we no longer need to spell out the list of Contributors in each source file notice. Replace CMake per-source copyright/license notice headers with a short description of the license and links to `Copyright.txt` and online information available from "https://cmake.org/licensing". The online URL also handles cases of modules being copied out of our source into other projects, so we can drop our notices about replacing links with full license text. Run the `Utilities/Scripts/filter-notices.bash` script to perform the majority of the replacements mechanically. Manually fix up shebang lines and trailing newlines in a few files. Manually update the notices in a few files that the script does not handle.
2016-09-27 22:01:08 +03:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
2005-01-27 23:54:47 +03:00
#include "cmCTestMemCheckHandler.h"
2005-01-27 23:54:47 +03:00
#include "cmCTest.h"
#include "cmSystemTools.h"
#include "cmXMLParser.h"
#include "cmXMLWriter.h"
#include <cmsys/FStream.hxx>
#include <cmsys/Glob.hxx>
#include <cmsys/RegularExpression.hxx>
#include <iostream>
#include <sstream>
#include <string.h>
2005-01-27 23:54:47 +03:00
2007-07-24 22:43:31 +04:00
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 },
2016-06-27 23:44:16 +03:00
{ CM_NULLPTR, 0 }
2007-07-24 22:43:31 +04:00
};
static void xmlReportError(int line, const char* msg, void* data)
{
cmCTest* ctest = (cmCTest*)data;
cmCTestLog(ctest, ERROR_MESSAGE, "Error parsing XML in stream at line "
<< line << ": " << msg << std::endl);
}
// parse the xml file containing the results of last BoundsChecker run
2007-07-24 22:43:31 +04:00
class cmBoundsCheckerParser : public cmXMLParser
{
public:
cmBoundsCheckerParser(cmCTest* c)
{
this->CTest = c;
this->SetErrorCallback(xmlReportError, (void*)c);
}
void StartElement(const std::string& name, const char** atts) CM_OVERRIDE
{
if (name == "MemoryLeak" || name == "ResourceLeak") {
this->Errors.push_back(cmCTestMemCheckHandler::MLK);
} else if (name == "Error" || name == "Dangling Pointer") {
this->ParseError(atts);
2007-07-24 22:43:31 +04:00
}
// Create the log
std::ostringstream ostr;
ostr << name << ":\n";
int i = 0;
2016-06-27 23:44:16 +03:00
for (; atts[i] != CM_NULLPTR; i += 2) {
ostr << " " << atts[i] << " - " << atts[i + 1] << "\n";
2007-07-24 22:43:31 +04:00
}
ostr << "\n";
this->Log += ostr.str();
}
void EndElement(const std::string& /*name*/) CM_OVERRIDE {}
2007-07-24 22:43:31 +04:00
const char* GetAttribute(const char* name, const char** atts)
{
int i = 0;
2016-06-27 23:44:16 +03:00
for (; atts[i] != CM_NULLPTR; ++i) {
if (strcmp(name, atts[i]) == 0) {
return atts[i + 1];
}
2007-07-24 22:43:31 +04:00
}
2016-06-27 23:44:16 +03:00
return CM_NULLPTR;
}
2007-07-24 22:43:31 +04:00
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++;
2007-07-24 22:43:31 +04:00
}
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);
}
}
2007-07-24 22:43:31 +04:00
cmCTest* CTest;
std::vector<int> Errors;
std::string Log;
};
#define BOUNDS_CHECKER_MARKER \
"******######*****Begin BOUNDS CHECKER XML******######******"
2005-01-27 23:54:47 +03:00
cmCTestMemCheckHandler::cmCTestMemCheckHandler()
{
2006-03-10 23:03:09 +03:00
this->MemCheck = true;
this->CustomMaximumPassedTestOutputSize = 0;
this->CustomMaximumFailedTestOutputSize = 0;
this->LogWithPID = false;
2005-01-27 23:54:47 +03:00
}
void cmCTestMemCheckHandler::Initialize()
{
this->Superclass::Initialize();
this->LogWithPID = false;
this->CustomMaximumPassedTestOutputSize = 0;
this->CustomMaximumFailedTestOutputSize = 0;
2006-03-10 23:03:09 +03:00
this->MemoryTester = "";
this->MemoryTesterDynamicOptions.clear();
this->MemoryTesterOptions.clear();
2006-03-10 23:03:09 +03:00
this->MemoryTesterStyle = UNKNOWN;
this->MemoryTesterOutputFile = "";
}
2005-01-27 23:54:47 +03:00
int cmCTestMemCheckHandler::PreProcessHandler()
{
if (!this->InitializeMemoryChecking()) {
2005-01-27 23:54:47 +03:00
return 0;
}
2005-01-27 23:54:47 +03:00
if (!this->ExecuteCommands(this->CustomPreMemCheck)) {
2006-03-10 23:03:09 +03:00
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Problem executing pre-memcheck command(s)." << std::endl);
2005-01-27 23:54:47 +03:00
return 0;
}
2005-01-27 23:54:47 +03:00
return 1;
}
int cmCTestMemCheckHandler::PostProcessHandler()
{
if (!this->ExecuteCommands(this->CustomPostMemCheck)) {
2006-03-10 23:03:09 +03:00
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Problem executing post-memcheck command(s)." << std::endl);
2005-01-27 23:54:47 +03:00
return 0;
}
2005-01-27 23:54:47 +03:00
return 1;
}
2006-03-09 19:17:10 +03:00
void cmCTestMemCheckHandler::GenerateTestCommand(
std::vector<std::string>& args, int test)
2005-01-27 23:54:47 +03:00
{
std::vector<std::string>::size_type pp;
std::string index;
std::ostringstream 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.empty()) {
// 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.empty()) {
std::string::size_type pos = memTesterEnvironmentVariable.find("??");
if (pos != std::string::npos) {
memTesterEnvironmentVariable.replace(pos, 2, index);
}
memcheckcommand += " " + memTesterEnvironmentVariable;
args.push_back(memTesterEnvironmentVariable);
}
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Memory check command: " << memcheckcommand << std::endl,
this->Quiet);
2005-01-27 23:54:47 +03:00
}
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[] = {
2016-06-27 23:44:16 +03:00
"ABR", "ABW", "ABWL", "COR", "EXU", "FFM", "FIM", "FMM",
"FMR", "FMW", "FUM", "IPR", "IPW", "MAF", "MLK", "MPK",
"NPR", "ODS", "PAR", "PLK", "UMC", "UMR", CM_NULLPTR
};
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",
2016-06-27 23:44:16 +03:00
CM_NULLPTR
};
this->GlobalResults.clear();
2016-06-27 23:44:16 +03:00
for (int i = 0; cmCTestMemCheckResultStrings[i] != CM_NULLPTR; ++i) {
this->ResultStrings.push_back(cmCTestMemCheckResultStrings[i]);
this->ResultStringsLong.push_back(cmCTestMemCheckResultLongStrings[i]);
this->GlobalResults.push_back(0);
}
}
void cmCTestMemCheckHandler::PopulateCustomVectors(cmMakefile* mf)
2005-01-27 23:54:47 +03:00
{
this->cmCTestTestHandler::PopulateCustomVectors(mf);
this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_PRE_MEMCHECK",
this->CustomPreMemCheck);
this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_POST_MEMCHECK",
this->CustomPostMemCheck);
2005-01-27 23:54:47 +03:00
this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_MEMCHECK_IGNORE",
this->CustomTestsIgnore);
std::string cmake = cmSystemTools::GetCMakeCommand();
2015-02-18 01:15:00 +03:00
this->CTest->SetCTestConfiguration("CMakeCommand", cmake.c_str(),
this->Quiet);
2005-01-27 23:54:47 +03:00
}
void cmCTestMemCheckHandler::GenerateDartOutput(cmXMLWriter& xml)
2005-01-27 23:54:47 +03:00
{
if (!this->CTest->GetProduceXML()) {
2005-01-27 23:54:47 +03:00
return;
}
this->CTest->StartXML(xml, this->AppendXML);
xml.StartElement("DynamicAnalysis");
switch (this->MemoryTesterStyle) {
2005-01-27 23:54:47 +03:00
case cmCTestMemCheckHandler::VALGRIND:
xml.Attribute("Checker", "Valgrind");
2005-01-27 23:54:47 +03:00
break;
case cmCTestMemCheckHandler::PURIFY:
xml.Attribute("Checker", "Purify");
2005-01-27 23:54:47 +03:00
break;
case cmCTestMemCheckHandler::BOUNDS_CHECKER:
xml.Attribute("Checker", "BoundsChecker");
2005-01-27 23:54:47 +03:00
break;
case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
xml.Attribute("Checker", "AddressSanitizer");
break;
case cmCTestMemCheckHandler::THREAD_SANITIZER:
xml.Attribute("Checker", "ThreadSanitizer");
break;
case cmCTestMemCheckHandler::MEMORY_SANITIZER:
xml.Attribute("Checker", "MemorySanitizer");
break;
case cmCTestMemCheckHandler::UB_SANITIZER:
xml.Attribute("Checker", "UndefinedBehaviorSanitizer");
break;
2005-01-27 23:54:47 +03:00
default:
xml.Attribute("Checker", "Unknown");
}
2005-01-27 23:54:47 +03:00
xml.Element("StartDateTime", this->StartTest);
xml.Element("StartTestTime", this->StartTestTime);
xml.StartElement("TestList");
2006-03-10 23:03:09 +03:00
cmCTestMemCheckHandler::TestResultsVector::size_type cc;
for (cc = 0; cc < this->TestResults.size(); cc++) {
cmCTestTestResult* result = &this->TestResults[cc];
2006-03-10 23:03:09 +03:00
std::string testPath = result->Path + "/" + result->Name;
xml.Element("Test", this->CTest->GetShortPathToFile(testPath.c_str()));
}
xml.EndElement(); // TestList
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
"-- Processing memory checking output: ", this->Quiet);
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];
2005-01-27 23:54:47 +03:00
std::string memcheckstr;
std::vector<int> memcheckresults(this->ResultStrings.size(), 0);
2006-03-10 23:03:09 +03:00
bool res = this->ProcessMemCheckOutput(result->Output, memcheckstr,
memcheckresults);
if (res && result->Status == cmCTestMemCheckHandler::COMPLETED) {
2005-01-27 23:54:47 +03:00
continue;
}
this->CleanTestOutput(
memcheckstr,
static_cast<size_t>(this->CustomMaximumFailedTestOutputSize));
this->WriteTestResultHeader(xml, result);
xml.StartElement("Results");
for (std::vector<int>::size_type kk = 0; kk < memcheckresults.size();
++kk) {
if (memcheckresults[kk]) {
xml.StartElement("Defect");
xml.Attribute("type", this->ResultStringsLong[kk]);
xml.Content(memcheckresults[kk]);
xml.EndElement(); // Defect
2005-01-27 23:54:47 +03:00
}
this->GlobalResults[kk] += memcheckresults[kk];
}
xml.EndElement(); // Results
xml.StartElement("Log");
if (this->CTest->ShouldCompressMemCheckOutput()) {
this->CTest->CompressString(memcheckstr);
xml.Attribute("compression", "gzip");
xml.Attribute("encoding", "base64");
}
xml.Content(memcheckstr);
xml.EndElement(); // Log
this->WriteTestResultFooter(xml, result);
if (current < cc) {
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "#" << std::flush,
this->Quiet);
2005-01-27 23:54:47 +03:00
current += step;
}
}
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, std::endl, this->Quiet);
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
"Memory checking results:" << std::endl, this->Quiet);
xml.StartElement("DefectList");
for (cc = 0; cc < this->GlobalResults.size(); cc++) {
if (this->GlobalResults[cc]) {
#ifdef cerr
#undef cerr
#endif
2005-01-27 23:54:47 +03:00
std::cerr.width(35);
#define cerr no_cerr
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
this->ResultStringsLong[cc]
<< " - " << this->GlobalResults[cc] << std::endl,
this->Quiet);
xml.StartElement("Defect");
xml.Attribute("Type", this->ResultStringsLong[cc]);
xml.EndElement();
2005-01-27 23:54:47 +03:00
}
}
xml.EndElement(); // DefectList
2005-01-27 23:54:47 +03:00
xml.Element("EndDateTime", this->EndTest);
xml.Element("EndTestTime", this->EndTestTime);
xml.Element("ElapsedMinutes",
static_cast<int>(this->ElapsedTestingTime / 6) / 10.0);
2006-03-09 19:17:10 +03:00
xml.EndElement(); // DynamicAnalysis
this->CTest->EndXML(xml);
2005-01-27 23:54:47 +03:00
}
bool cmCTestMemCheckHandler::InitializeMemoryChecking()
{
this->MemoryTesterEnvironmentVariable = "";
this->MemoryTester = "";
2005-01-27 23:54:47 +03:00
// Setup the command
if (cmSystemTools::FileExists(
this->CTest->GetCTestConfiguration("MemoryCheckCommand").c_str())) {
this->MemoryTester =
this->CTest->GetCTestConfiguration("MemoryCheckCommand");
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;
2005-01-27 23:54:47 +03:00
}
} else if (cmSystemTools::FileExists(
this->CTest->GetCTestConfiguration("PurifyCommand").c_str())) {
this->MemoryTester = this->CTest->GetCTestConfiguration("PurifyCommand");
this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
} else if (cmSystemTools::FileExists(
this->CTest->GetCTestConfiguration("ValgrindCommand")
.c_str())) {
this->MemoryTester = this->CTest->GetCTestConfiguration("ValgrindCommand");
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
} else if (cmSystemTools::FileExists(
this->CTest->GetCTestConfiguration("BoundsCheckerCommand")
.c_str())) {
this->MemoryTester =
this->CTest->GetCTestConfiguration("BoundsCheckerCommand");
this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
}
if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
"AddressSanitizer") {
this->MemoryTester = this->CTest->GetCTestConfiguration("CMakeCommand");
this->MemoryTesterStyle = cmCTestMemCheckHandler::ADDRESS_SANITIZER;
this->LogWithPID = true; // even if we give the log file the pid is added
}
if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
"ThreadSanitizer") {
this->MemoryTester = this->CTest->GetCTestConfiguration("CMakeCommand");
this->MemoryTesterStyle = cmCTestMemCheckHandler::THREAD_SANITIZER;
this->LogWithPID = true; // even if we give the log file the pid is added
}
if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
"MemorySanitizer") {
this->MemoryTester = this->CTest->GetCTestConfiguration("CMakeCommand");
this->MemoryTesterStyle = cmCTestMemCheckHandler::MEMORY_SANITIZER;
this->LogWithPID = true; // even if we give the log file the pid is added
}
if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
"UndefinedBehaviorSanitizer") {
this->MemoryTester = this->CTest->GetCTestConfiguration("CMakeCommand");
this->MemoryTesterStyle = cmCTestMemCheckHandler::UB_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.empty()) {
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, WARNING,
"Memory checker (MemoryCheckCommand) "
"not set, or cannot find the specified program."
<< std::endl,
this->Quiet);
2005-01-27 23:54:47 +03:00
return false;
}
2005-01-27 23:54:47 +03:00
// Setup the options
std::string memoryTesterOptions;
if (!this->CTest->GetCTestConfiguration("MemoryCheckCommandOptions")
.empty()) {
memoryTesterOptions =
this->CTest->GetCTestConfiguration("MemoryCheckCommandOptions");
} else if (!this->CTest->GetCTestConfiguration("ValgrindCommandOptions")
.empty()) {
memoryTesterOptions =
this->CTest->GetCTestConfiguration("ValgrindCommandOptions");
}
this->MemoryTesterOptions =
cmSystemTools::ParseArguments(memoryTesterOptions.c_str());
2005-01-27 23:54:47 +03:00
this->MemoryTesterOutputFile =
this->CTest->GetBinaryDir() + "/Testing/Temporary/MemoryChecker.??.log";
2005-01-27 23:54:47 +03:00
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")
.empty()) {
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=";
2005-01-27 23:54:47 +03:00
#else
outputFile = "-log-file=";
2005-01-27 23:54:47 +03:00
#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;
}
// these are almost the same but the env var used is different
case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
case cmCTestMemCheckHandler::THREAD_SANITIZER:
case cmCTestMemCheckHandler::MEMORY_SANITIZER:
case cmCTestMemCheckHandler::UB_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 envVar;
std::string extraOptions =
this->CTest->GetCTestConfiguration("MemoryCheckSanitizerOptions");
if (this->MemoryTesterStyle ==
cmCTestMemCheckHandler::ADDRESS_SANITIZER) {
envVar = "ASAN_OPTIONS";
extraOptions += " detect_leaks=1";
} else if (this->MemoryTesterStyle ==
cmCTestMemCheckHandler::THREAD_SANITIZER) {
envVar = "TSAN_OPTIONS";
} else if (this->MemoryTesterStyle ==
cmCTestMemCheckHandler::MEMORY_SANITIZER) {
envVar = "MSAN_OPTIONS";
} else if (this->MemoryTesterStyle ==
cmCTestMemCheckHandler::UB_SANITIZER) {
envVar = "UBSAN_OPTIONS";
}
std::string outputFile =
envVar + "=log_path=\"" + this->MemoryTesterOutputFile + "\" ";
this->MemoryTesterEnvironmentVariable = outputFile + extraOptions;
break;
}
default:
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Do not understand memory checker: " << this->MemoryTester
<< std::endl);
return false;
}
2005-01-27 23:54:47 +03:00
this->InitializeResultsVectors();
// std::vector<std::string>::size_type cc;
// for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ )
// {
// this->MemoryTesterGlobalResults[cc] = 0;
// }
2005-01-27 23:54:47 +03:00
return true;
}
bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
std::string& log,
std::vector<int>& results)
2005-01-27 23:54:47 +03:00
{
2016-08-18 21:04:21 +03:00
switch (this->MemoryTesterStyle) {
case cmCTestMemCheckHandler::VALGRIND:
return this->ProcessMemCheckValgrindOutput(str, log, results);
case cmCTestMemCheckHandler::PURIFY:
return this->ProcessMemCheckPurifyOutput(str, log, results);
case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
case cmCTestMemCheckHandler::THREAD_SANITIZER:
case cmCTestMemCheckHandler::MEMORY_SANITIZER:
case cmCTestMemCheckHandler::UB_SANITIZER:
return this->ProcessMemCheckSanitizerOutput(str, log, results);
case cmCTestMemCheckHandler::BOUNDS_CHECKER:
return this->ProcessMemCheckBoundsCheckerOutput(str, log, results);
default:
log.append("\nMemory checking style used was: ");
log.append("None that I know");
log = str;
return true;
}
2005-01-27 23:54:47 +03:00
}
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::ProcessMemCheckSanitizerOutput(
const std::string& str, std::string& log, std::vector<int>& result)
{
std::string regex;
switch (this->MemoryTesterStyle) {
case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
regex = "ERROR: AddressSanitizer: (.*) on.*";
break;
case cmCTestMemCheckHandler::THREAD_SANITIZER:
regex = "WARNING: ThreadSanitizer: (.*) \\(pid=.*\\)";
break;
case cmCTestMemCheckHandler::MEMORY_SANITIZER:
regex = "WARNING: MemorySanitizer: (.*)";
break;
case cmCTestMemCheckHandler::UB_SANITIZER:
regex = "runtime error: (.*)";
break;
default:
break;
}
cmsys::RegularExpression sanitizerWarning(regex);
cmsys::RegularExpression leakWarning("(Direct|Indirect) leak of .*");
int defects = 0;
std::vector<std::string> lines;
cmSystemTools::Split(str.c_str(), lines);
std::ostringstream ostr;
log = "";
for (std::vector<std::string>::iterator i = lines.begin(); i != lines.end();
++i) {
std::string resultFound;
if (leakWarning.find(*i)) {
resultFound = leakWarning.match(1) + " leak";
} else if (sanitizerWarning.find(*i)) {
resultFound = sanitizerWarning.match(1);
}
if (!resultFound.empty()) {
std::vector<int>::size_type idx = this->FindOrAddWarning(resultFound);
if (result.empty() || idx > result.size() - 1) {
result.push_back(1);
} else {
result[idx]++;
}
defects++;
ostr << "<b>" << this->ResultStrings[idx] << "</b> ";
}
ostr << *i << std::endl;
}
log = ostr.str();
return defects == 0;
}
2005-01-27 23:54:47 +03:00
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);
std::ostringstream ostr;
2005-01-27 23:54:47 +03:00
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]) {
2005-01-27 23:54:47 +03:00
failure = cc;
break;
}
}
if (cc == this->ResultStrings.size()) {
2006-03-10 23:03:09 +03:00
cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown Purify memory fault: "
<< pfW.match(1) << std::endl);
2006-03-09 19:17:10 +03:00
ostr << "*** Unknown Purify memory fault: " << pfW.match(1)
<< std::endl;
2005-01-27 23:54:47 +03:00
}
}
if (failure != this->ResultStrings.size()) {
ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
results[failure]++;
defects++;
}
ostr << *i << std::endl;
}
2005-01-27 23:54:47 +03:00
log = ostr.str();
return defects == 0;
2005-01-27 23:54:47 +03:00
}
bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
const std::string& str, std::string& log, std::vector<int>& results)
2005-01-27 23:54:47 +03:00
{
std::vector<std::string> lines;
2005-01-27 23:54:47 +03:00
cmSystemTools::Split(str.c_str(), lines);
bool unlimitedOutput = false;
if (str.find("CTEST_FULL_OUTPUT") != str.npos ||
this->CustomMaximumFailedTestOutputSize == 0) {
unlimitedOutput = true;
}
2005-01-27 23:54:47 +03:00
std::string::size_type cc;
std::ostringstream ostr;
2005-01-27 23:54:47 +03:00
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 \\[\\]");
2009-10-29 15:38:04 +03:00
cmsys::RegularExpression vgMLK1(
"== .*[0-9,]+ bytes in [0-9,]+ blocks are definitely lost"
" in loss record [0-9,]+ of [0-9,]+");
2009-10-29 15:38:04 +03:00
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,]+");
2005-01-27 23:54:47 +03:00
cmsys::RegularExpression vgPAR(
"== .*Syscall param .* (contains|points to) unaddressable byte\\(s\\)");
2005-01-27 23:54:47 +03:00
cmsys::RegularExpression vgMPK1(
"== .*[0-9,]+ bytes in [0-9,]+ blocks are possibly lost in"
" loss record [0-9,]+ of [0-9,]+");
2005-01-27 23:54:47 +03:00
cmsys::RegularExpression vgMPK2(
"== .*[0-9,]+ bytes in [0-9,]+ blocks are still reachable"
" in loss record [0-9,]+ of [0-9,]+");
2005-01-27 23:54:47 +03:00
cmsys::RegularExpression vgUMC(
"== .*Conditional jump or move depends on uninitialised value\\(s\\)");
2006-03-09 19:17:10 +03:00
cmsys::RegularExpression vgUMR1(
"== .*Use of uninitialised value of size [0-9,]+");
cmsys::RegularExpression vgUMR2("== .*Invalid read of size [0-9,]+");
2005-01-27 23:54:47 +03:00
cmsys::RegularExpression vgUMR3("== .*Jump to the invalid address ");
cmsys::RegularExpression vgUMR4(
"== .*Syscall param .* contains "
2006-03-09 19:17:10 +03:00
"uninitialised or unaddressable byte\\(s\\)");
2007-08-04 00:35:22 +04:00
cmsys::RegularExpression vgUMR5("== .*Syscall param .* uninitialised");
cmsys::RegularExpression vgIPW("== .*Invalid write of size [0-9,]+");
2006-03-09 19:17:10 +03:00
cmsys::RegularExpression vgABR("== .*pthread_mutex_unlock: mutex is "
"locked by a different thread");
std::vector<std::string::size_type> nonValGrindOutput;
double sttime = cmSystemTools::GetTime();
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, DEBUG,
"Start test: " << lines.size() << std::endl, this->Quiet);
std::string::size_type totalOutputSize = 0;
for (cc = 0; cc < lines.size(); cc++) {
cmCTestOptionalLog(this->CTest, DEBUG,
"test line " << lines[cc] << std::endl, this->Quiet);
if (valgrindLine.find(lines[cc])) {
cmCTestOptionalLog(this->CTest, DEBUG,
"valgrind line " << lines[cc] << std::endl,
this->Quiet);
2005-01-27 23:54:47 +03:00
int failure = cmCTestMemCheckHandler::NO_MEMORY_FAULT;
if (vgFIM.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::FIM;
} else if (vgFMM.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::FMM;
} else if (vgMLK1.find(lines[cc])) {
2009-10-29 15:38:04 +03:00
failure = cmCTestMemCheckHandler::MLK;
} else if (vgMLK2.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::MLK;
} else if (vgPAR.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::PAR;
} else if (vgMPK1.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::MPK;
} else if (vgMPK2.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::MPK;
} else if (vgUMC.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::UMC;
} else if (vgUMR1.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::UMR;
} else if (vgUMR2.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::UMR;
} else if (vgUMR3.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::UMR;
} else if (vgUMR4.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::UMR;
} else if (vgUMR5.find(lines[cc])) {
2007-08-04 00:35:22 +04:00
failure = cmCTestMemCheckHandler::UMR;
} else if (vgIPW.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::IPW;
} else if (vgABR.find(lines[cc])) {
2006-03-09 19:17:10 +03:00
failure = cmCTestMemCheckHandler::ABR;
}
2005-01-27 23:54:47 +03:00
if (failure != cmCTestMemCheckHandler::NO_MEMORY_FAULT) {
ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
results[failure]++;
defects++;
}
totalOutputSize += lines[cc].size();
ostr << lines[cc] << std::endl;
} else {
nonValGrindOutput.push_back(cc);
}
}
// Now put all all the non valgrind output into the test output
// This should be last in case it gets truncated by the output
// limiting code
for (std::vector<std::string::size_type>::iterator i =
nonValGrindOutput.begin();
i != nonValGrindOutput.end(); ++i) {
totalOutputSize += lines[*i].size();
ostr << lines[*i] << std::endl;
if (!unlimitedOutput &&
totalOutputSize >
static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)) {
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";
break; // stop the copy of output if we are full
2005-01-27 23:54:47 +03:00
}
}
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, DEBUG, "End test (elapsed: "
<< (cmSystemTools::GetTime() - sttime) << std::endl,
this->Quiet);
2005-01-27 23:54:47 +03:00
log = ostr.str();
return defects == 0;
2005-01-27 23:54:47 +03:00
}
2007-07-24 22:43:31 +04:00
bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
const std::string& str, std::string& log, std::vector<int>& results)
2007-07-24 22:43:31 +04:00
{
log = "";
double sttime = cmSystemTools::GetTime();
std::vector<std::string> lines;
cmSystemTools::Split(str.c_str(), lines);
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, DEBUG,
"Start test: " << lines.size() << std::endl, this->Quiet);
std::vector<std::string>::size_type cc;
for (cc = 0; cc < lines.size(); cc++) {
if (lines[cc] == BOUNDS_CHECKER_MARKER) {
2007-07-24 22:43:31 +04:00
break;
}
}
2007-07-24 22:43:31 +04:00
cmBoundsCheckerParser parser(this->CTest);
parser.InitializeParser();
if (cc < lines.size()) {
for (cc++; cc < lines.size(); ++cc) {
2007-07-24 22:43:31 +04:00
std::string& theLine = lines[cc];
// check for command line arguments that are not escaped
// correctly by BC
if (theLine.find("TargetArgs=") != theLine.npos) {
2007-07-24 22:43:31 +04:00
// skip this because BC gets it wrong and we can't parse it
} else if (!parser.ParseChunk(theLine.c_str(), theLine.size())) {
2007-07-24 22:43:31 +04:00
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Error in ParseChunk: " << theLine << std::endl);
2007-07-24 22:43:31 +04:00
}
}
}
2007-07-24 22:43:31 +04:00
int defects = 0;
for (cc = 0; cc < parser.Errors.size(); ++cc) {
2007-07-24 22:43:31 +04:00
results[parser.Errors[cc]]++;
defects++;
}
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, DEBUG, "End test (elapsed: "
<< (cmSystemTools::GetTime() - sttime) << std::endl,
this->Quiet);
if (defects) {
2007-07-24 22:43:31 +04:00
// only put the output of Bounds Checker if there were
// errors or leaks detected
log = parser.Log;
return false;
}
2007-07-24 22:43:31 +04:00
return true;
}
// PostProcessTest memcheck results
void cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res, int test)
{
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"PostProcessTest memcheck results for : " << res.Name
<< std::endl,
this->Quiet);
if (this->MemoryTesterStyle == cmCTestMemCheckHandler::BOUNDS_CHECKER) {
this->PostProcessBoundsCheckerTest(res, test);
} else {
std::vector<std::string> files;
this->TestOutputFileNames(test, files);
for (std::vector<std::string>::iterator i = files.begin();
i != files.end(); ++i) {
this->AppendMemTesterOutput(res, *i);
}
}
}
2007-07-24 22:43:31 +04:00
// This method puts the bounds checker output file into the output
// for the test
void cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(
cmCTestTestResult& res, int test)
{
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"PostProcessBoundsCheckerTest for : " << res.Name
<< std::endl,
this->Quiet);
std::vector<std::string> files;
this->TestOutputFileNames(test, files);
if (files.empty()) {
2007-07-24 22:43:31 +04:00
return;
}
std::string ofile = files[0];
if (ofile.empty()) {
return;
}
2007-07-24 22:43:31 +04:00
// 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 << std::endl);
return;
}
res.Output += BOUNDS_CHECKER_MARKER;
2007-07-24 22:43:31 +04:00
res.Output += "\n";
std::string line;
while (cmSystemTools::GetLineFromStream(ifs, line)) {
res.Output += line;
res.Output += "\n";
2007-07-24 22:43:31 +04:00
}
}
cmSystemTools::Delay(1000);
cmSystemTools::RemoveFile(this->BoundsCheckerDPBDFile);
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Remove: " << this->BoundsCheckerDPBDFile << std::endl,
this->Quiet);
cmSystemTools::RemoveFile(this->BoundsCheckerXMLFile);
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Remove: " << this->BoundsCheckerXMLFile << std::endl,
this->Quiet);
2007-07-24 22:43:31 +04:00
}
void cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res,
std::string const& ofile)
{
if (ofile.empty()) {
2007-07-24 22:43:31 +04:00
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 << std::endl);
return;
}
std::string line;
while (cmSystemTools::GetLineFromStream(ifs, line)) {
res.Output += line;
res.Output += "\n";
2007-07-24 22:43:31 +04:00
}
}
if (this->LogWithPID) {
cmSystemTools::RemoveFile(ofile);
2015-02-18 01:15:00 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Remove: " << ofile << "\n", this->Quiet);
}
2007-07-24 22:43:31 +04:00
}
void cmCTestMemCheckHandler::TestOutputFileNames(
int test, std::vector<std::string>& files)
{
std::string index;
std::ostringstream 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().empty()) {
std::string log = "Cannot find memory tester output file: " + ofile;
cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
ofile = "";
} else {
files = g.GetFiles();
return;
}
} else if (!cmSystemTools::FileExists(ofile.c_str())) {
std::string log = "Cannot find memory tester output file: " + ofile;
cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
ofile = "";
}
files.push_back(ofile);
}