|
@@ -2,9 +2,11 @@
|
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
#include "cmCTestMemCheckHandler.h"
|
|
#include "cmCTestMemCheckHandler.h"
|
|
|
|
|
|
|
|
|
|
+#include <algorithm>
|
|
|
#include <chrono>
|
|
#include <chrono>
|
|
|
#include <cstring>
|
|
#include <cstring>
|
|
|
#include <iostream>
|
|
#include <iostream>
|
|
|
|
|
+#include <iterator>
|
|
|
#include <sstream>
|
|
#include <sstream>
|
|
|
#include <utility>
|
|
#include <utility>
|
|
|
|
|
|
|
@@ -12,6 +14,7 @@
|
|
|
#include "cmsys/Glob.hxx"
|
|
#include "cmsys/Glob.hxx"
|
|
|
#include "cmsys/RegularExpression.hxx"
|
|
#include "cmsys/RegularExpression.hxx"
|
|
|
|
|
|
|
|
|
|
+#include "cmAlgorithms.h"
|
|
|
#include "cmCTest.h"
|
|
#include "cmCTest.h"
|
|
|
#include "cmDuration.h"
|
|
#include "cmDuration.h"
|
|
|
#include "cmSystemTools.h"
|
|
#include "cmSystemTools.h"
|
|
@@ -165,6 +168,10 @@ void cmCTestMemCheckHandler::GenerateTestCommand(
|
|
|
std::string index = std::to_string(test);
|
|
std::string index = std::to_string(test);
|
|
|
std::string memcheckcommand =
|
|
std::string memcheckcommand =
|
|
|
cmSystemTools::ConvertToOutputPath(this->MemoryTester);
|
|
cmSystemTools::ConvertToOutputPath(this->MemoryTester);
|
|
|
|
|
+
|
|
|
|
|
+ std::vector<std::string> dirs;
|
|
|
|
|
+ bool nextArgIsDir = false;
|
|
|
|
|
+
|
|
|
for (std::string arg : this->MemoryTesterDynamicOptions) {
|
|
for (std::string arg : this->MemoryTesterDynamicOptions) {
|
|
|
std::string::size_type pos = arg.find("??");
|
|
std::string::size_type pos = arg.find("??");
|
|
|
if (pos != std::string::npos) {
|
|
if (pos != std::string::npos) {
|
|
@@ -174,6 +181,16 @@ void cmCTestMemCheckHandler::GenerateTestCommand(
|
|
|
memcheckcommand += " \"";
|
|
memcheckcommand += " \"";
|
|
|
memcheckcommand += arg;
|
|
memcheckcommand += arg;
|
|
|
memcheckcommand += "\"";
|
|
memcheckcommand += "\"";
|
|
|
|
|
+
|
|
|
|
|
+ if (nextArgIsDir) {
|
|
|
|
|
+ nextArgIsDir = false;
|
|
|
|
|
+ dirs.push_back(arg);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (this->MemoryTesterStyle == cmCTestMemCheckHandler::DRMEMORY &&
|
|
|
|
|
+ (arg == "-logdir" || arg == "-symcache_dir")) {
|
|
|
|
|
+ nextArgIsDir = true;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
// Create a copy of the memory tester environment variable.
|
|
// Create a copy of the memory tester environment variable.
|
|
|
// This is used for memory testing programs that pass options
|
|
// This is used for memory testing programs that pass options
|
|
@@ -205,6 +222,11 @@ void cmCTestMemCheckHandler::GenerateTestCommand(
|
|
|
memcheckcommand += " " + memTesterEnvironmentVariable;
|
|
memcheckcommand += " " + memTesterEnvironmentVariable;
|
|
|
args.push_back(memTesterEnvironmentVariable);
|
|
args.push_back(memTesterEnvironmentVariable);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ for (std::string const& dir : dirs) {
|
|
|
|
|
+ cmSystemTools::MakeDirectory(dir);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
|
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
|
|
"Memory check command: " << memcheckcommand << std::endl,
|
|
"Memory check command: " << memcheckcommand << std::endl,
|
|
|
this->Quiet);
|
|
this->Quiet);
|
|
@@ -297,6 +319,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(cmXMLWriter& xml)
|
|
|
case cmCTestMemCheckHandler::VALGRIND:
|
|
case cmCTestMemCheckHandler::VALGRIND:
|
|
|
xml.Attribute("Checker", "Valgrind");
|
|
xml.Attribute("Checker", "Valgrind");
|
|
|
break;
|
|
break;
|
|
|
|
|
+ case cmCTestMemCheckHandler::DRMEMORY:
|
|
|
|
|
+ xml.Attribute("Checker", "DrMemory");
|
|
|
|
|
+ break;
|
|
|
case cmCTestMemCheckHandler::PURIFY:
|
|
case cmCTestMemCheckHandler::PURIFY:
|
|
|
xml.Attribute("Checker", "Purify");
|
|
xml.Attribute("Checker", "Purify");
|
|
|
break;
|
|
break;
|
|
@@ -434,6 +459,10 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
|
|
|
if (testerName.find("valgrind") != std::string::npos ||
|
|
if (testerName.find("valgrind") != std::string::npos ||
|
|
|
this->CTest->GetCTestConfiguration("MemoryCheckType") == "Valgrind") {
|
|
this->CTest->GetCTestConfiguration("MemoryCheckType") == "Valgrind") {
|
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
|
|
|
|
|
+ } else if (testerName.find("drmemory") != std::string::npos ||
|
|
|
|
|
+ this->CTest->GetCTestConfiguration("MemoryCheckType") ==
|
|
|
|
|
+ "DrMemory") {
|
|
|
|
|
+ this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
|
|
|
} else if (testerName.find("purify") != std::string::npos) {
|
|
} else if (testerName.find("purify") != std::string::npos) {
|
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
|
|
|
} else if (testerName.find("BC") != std::string::npos) {
|
|
} else if (testerName.find("BC") != std::string::npos) {
|
|
@@ -449,6 +478,10 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
|
|
|
this->CTest->GetCTestConfiguration("ValgrindCommand"))) {
|
|
this->CTest->GetCTestConfiguration("ValgrindCommand"))) {
|
|
|
this->MemoryTester = this->CTest->GetCTestConfiguration("ValgrindCommand");
|
|
this->MemoryTester = this->CTest->GetCTestConfiguration("ValgrindCommand");
|
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
|
|
|
|
|
+ } else if (cmSystemTools::FileExists(
|
|
|
|
|
+ this->CTest->GetCTestConfiguration("DrMemoryCommand"))) {
|
|
|
|
|
+ this->MemoryTester = this->CTest->GetCTestConfiguration("DrMemoryCommand");
|
|
|
|
|
+ this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
|
|
|
} else if (cmSystemTools::FileExists(
|
|
} else if (cmSystemTools::FileExists(
|
|
|
this->CTest->GetCTestConfiguration("BoundsCheckerCommand"))) {
|
|
this->CTest->GetCTestConfiguration("BoundsCheckerCommand"))) {
|
|
|
this->MemoryTester =
|
|
this->MemoryTester =
|
|
@@ -495,6 +528,8 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
|
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
|
|
|
} else if (checkType == "Valgrind") {
|
|
} else if (checkType == "Valgrind") {
|
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
|
|
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
|
|
|
|
|
+ } else if (checkType == "DrMemory") {
|
|
|
|
|
+ this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
if (this->MemoryTester.empty()) {
|
|
if (this->MemoryTester.empty()) {
|
|
@@ -516,6 +551,10 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
|
|
|
.empty()) {
|
|
.empty()) {
|
|
|
memoryTesterOptions =
|
|
memoryTesterOptions =
|
|
|
this->CTest->GetCTestConfiguration("ValgrindCommandOptions");
|
|
this->CTest->GetCTestConfiguration("ValgrindCommandOptions");
|
|
|
|
|
+ } else if (!this->CTest->GetCTestConfiguration("DrMemoryCommandOptions")
|
|
|
|
|
+ .empty()) {
|
|
|
|
|
+ memoryTesterOptions =
|
|
|
|
|
+ this->CTest->GetCTestConfiguration("DrMemoryCommandOptions");
|
|
|
}
|
|
}
|
|
|
this->MemoryTesterOptions =
|
|
this->MemoryTesterOptions =
|
|
|
cmSystemTools::ParseArguments(memoryTesterOptions);
|
|
cmSystemTools::ParseArguments(memoryTesterOptions);
|
|
@@ -551,6 +590,64 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
|
|
|
this->MemoryTesterOutputFile);
|
|
this->MemoryTesterOutputFile);
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
|
|
+ case cmCTestMemCheckHandler::DRMEMORY: {
|
|
|
|
|
+ std::string tempDrMemoryDir =
|
|
|
|
|
+ this->CTest->GetBinaryDir() + "/Testing/Temporary/DrMemory";
|
|
|
|
|
+
|
|
|
|
|
+ if (!cmContains(this->MemoryTesterOptions, "-quiet")) {
|
|
|
|
|
+ this->MemoryTesterOptions.emplace_back("-quiet");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!cmContains(this->MemoryTesterOptions, "-batch")) {
|
|
|
|
|
+ this->MemoryTesterOptions.emplace_back("-batch");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this->MemoryTesterDynamicOptions.emplace_back("-logdir");
|
|
|
|
|
+ auto logdirOption =
|
|
|
|
|
+ std::find(this->MemoryTesterOptions.begin(),
|
|
|
|
|
+ this->MemoryTesterOptions.end(), "-logdir");
|
|
|
|
|
+ if (logdirOption == this->MemoryTesterOptions.end()) {
|
|
|
|
|
+ // No logdir found in memory tester options
|
|
|
|
|
+ std::string drMemoryLogDir = tempDrMemoryDir + "/??";
|
|
|
|
|
+ this->MemoryTesterDynamicOptions.push_back(drMemoryLogDir);
|
|
|
|
|
+ this->MemoryTesterOutputFile = drMemoryLogDir;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Use logdir found in memory tester options
|
|
|
|
|
+ auto logdirLocation = std::next(logdirOption);
|
|
|
|
|
+ this->MemoryTesterOutputFile = *logdirLocation;
|
|
|
|
|
+ this->MemoryTesterDynamicOptions.push_back(*logdirLocation);
|
|
|
|
|
+ this->MemoryTesterOptions.erase(logdirOption, logdirLocation + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ this->MemoryTesterOutputFile += "/*/results.txt";
|
|
|
|
|
+
|
|
|
|
|
+ if (std::find(this->MemoryTesterOptions.begin(),
|
|
|
|
|
+ this->MemoryTesterOptions.end(),
|
|
|
|
|
+ "-symcache_dir") == this->MemoryTesterOptions.end()) {
|
|
|
|
|
+ this->MemoryTesterDynamicOptions.emplace_back("-symcache_dir");
|
|
|
|
|
+ std::string drMemoryCacheDir = tempDrMemoryDir + "/cache";
|
|
|
|
|
+ this->MemoryTesterDynamicOptions.push_back(drMemoryCacheDir);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
|
|
|
|
|
+ .empty()) {
|
|
|
|
|
+ if (!cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
|
|
|
|
|
+ "MemoryCheckSuppressionFile"))) {
|
|
|
|
|
+ cmCTestLog(this->CTest, ERROR_MESSAGE,
|
|
|
|
|
+ "Cannot find memory checker suppression file: "
|
|
|
|
|
+ << this->CTest->GetCTestConfiguration(
|
|
|
|
|
+ "MemoryCheckSuppressionFile")
|
|
|
|
|
+ << std::endl);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ this->MemoryTesterOptions.emplace_back("-suppress");
|
|
|
|
|
+ this->MemoryTesterOptions.push_back(
|
|
|
|
|
+ this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this->MemoryTesterOptions.emplace_back("--");
|
|
|
|
|
+
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
case cmCTestMemCheckHandler::PURIFY: {
|
|
case cmCTestMemCheckHandler::PURIFY: {
|
|
|
std::string outputFile;
|
|
std::string outputFile;
|
|
|
#ifdef _WIN32
|
|
#ifdef _WIN32
|
|
@@ -664,6 +761,8 @@ bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
|
|
|
switch (this->MemoryTesterStyle) {
|
|
switch (this->MemoryTesterStyle) {
|
|
|
case cmCTestMemCheckHandler::VALGRIND:
|
|
case cmCTestMemCheckHandler::VALGRIND:
|
|
|
return this->ProcessMemCheckValgrindOutput(str, log, results);
|
|
return this->ProcessMemCheckValgrindOutput(str, log, results);
|
|
|
|
|
+ case cmCTestMemCheckHandler::DRMEMORY:
|
|
|
|
|
+ return this->ProcessMemCheckDrMemoryOutput(str, log, results);
|
|
|
case cmCTestMemCheckHandler::PURIFY:
|
|
case cmCTestMemCheckHandler::PURIFY:
|
|
|
return this->ProcessMemCheckPurifyOutput(str, log, results);
|
|
return this->ProcessMemCheckPurifyOutput(str, log, results);
|
|
|
case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
|
|
case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
|
|
@@ -929,6 +1028,47 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
|
|
|
return defects == 0;
|
|
return defects == 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+bool cmCTestMemCheckHandler::ProcessMemCheckDrMemoryOutput(
|
|
|
|
|
+ const std::string& str, std::string& log, std::vector<int>& results)
|
|
|
|
|
+{
|
|
|
|
|
+ std::vector<std::string> lines;
|
|
|
|
|
+ cmsys::SystemTools::Split(str, lines);
|
|
|
|
|
+
|
|
|
|
|
+ cmsys::RegularExpression drMemoryError("^Error #[0-9]+");
|
|
|
|
|
+
|
|
|
|
|
+ cmsys::RegularExpression unaddressableAccess("UNADDRESSABLE ACCESS");
|
|
|
|
|
+ cmsys::RegularExpression uninitializedRead("UNINITIALIZED READ");
|
|
|
|
|
+ cmsys::RegularExpression invalidHeapArgument("INVALID HEAP ARGUMENT");
|
|
|
|
|
+ cmsys::RegularExpression leak("LEAK");
|
|
|
|
|
+ cmsys::RegularExpression handleLeak("HANDLE LEAK");
|
|
|
|
|
+
|
|
|
|
|
+ int defects = 0;
|
|
|
|
|
+
|
|
|
|
|
+ std::ostringstream ostr;
|
|
|
|
|
+ for (const auto& l : lines) {
|
|
|
|
|
+ ostr << l << std::endl;
|
|
|
|
|
+ if (drMemoryError.find(l)) {
|
|
|
|
|
+ defects++;
|
|
|
|
|
+ if (unaddressableAccess.find(l)) {
|
|
|
|
|
+ results[cmCTestMemCheckHandler::UMR]++;
|
|
|
|
|
+ } else if (uninitializedRead.find(l)) {
|
|
|
|
|
+ results[cmCTestMemCheckHandler::UMR]++;
|
|
|
|
|
+ } else if (leak.find(l)) {
|
|
|
|
|
+ results[cmCTestMemCheckHandler::MLK]++;
|
|
|
|
|
+ } else if (handleLeak.find(l)) {
|
|
|
|
|
+ results[cmCTestMemCheckHandler::MLK]++;
|
|
|
|
|
+ } else if (invalidHeapArgument.find(l)) {
|
|
|
|
|
+ results[cmCTestMemCheckHandler::FMM]++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log = ostr.str();
|
|
|
|
|
+
|
|
|
|
|
+ this->DefectCount += defects;
|
|
|
|
|
+ return defects == 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
|
|
bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
|
|
|
const std::string& str, std::string& log, std::vector<int>& results)
|
|
const std::string& str, std::string& log, std::vector<int>& results)
|
|
|
{
|
|
{
|
|
@@ -988,6 +1128,8 @@ void cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res, int test)
|
|
|
this->Quiet);
|
|
this->Quiet);
|
|
|
if (this->MemoryTesterStyle == cmCTestMemCheckHandler::BOUNDS_CHECKER) {
|
|
if (this->MemoryTesterStyle == cmCTestMemCheckHandler::BOUNDS_CHECKER) {
|
|
|
this->PostProcessBoundsCheckerTest(res, test);
|
|
this->PostProcessBoundsCheckerTest(res, test);
|
|
|
|
|
+ } else if (this->MemoryTesterStyle == cmCTestMemCheckHandler::DRMEMORY) {
|
|
|
|
|
+ this->PostProcessDrMemoryTest(res, test);
|
|
|
} else {
|
|
} else {
|
|
|
std::vector<std::string> files;
|
|
std::vector<std::string> files;
|
|
|
this->TestOutputFileNames(test, files);
|
|
this->TestOutputFileNames(test, files);
|
|
@@ -1042,6 +1184,37 @@ void cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(
|
|
|
this->Quiet);
|
|
this->Quiet);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+void cmCTestMemCheckHandler::PostProcessDrMemoryTest(
|
|
|
|
|
+ cmCTestTestHandler::cmCTestTestResult& res, int test)
|
|
|
|
|
+{
|
|
|
|
|
+ std::string drMemoryLogDir = this->MemoryTesterOutputFile.substr(
|
|
|
|
|
+ 0, this->MemoryTesterOutputFile.find("/*/results.txt"));
|
|
|
|
|
+
|
|
|
|
|
+ // replace placeholder of test
|
|
|
|
|
+ std::string::size_type pos = drMemoryLogDir.find("??");
|
|
|
|
|
+ if (pos != std::string::npos) {
|
|
|
|
|
+ drMemoryLogDir.replace(pos, 2, std::to_string(test));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ cmsys::Glob g;
|
|
|
|
|
+ g.FindFiles(drMemoryLogDir + "/resfile.*");
|
|
|
|
|
+ const std::vector<std::string>& files = g.GetFiles();
|
|
|
|
|
+
|
|
|
|
|
+ for (const std::string& f : files) {
|
|
|
|
|
+ cmsys::ifstream ifs(f.c_str());
|
|
|
|
|
+ if (!ifs) {
|
|
|
|
|
+ std::string log = "Cannot read memory tester output file: " + f;
|
|
|
|
|
+ cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ std::string resultFileLocation;
|
|
|
|
|
+ cmSystemTools::GetLineFromStream(ifs, resultFileLocation);
|
|
|
|
|
+ this->AppendMemTesterOutput(res, resultFileLocation);
|
|
|
|
|
+ ifs.close();
|
|
|
|
|
+ cmSystemTools::RemoveFile(f);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
void cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res,
|
|
void cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res,
|
|
|
std::string const& ofile)
|
|
std::string const& ofile)
|
|
|
{
|
|
{
|