mirror of
https://github.com/reactos/CMake.git
synced 2024-12-04 17:56:26 +00:00
parent
2db0a65f56
commit
676befdf52
@ -1113,6 +1113,20 @@ Additional configuration settings include:
|
||||
* `CTest Script`_ variable: none
|
||||
* :module:`CTest` module variable: ``VALGRIND_COMMAND_OPTIONS``
|
||||
|
||||
``DrMemoryCommand``
|
||||
Specify a ``MemoryCheckCommand`` that is known to be a command-line
|
||||
compatible with DrMemory.
|
||||
|
||||
* `CTest Script`_ variable: none
|
||||
* :module:`CTest` module variable: ``DRMEMORY_COMMAND``
|
||||
|
||||
``DrMemoryCommandOptions``
|
||||
Specify command-line options to the ``DrMemoryCommand`` tool.
|
||||
They will be placed prior to the test command line.
|
||||
|
||||
* `CTest Script`_ variable: none
|
||||
* :module:`CTest` module variable: ``DRMEMORY_COMMAND_OPTIONS``
|
||||
|
||||
.. _`CTest Submit Step`:
|
||||
|
||||
CTest Submit Step
|
||||
|
5
Help/release/dev/ctest-drmemory-support.rst
Normal file
5
Help/release/dev/ctest-drmemory-support.rst
Normal file
@ -0,0 +1,5 @@
|
||||
ctest-drmemory-support
|
||||
----------------------
|
||||
|
||||
* The :manual:`ctest(1)` gained support for Dr. Memory to run
|
||||
memcheck runs.
|
@ -3,6 +3,6 @@ CTEST_MEMORYCHECK_TYPE
|
||||
|
||||
Specify the CTest ``MemoryCheckType`` setting
|
||||
in a :manual:`ctest(1)` dashboard client script.
|
||||
Valid values are ``Valgrind``, ``Purify``, ``BoundsChecker``, and
|
||||
Valid values are ``Valgrind``, ``Purify``, ``BoundsChecker``, ``DrMemory`` and
|
||||
``ThreadSanitizer``, ``AddressSanitizer``, ``LeakSanitizer``, ``MemorySanitizer``, and
|
||||
``UndefinedBehaviorSanitizer``.
|
||||
|
@ -174,7 +174,7 @@ if(BUILD_TESTING)
|
||||
"How many times to retry timed-out CTest submissions.")
|
||||
|
||||
find_program(MEMORYCHECK_COMMAND
|
||||
NAMES purify valgrind boundscheck
|
||||
NAMES purify valgrind boundscheck drmemory
|
||||
PATHS
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Rational Software\\Purify\\Setup;InstallFolder]"
|
||||
DOC "Path to the memory checking command, used for memory error detection."
|
||||
|
@ -69,6 +69,8 @@ CompilerVersion: @CMAKE_CXX_COMPILER_VERSION@
|
||||
PurifyCommand: @PURIFYCOMMAND@
|
||||
ValgrindCommand: @VALGRIND_COMMAND@
|
||||
ValgrindCommandOptions: @VALGRIND_COMMAND_OPTIONS@
|
||||
DrMemoryCommand: @DRMEMORY_COMMAND@
|
||||
DrMemoryCommandOptions: @DRMEMORY_COMMAND_OPTIONS@
|
||||
MemoryCheckType: @MEMORYCHECK_TYPE@
|
||||
MemoryCheckSanitizerOptions: @MEMORYCHECK_SANITIZER_OPTIONS@
|
||||
MemoryCheckCommand: @MEMORYCHECK_COMMAND@
|
||||
|
@ -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) {
|
||||
@ -449,6 +478,10 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
|
||||
this->CTest->GetCTestConfiguration("ValgrindCommand"))) {
|
||||
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 =
|
||||
@ -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)
|
||||
{
|
||||
|
@ -43,6 +43,7 @@ private:
|
||||
UNKNOWN = 0,
|
||||
VALGRIND,
|
||||
PURIFY,
|
||||
DRMEMORY,
|
||||
BOUNDS_CHECKER,
|
||||
// checkers after here do not use the standard error list
|
||||
ADDRESS_SANITIZER,
|
||||
@ -132,6 +133,8 @@ private:
|
||||
std::vector<int>& results);
|
||||
bool ProcessMemCheckValgrindOutput(const std::string& str, std::string& log,
|
||||
std::vector<int>& results);
|
||||
bool ProcessMemCheckDrMemoryOutput(const std::string& str, std::string& log,
|
||||
std::vector<int>& results);
|
||||
bool ProcessMemCheckPurifyOutput(const std::string& str, std::string& log,
|
||||
std::vector<int>& results);
|
||||
bool ProcessMemCheckSanitizerOutput(const std::string& str, std::string& log,
|
||||
@ -142,6 +145,7 @@ private:
|
||||
|
||||
void PostProcessTest(cmCTestTestResult& res, int test);
|
||||
void PostProcessBoundsCheckerTest(cmCTestTestResult& res, int test);
|
||||
void PostProcessDrMemoryTest(cmCTestTestResult& res, int test);
|
||||
|
||||
//! append MemoryTesterOutputFile to the test log
|
||||
void AppendMemTesterOutput(cmCTestTestHandler::cmCTestTestResult& res,
|
||||
|
Loading…
Reference in New Issue
Block a user