summaryrefslogtreecommitdiff
path: root/Source/CTest/cmCTestMemCheckHandler.cxx
diff options
context:
space:
mode:
authorDietmar Scheidl <dietmar.scheidl@dynatrace.com>2019-11-19 11:44:16 +0100
committerBrad King <brad.king@kitware.com>2019-12-10 14:42:30 -0500
commit676befdf524a6eaa234103c670b9c7017726b8ac (patch)
tree15223a46ba9ca78ad8939809990285f405a45840 /Source/CTest/cmCTestMemCheckHandler.cxx
parent2db0a65f56f66454d36a35a74fde27c0833c956f (diff)
downloadcmake-676befdf524a6eaa234103c670b9c7017726b8ac.tar.gz
ctest: add support for memcheck using Dr. Memory
Fixes: #19788
Diffstat (limited to 'Source/CTest/cmCTestMemCheckHandler.cxx')
-rw-r--r--Source/CTest/cmCTestMemCheckHandler.cxx173
1 files changed, 173 insertions, 0 deletions
diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx
index 8093622cc4..c1ecaf125d 100644
--- a/Source/CTest/cmCTestMemCheckHandler.cxx
+++ b/Source/CTest/cmCTestMemCheckHandler.cxx
@@ -2,9 +2,11 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCTestMemCheckHandler.h"
+#include <algorithm>
#include <chrono>
#include <cstring>
#include <iostream>
+#include <iterator>
#include <sstream>
#include <utility>
@@ -12,6 +14,7 @@
#include "cmsys/Glob.hxx"
#include "cmsys/RegularExpression.hxx"
+#include "cmAlgorithms.h"
#include "cmCTest.h"
#include "cmDuration.h"
#include "cmSystemTools.h"
@@ -165,6 +168,10 @@ void cmCTestMemCheckHandler::GenerateTestCommand(
std::string index = std::to_string(test);
std::string memcheckcommand =
cmSystemTools::ConvertToOutputPath(this->MemoryTester);
+
+ std::vector<std::string> dirs;
+ bool nextArgIsDir = false;
+
for (std::string arg : this->MemoryTesterDynamicOptions) {
std::string::size_type pos = arg.find("??");
if (pos != std::string::npos) {
@@ -174,6 +181,16 @@ void cmCTestMemCheckHandler::GenerateTestCommand(
memcheckcommand += " \"";
memcheckcommand += arg;
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.
// This is used for memory testing programs that pass options
@@ -205,6 +222,11 @@ void cmCTestMemCheckHandler::GenerateTestCommand(
memcheckcommand += " " + memTesterEnvironmentVariable;
args.push_back(memTesterEnvironmentVariable);
}
+
+ for (std::string const& dir : dirs) {
+ cmSystemTools::MakeDirectory(dir);
+ }
+
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Memory check command: " << memcheckcommand << std::endl,
this->Quiet);
@@ -297,6 +319,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(cmXMLWriter& xml)
case cmCTestMemCheckHandler::VALGRIND:
xml.Attribute("Checker", "Valgrind");
break;
+ case cmCTestMemCheckHandler::DRMEMORY:
+ xml.Attribute("Checker", "DrMemory");
+ break;
case cmCTestMemCheckHandler::PURIFY:
xml.Attribute("Checker", "Purify");
break;
@@ -434,6 +459,10 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
if (testerName.find("valgrind") != std::string::npos ||
this->CTest->GetCTestConfiguration("MemoryCheckType") == "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) {
this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
} else if (testerName.find("BC") != std::string::npos) {
@@ -450,6 +479,10 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
this->MemoryTester = this->CTest->GetCTestConfiguration("ValgrindCommand");
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(
this->CTest->GetCTestConfiguration("BoundsCheckerCommand"))) {
this->MemoryTester =
this->CTest->GetCTestConfiguration("BoundsCheckerCommand");
@@ -495,6 +528,8 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
} else if (checkType == "Valgrind") {
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
+ } else if (checkType == "DrMemory") {
+ this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
}
}
if (this->MemoryTester.empty()) {
@@ -516,6 +551,10 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
.empty()) {
memoryTesterOptions =
this->CTest->GetCTestConfiguration("ValgrindCommandOptions");
+ } else if (!this->CTest->GetCTestConfiguration("DrMemoryCommandOptions")
+ .empty()) {
+ memoryTesterOptions =
+ this->CTest->GetCTestConfiguration("DrMemoryCommandOptions");
}
this->MemoryTesterOptions =
cmSystemTools::ParseArguments(memoryTesterOptions);
@@ -551,6 +590,64 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
this->MemoryTesterOutputFile);
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: {
std::string outputFile;
#ifdef _WIN32
@@ -664,6 +761,8 @@ bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
switch (this->MemoryTesterStyle) {
case cmCTestMemCheckHandler::VALGRIND:
return this->ProcessMemCheckValgrindOutput(str, log, results);
+ case cmCTestMemCheckHandler::DRMEMORY:
+ return this->ProcessMemCheckDrMemoryOutput(str, log, results);
case cmCTestMemCheckHandler::PURIFY:
return this->ProcessMemCheckPurifyOutput(str, log, results);
case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
@@ -929,6 +1028,47 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
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(
const std::string& str, std::string& log, std::vector<int>& results)
{
@@ -988,6 +1128,8 @@ void cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res, int test)
this->Quiet);
if (this->MemoryTesterStyle == cmCTestMemCheckHandler::BOUNDS_CHECKER) {
this->PostProcessBoundsCheckerTest(res, test);
+ } else if (this->MemoryTesterStyle == cmCTestMemCheckHandler::DRMEMORY) {
+ this->PostProcessDrMemoryTest(res, test);
} else {
std::vector<std::string> files;
this->TestOutputFileNames(test, files);
@@ -1042,6 +1184,37 @@ void cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(
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,
std::string const& ofile)
{