CTest: add cuda-memcheck support

This commit is contained in:
Tobias Ribizel 2020-06-29 16:04:54 +02:00
parent f2b84d24cf
commit fe062800f0
No known key found for this signature in database
GPG Key ID: CEAFBE6D1C2DB92D
2 changed files with 145 additions and 0 deletions

View File

@ -326,6 +326,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(cmXMLWriter& xml)
case cmCTestMemCheckHandler::BOUNDS_CHECKER:
xml.Attribute("Checker", "BoundsChecker");
break;
case cmCTestMemCheckHandler::CUDA_MEMCHECK:
xml.Attribute("Checker", "CudaMemcheck");
break;
case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
xml.Attribute("Checker", "AddressSanitizer");
break;
@ -465,6 +468,8 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
} else if (testerName.find("BC") != std::string::npos) {
this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
} else if (testerName.find("cuda-memcheck") != std::string::npos) {
this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_MEMCHECK;
} else {
this->MemoryTesterStyle = cmCTestMemCheckHandler::UNKNOWN;
}
@ -485,6 +490,11 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
this->MemoryTester =
this->CTest->GetCTestConfiguration("BoundsCheckerCommand");
this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
} else if (cmSystemTools::FileExists(
this->CTest->GetCTestConfiguration("CudaMemcheckCommand"))) {
this->MemoryTester =
this->CTest->GetCTestConfiguration("CudaMemcheckCommand");
this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_MEMCHECK;
}
if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
"AddressSanitizer") {
@ -528,6 +538,8 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
} else if (checkType == "DrMemory") {
this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
} else if (checkType == "CudaMemcheck") {
this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_MEMCHECK;
}
}
if (this->MemoryTester.empty()) {
@ -553,6 +565,10 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
.empty()) {
memoryTesterOptions =
this->CTest->GetCTestConfiguration("DrMemoryCommandOptions");
} else if (!this->CTest->GetCTestConfiguration("CudaMemcheckCommandOptions")
.empty()) {
memoryTesterOptions =
this->CTest->GetCTestConfiguration("CudaMemcheckCommandOptions");
}
this->MemoryTesterOptions =
cmSystemTools::ParseArguments(memoryTesterOptions);
@ -686,6 +702,18 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
this->MemoryTesterOptions.emplace_back("/M");
break;
}
case cmCTestMemCheckHandler::CUDA_MEMCHECK: {
// cuda-memcheck separates flags from arguments by spaces
if (this->MemoryTesterOptions.empty()) {
this->MemoryTesterOptions.emplace_back("--tool");
this->MemoryTesterOptions.emplace_back("memcheck");
this->MemoryTesterOptions.emplace_back("--leak-check");
this->MemoryTesterOptions.emplace_back("full");
}
this->MemoryTesterDynamicOptions.emplace_back("--log-file");
this->MemoryTesterDynamicOptions.push_back(this->MemoryTesterOutputFile);
break;
}
// these are almost the same but the env var used is different
case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
case cmCTestMemCheckHandler::LEAK_SANITIZER:
@ -771,6 +799,8 @@ bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
return this->ProcessMemCheckSanitizerOutput(str, log, results);
case cmCTestMemCheckHandler::BOUNDS_CHECKER:
return this->ProcessMemCheckBoundsCheckerOutput(str, log, results);
case cmCTestMemCheckHandler::CUDA_MEMCHECK:
return this->ProcessMemCheckCudaOutput(str, log, results);
default:
log.append("\nMemory checking style used was: ");
log.append("None that I know");
@ -1103,6 +1133,118 @@ bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
return defects == 0;
}
bool cmCTestMemCheckHandler::ProcessMemCheckCudaOutput(
const std::string& str, std::string& log, std::vector<int>& results)
{
std::vector<std::string> lines;
cmsys::SystemTools::Split(str, lines);
bool unlimitedOutput = false;
if (str.find("CTEST_FULL_OUTPUT") != std::string::npos ||
this->CustomMaximumFailedTestOutputSize == 0) {
unlimitedOutput = true;
}
std::string::size_type cc;
std::ostringstream ostr;
log.clear();
int defects = 0;
cmsys::RegularExpression memcheckLine("^========");
cmsys::RegularExpression leakExpr("== Leaked [0-9,]+ bytes at");
// list of matchers for output messages that contain variable content
// (addresses, sizes, ...) or can be shortened in general. the first match is
// used as a error name.
std::vector<cmsys::RegularExpression> matchers{
// API errors
"== Malloc/Free error encountered: (.*)",
"== Program hit error ([^ ]*).* on CUDA API call to",
"== Program hit ([^ ]*).* on CUDA API call to",
// memcheck
"== (Invalid .*) of size [0-9,]+",
// racecheck
"== .* (Potential .* hazard detected)", "== .* (Race reported)",
// synccheck
"== (Barrier error)",
// initcheck
"== (Uninitialized .* memory read)", "== (Unused memory)",
// generic error: ignore ERROR SUMMARY, CUDA-MEMCHECK and others
"== ([A-Z][a-z].*)"
};
std::vector<std::string::size_type> nonMemcheckOutput;
auto sttime = std::chrono::steady_clock::now();
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 (memcheckLine.find(lines[cc])) {
cmCTestOptionalLog(this->CTest, DEBUG,
"cuda-memcheck line " << lines[cc] << std::endl,
this->Quiet);
int failure = -1;
auto& line = lines[cc];
if (leakExpr.find(line)) {
failure = static_cast<int>(this->FindOrAddWarning("Memory leak"));
} else {
for (auto& matcher : matchers) {
if (matcher.find(line)) {
failure =
static_cast<int>(this->FindOrAddWarning(matcher.match(1)));
break;
}
}
}
if (failure >= 0) {
ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
if (results.empty() || unsigned(failure) > results.size() - 1) {
results.push_back(1);
} else {
results[failure]++;
}
defects++;
}
totalOutputSize += lines[cc].size();
ostr << lines[cc] << std::endl;
} else {
nonMemcheckOutput.push_back(cc);
}
}
// Now put all all the non cuda-memcheck output into the test output
// This should be last in case it gets truncated by the output
// limiting code
for (std::string::size_type i : nonMemcheckOutput) {
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
}
}
cmCTestOptionalLog(this->CTest, DEBUG,
"End test (elapsed: "
<< cmDurationTo<unsigned int>(
std::chrono::steady_clock::now() - sttime)
<< "s)" << std::endl,
this->Quiet);
log = ostr.str();
this->DefectCount += defects;
return defects == 0;
}
// PostProcessTest memcheck results
void cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res, int test)
{

View File

@ -46,6 +46,7 @@ private:
DRMEMORY,
BOUNDS_CHECKER,
// checkers after here do not use the standard error list
CUDA_MEMCHECK,
ADDRESS_SANITIZER,
LEAK_SANITIZER,
THREAD_SANITIZER,
@ -137,6 +138,8 @@ private:
std::vector<int>& results);
bool ProcessMemCheckPurifyOutput(const std::string& str, std::string& log,
std::vector<int>& results);
bool ProcessMemCheckCudaOutput(const std::string& str, std::string& log,
std::vector<int>& results);
bool ProcessMemCheckSanitizerOutput(const std::string& str, std::string& log,
std::vector<int>& results);
bool ProcessMemCheckBoundsCheckerOutput(const std::string& str,