1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCTestMemCheckHandler.h"
13 #include <cmext/algorithm>
15 #include "cmsys/FStream.hxx"
16 #include "cmsys/Glob.hxx"
17 #include "cmsys/RegularExpression.hxx"
20 #include "cmDuration.h"
21 #include "cmSystemTools.h"
22 #include "cmXMLParser.h"
23 #include "cmXMLWriter.h"
27 const char* ErrorCategory;
31 static CatToErrorType cmCTestMemCheckBoundsChecker[] = {
33 { "Write Overrun", cmCTestMemCheckHandler::ABW },
34 { "Read Overrun", cmCTestMemCheckHandler::ABR },
35 { "Memory Overrun", cmCTestMemCheckHandler::ABW },
36 { "Allocation Conflict", cmCTestMemCheckHandler::FMM },
37 { "Bad Pointer Use", cmCTestMemCheckHandler::FMW },
38 { "Dangling Pointer", cmCTestMemCheckHandler::FMR },
42 static void xmlReportError(int line, const char* msg, void* data)
44 cmCTest* ctest = static_cast<cmCTest*>(data);
45 cmCTestLog(ctest, ERROR_MESSAGE,
46 "Error parsing XML in stream at line " << line << ": " << msg
50 // parse the xml file containing the results of last BoundsChecker run
51 class cmBoundsCheckerParser : public cmXMLParser
54 cmBoundsCheckerParser(cmCTest* c)
57 this->SetErrorCallback(xmlReportError, c);
59 void StartElement(const std::string& name, const char** atts) override
61 if (name == "MemoryLeak" || name == "ResourceLeak") {
62 this->Errors.push_back(cmCTestMemCheckHandler::MLK);
63 } else if (name == "Error" || name == "Dangling Pointer") {
64 this->ParseError(atts);
67 std::ostringstream ostr;
68 ostr << name << ":\n";
70 for (; atts[i] != nullptr; i += 2) {
71 ostr << " " << atts[i] << " - " << atts[i + 1] << "\n";
74 this->Log += ostr.str();
76 void EndElement(const std::string& /*name*/) override {}
78 const char* GetAttribute(const char* name, const char** atts)
81 for (; atts[i] != nullptr; ++i) {
82 if (strcmp(name, atts[i]) == 0) {
88 void ParseError(const char** atts)
90 CatToErrorType* ptr = cmCTestMemCheckBoundsChecker;
91 const char* cat = this->GetAttribute("ErrorCategory", atts);
93 this->Errors.push_back(cmCTestMemCheckHandler::ABW); // do not know
94 cmCTestLog(this->CTest, ERROR_MESSAGE,
95 "No Category found in Bounds checker XML\n");
98 while (ptr->ErrorCategory && cat) {
99 if (strcmp(ptr->ErrorCategory, cat) == 0) {
100 this->Errors.push_back(ptr->ErrorCode);
101 return; // found it we are done
105 if (ptr->ErrorCategory) {
106 this->Errors.push_back(cmCTestMemCheckHandler::ABW); // do not know
107 cmCTestLog(this->CTest, ERROR_MESSAGE,
108 "Found unknown Bounds Checker error " << ptr->ErrorCategory
113 std::vector<int> Errors;
117 #define BOUNDS_CHECKER_MARKER \
118 "******######*****Begin BOUNDS CHECKER XML******######******"
120 cmCTestMemCheckHandler::cmCTestMemCheckHandler()
122 this->MemCheck = true;
123 this->CustomMaximumPassedTestOutputSize = 0;
124 this->CustomMaximumFailedTestOutputSize = 0;
125 this->LogWithPID = false;
128 void cmCTestMemCheckHandler::Initialize()
130 this->Superclass::Initialize();
131 this->LogWithPID = false;
132 this->CustomMaximumPassedTestOutputSize = 0;
133 this->CustomMaximumFailedTestOutputSize = 0;
134 this->MemoryTester.clear();
135 this->MemoryTesterDynamicOptions.clear();
136 this->MemoryTesterOptions.clear();
137 this->MemoryTesterStyle = UNKNOWN;
138 this->MemoryTesterOutputFile.clear();
139 this->DefectCount = 0;
142 int cmCTestMemCheckHandler::PreProcessHandler()
144 if (!this->InitializeMemoryChecking()) {
148 if (!this->ExecuteCommands(this->CustomPreMemCheck)) {
149 cmCTestLog(this->CTest, ERROR_MESSAGE,
150 "Problem executing pre-memcheck command(s)." << std::endl);
156 int cmCTestMemCheckHandler::PostProcessHandler()
158 if (!this->ExecuteCommands(this->CustomPostMemCheck)) {
159 cmCTestLog(this->CTest, ERROR_MESSAGE,
160 "Problem executing post-memcheck command(s)." << std::endl);
166 void cmCTestMemCheckHandler::GenerateTestCommand(
167 std::vector<std::string>& args, int test)
169 std::string index = std::to_string(test);
170 std::string memcheckcommand =
171 cmSystemTools::ConvertToOutputPath(this->MemoryTester);
173 std::vector<std::string> dirs;
174 bool nextArgIsDir = false;
176 for (std::string arg : this->MemoryTesterDynamicOptions) {
177 std::string::size_type pos = arg.find("??");
178 if (pos != std::string::npos) {
179 arg.replace(pos, 2, index);
182 memcheckcommand += " \"";
183 memcheckcommand += arg;
184 memcheckcommand += "\"";
187 nextArgIsDir = false;
191 if (this->MemoryTesterStyle == cmCTestMemCheckHandler::DRMEMORY &&
192 (arg == "-logdir" || arg == "-symcache_dir")) {
196 // Create a copy of the memory tester environment variable.
197 // This is used for memory testing programs that pass options
198 // via environment variables.
199 std::string memTesterEnvironmentVariable =
200 this->MemoryTesterEnvironmentVariable;
201 for (std::string const& arg : this->MemoryTesterOptions) {
202 if (!memTesterEnvironmentVariable.empty()) {
203 // If we are using env to pass options, append all the options to
204 // this string with space separation.
205 memTesterEnvironmentVariable += " " + arg;
207 // for regular options just add them to args and memcheckcommand
208 // which is just used for display
211 memcheckcommand += " \"";
212 memcheckcommand += arg;
213 memcheckcommand += "\"";
216 // if this is an env option type, then add the env string as a single
218 if (!memTesterEnvironmentVariable.empty()) {
219 std::string::size_type pos = memTesterEnvironmentVariable.find("??");
220 if (pos != std::string::npos) {
221 memTesterEnvironmentVariable.replace(pos, 2, index);
223 memcheckcommand += " " + memTesterEnvironmentVariable;
224 args.push_back(memTesterEnvironmentVariable);
227 for (std::string const& dir : dirs) {
228 cmSystemTools::MakeDirectory(dir);
231 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
232 "Memory check command: " << memcheckcommand << std::endl,
236 void cmCTestMemCheckHandler::InitializeResultsVectors()
238 // fill these members
239 // cmsys::vector<std::string> ResultStrings;
240 // cmsys::vector<std::string> ResultStringsLong;
241 // cmsys::vector<int> GlobalResults;
242 this->ResultStringsLong.clear();
243 this->ResultStrings.clear();
244 this->GlobalResults.clear();
245 // If we are working with style checkers that dynamically fill
246 // the results strings then return.
247 if (this->MemoryTesterStyle > cmCTestMemCheckHandler::BOUNDS_CHECKER) {
251 // define the standard set of errors
252 //----------------------------------------------------------------------
253 static const char* cmCTestMemCheckResultStrings[] = {
254 "ABR", "ABW", "ABWL", "COR", "EXU", "FFM", "FIM", "FMM",
255 "FMR", "FMW", "FUM", "IPR", "IPW", "MAF", "MLK", "MPK",
256 "NPR", "ODS", "PAR", "PLK", "UMC", "UMR", nullptr
258 static const char* cmCTestMemCheckResultLongStrings[] = {
266 "Mismatched deallocation",
274 "Potential Memory Leak",
277 "Invalid syscall param",
279 "Uninitialized Memory Conditional",
280 "Uninitialized Memory Read",
283 this->GlobalResults.clear();
284 for (int i = 0; cmCTestMemCheckResultStrings[i] != nullptr; ++i) {
285 this->ResultStrings.emplace_back(cmCTestMemCheckResultStrings[i]);
286 this->ResultStringsLong.emplace_back(cmCTestMemCheckResultLongStrings[i]);
287 this->GlobalResults.push_back(0);
291 void cmCTestMemCheckHandler::PopulateCustomVectors(cmMakefile* mf)
293 this->cmCTestTestHandler::PopulateCustomVectors(mf);
294 this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_PRE_MEMCHECK",
295 this->CustomPreMemCheck);
296 this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_POST_MEMCHECK",
297 this->CustomPostMemCheck);
299 this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_MEMCHECK_IGNORE",
300 this->CustomTestsIgnore);
303 int cmCTestMemCheckHandler::GetDefectCount() const
305 return this->DefectCount;
308 void cmCTestMemCheckHandler::GenerateCTestXML(cmXMLWriter& xml)
310 if (!this->CTest->GetProduceXML()) {
313 this->CTest->StartXML(xml, this->AppendXML);
314 this->CTest->GenerateSubprojectsOutput(xml);
315 xml.StartElement("DynamicAnalysis");
316 switch (this->MemoryTesterStyle) {
317 case cmCTestMemCheckHandler::VALGRIND:
318 xml.Attribute("Checker", "Valgrind");
320 case cmCTestMemCheckHandler::DRMEMORY:
321 xml.Attribute("Checker", "DrMemory");
323 case cmCTestMemCheckHandler::PURIFY:
324 xml.Attribute("Checker", "Purify");
326 case cmCTestMemCheckHandler::BOUNDS_CHECKER:
327 xml.Attribute("Checker", "BoundsChecker");
329 case cmCTestMemCheckHandler::CUDA_SANITIZER:
330 xml.Attribute("Checker", "CudaSanitizer");
332 case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
333 xml.Attribute("Checker", "AddressSanitizer");
335 case cmCTestMemCheckHandler::LEAK_SANITIZER:
336 xml.Attribute("Checker", "LeakSanitizer");
338 case cmCTestMemCheckHandler::THREAD_SANITIZER:
339 xml.Attribute("Checker", "ThreadSanitizer");
341 case cmCTestMemCheckHandler::MEMORY_SANITIZER:
342 xml.Attribute("Checker", "MemorySanitizer");
344 case cmCTestMemCheckHandler::UB_SANITIZER:
345 xml.Attribute("Checker", "UndefinedBehaviorSanitizer");
348 xml.Attribute("Checker", "Unknown");
351 xml.Element("StartDateTime", this->StartTest);
352 xml.Element("StartTestTime", this->StartTestTime);
353 xml.StartElement("TestList");
354 cmCTestMemCheckHandler::TestResultsVector::size_type cc;
355 for (cmCTestTestResult const& result : this->TestResults) {
356 std::string testPath = result.Path + "/" + result.Name;
357 xml.Element("Test", this->CTest->GetShortPathToFile(testPath));
359 xml.EndElement(); // TestList
360 cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
361 "-- Processing memory checking output:\n", this->Quiet);
362 size_t total = this->TestResults.size();
363 for (cc = 0; cc < this->TestResults.size(); cc++) {
364 cmCTestTestResult const& result = this->TestResults[cc];
365 std::string memcheckstr;
366 std::vector<int> memcheckresults(this->ResultStrings.size(), 0);
368 this->ProcessMemCheckOutput(result.Output, memcheckstr, memcheckresults);
369 if (res && result.Status == cmCTestMemCheckHandler::COMPLETED) {
372 this->CleanTestOutput(
374 static_cast<size_t>(this->CustomMaximumFailedTestOutputSize),
375 this->TestOutputTruncation);
376 this->WriteTestResultHeader(xml, result);
377 xml.StartElement("Results");
378 int memoryErrors = 0;
379 for (std::vector<int>::size_type kk = 0; kk < memcheckresults.size();
381 if (memcheckresults[kk]) {
382 xml.StartElement("Defect");
383 xml.Attribute("type", this->ResultStringsLong[kk]);
384 xml.Content(memcheckresults[kk]);
385 memoryErrors += memcheckresults[kk];
386 xml.EndElement(); // Defect
388 this->GlobalResults[kk] += memcheckresults[kk];
390 xml.EndElement(); // Results
391 if (memoryErrors > 0) {
392 const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
393 std::string outname = result.Name + " ";
394 outname.resize(maxTestNameWidth + 4, '.');
395 cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
396 cc + 1 << "/" << total << " MemCheck: #"
397 << result.TestCount << ": " << outname
398 << " Defects: " << memoryErrors << std::endl,
401 xml.StartElement("Log");
402 if (this->CTest->ShouldCompressTestOutput()) {
403 this->CTest->CompressString(memcheckstr);
404 xml.Attribute("compression", "gzip");
405 xml.Attribute("encoding", "base64");
407 xml.Content(memcheckstr);
408 xml.EndElement(); // Log
410 this->WriteTestResultFooter(xml, result);
412 cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
413 "MemCheck log files can be found here: "
414 "(<#> corresponds to test number)"
417 std::string output = this->MemoryTesterOutputFile;
418 cmSystemTools::ReplaceString(output, "??", "<#>");
419 cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, output << std::endl,
421 cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
422 "Memory checking results:" << std::endl, this->Quiet);
423 xml.StartElement("DefectList");
424 for (cc = 0; cc < this->GlobalResults.size(); cc++) {
425 if (this->GlobalResults[cc]) {
427 cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
428 this->ResultStringsLong[cc]
429 << " - " << this->GlobalResults[cc] << std::endl,
431 xml.StartElement("Defect");
432 xml.Attribute("Type", this->ResultStringsLong[cc]);
436 xml.EndElement(); // DefectList
438 xml.Element("EndDateTime", this->EndTest);
439 xml.Element("EndTestTime", this->EndTestTime);
442 std::chrono::duration_cast<std::chrono::minutes>(this->ElapsedTestingTime)
445 xml.EndElement(); // DynamicAnalysis
446 this->CTest->EndXML(xml);
449 bool cmCTestMemCheckHandler::InitializeMemoryChecking()
451 this->MemoryTesterEnvironmentVariable.clear();
452 this->MemoryTester.clear();
454 if (cmSystemTools::FileExists(
455 this->CTest->GetCTestConfiguration("MemoryCheckCommand"))) {
457 this->CTest->GetCTestConfiguration("MemoryCheckCommand");
458 std::string testerName =
459 cmSystemTools::GetFilenameName(this->MemoryTester);
460 // determine the checker type
461 if (testerName.find("valgrind") != std::string::npos ||
462 this->CTest->GetCTestConfiguration("MemoryCheckType") == "Valgrind") {
463 this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
464 } else if (testerName.find("drmemory") != std::string::npos ||
465 this->CTest->GetCTestConfiguration("MemoryCheckType") ==
467 this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
468 } else if (testerName.find("purify") != std::string::npos) {
469 this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
470 } else if (testerName.find("BC") != std::string::npos) {
471 this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
472 } else if (testerName.find("cuda-memcheck") != std::string::npos ||
473 testerName.find("compute-sanitizer") != std::string::npos) {
474 this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_SANITIZER;
476 this->MemoryTesterStyle = cmCTestMemCheckHandler::UNKNOWN;
478 } else if (cmSystemTools::FileExists(
479 this->CTest->GetCTestConfiguration("PurifyCommand"))) {
480 this->MemoryTester = this->CTest->GetCTestConfiguration("PurifyCommand");
481 this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
482 } else if (cmSystemTools::FileExists(
483 this->CTest->GetCTestConfiguration("ValgrindCommand"))) {
484 this->MemoryTester = this->CTest->GetCTestConfiguration("ValgrindCommand");
485 this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
486 } else if (cmSystemTools::FileExists(
487 this->CTest->GetCTestConfiguration("DrMemoryCommand"))) {
488 this->MemoryTester = this->CTest->GetCTestConfiguration("DrMemoryCommand");
489 this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
490 } else if (cmSystemTools::FileExists(
491 this->CTest->GetCTestConfiguration("BoundsCheckerCommand"))) {
493 this->CTest->GetCTestConfiguration("BoundsCheckerCommand");
494 this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
495 } else if (cmSystemTools::FileExists(
496 this->CTest->GetCTestConfiguration("CudaSanitizerCommand"))) {
498 this->CTest->GetCTestConfiguration("CudaSanitizerCommand");
499 this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_SANITIZER;
501 if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
502 "AddressSanitizer") {
503 this->MemoryTester = cmSystemTools::GetCMakeCommand();
504 this->MemoryTesterStyle = cmCTestMemCheckHandler::ADDRESS_SANITIZER;
505 this->LogWithPID = true; // even if we give the log file the pid is added
507 if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
509 this->MemoryTester = cmSystemTools::GetCMakeCommand();
510 this->MemoryTesterStyle = cmCTestMemCheckHandler::LEAK_SANITIZER;
511 this->LogWithPID = true; // even if we give the log file the pid is added
513 if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
515 this->MemoryTester = cmSystemTools::GetCMakeCommand();
516 this->MemoryTesterStyle = cmCTestMemCheckHandler::THREAD_SANITIZER;
517 this->LogWithPID = true; // even if we give the log file the pid is added
519 if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
521 this->MemoryTester = cmSystemTools::GetCMakeCommand();
522 this->MemoryTesterStyle = cmCTestMemCheckHandler::MEMORY_SANITIZER;
523 this->LogWithPID = true; // even if we give the log file the pid is added
525 if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
526 "UndefinedBehaviorSanitizer") {
527 this->MemoryTester = cmSystemTools::GetCMakeCommand();
528 this->MemoryTesterStyle = cmCTestMemCheckHandler::UB_SANITIZER;
529 this->LogWithPID = true; // even if we give the log file the pid is added
531 // Check the MemoryCheckType
532 if (this->MemoryTesterStyle == cmCTestMemCheckHandler::UNKNOWN) {
533 std::string checkType =
534 this->CTest->GetCTestConfiguration("MemoryCheckType");
535 if (checkType == "Purify") {
536 this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
537 } else if (checkType == "BoundsChecker") {
538 this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
539 } else if (checkType == "Valgrind") {
540 this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
541 } else if (checkType == "DrMemory") {
542 this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY;
543 } else if (checkType == "CudaSanitizer") {
544 this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_SANITIZER;
547 if (this->MemoryTester.empty()) {
548 cmCTestOptionalLog(this->CTest, WARNING,
549 "Memory checker (MemoryCheckCommand) "
550 "not set, or cannot find the specified program."
557 std::string memoryTesterOptions;
558 if (!this->CTest->GetCTestConfiguration("MemoryCheckCommandOptions")
560 memoryTesterOptions =
561 this->CTest->GetCTestConfiguration("MemoryCheckCommandOptions");
562 } else if (!this->CTest->GetCTestConfiguration("ValgrindCommandOptions")
564 memoryTesterOptions =
565 this->CTest->GetCTestConfiguration("ValgrindCommandOptions");
566 } else if (!this->CTest->GetCTestConfiguration("DrMemoryCommandOptions")
568 memoryTesterOptions =
569 this->CTest->GetCTestConfiguration("DrMemoryCommandOptions");
570 } else if (!this->CTest->GetCTestConfiguration("CudaSanitizerCommandOptions")
572 memoryTesterOptions =
573 this->CTest->GetCTestConfiguration("CudaSanitizerCommandOptions");
575 this->MemoryTesterOptions =
576 cmSystemTools::ParseArguments(memoryTesterOptions);
578 this->MemoryTesterOutputFile =
579 this->CTest->GetBinaryDir() + "/Testing/Temporary/MemoryChecker.??.log";
581 switch (this->MemoryTesterStyle) {
582 case cmCTestMemCheckHandler::VALGRIND: {
583 if (this->MemoryTesterOptions.empty()) {
584 this->MemoryTesterOptions.emplace_back("-q");
585 this->MemoryTesterOptions.emplace_back("--tool=memcheck");
586 this->MemoryTesterOptions.emplace_back("--leak-check=yes");
587 this->MemoryTesterOptions.emplace_back("--show-reachable=yes");
588 this->MemoryTesterOptions.emplace_back("--num-callers=50");
590 if (!this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
592 if (!cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
593 "MemoryCheckSuppressionFile"))) {
594 cmCTestLog(this->CTest, ERROR_MESSAGE,
595 "Cannot find memory checker suppression file: "
596 << this->CTest->GetCTestConfiguration(
597 "MemoryCheckSuppressionFile")
601 this->MemoryTesterOptions.push_back(
603 this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile"));
605 this->MemoryTesterDynamicOptions.push_back("--log-file=" +
606 this->MemoryTesterOutputFile);
609 case cmCTestMemCheckHandler::DRMEMORY: {
610 std::string tempDrMemoryDir =
611 this->CTest->GetBinaryDir() + "/Testing/Temporary/DrMemory";
613 if (!cm::contains(this->MemoryTesterOptions, "-quiet")) {
614 this->MemoryTesterOptions.emplace_back("-quiet");
617 if (!cm::contains(this->MemoryTesterOptions, "-batch")) {
618 this->MemoryTesterOptions.emplace_back("-batch");
621 this->MemoryTesterDynamicOptions.emplace_back("-logdir");
623 std::find(this->MemoryTesterOptions.begin(),
624 this->MemoryTesterOptions.end(), "-logdir");
625 if (logdirOption == this->MemoryTesterOptions.end()) {
626 // No logdir found in memory tester options
627 std::string drMemoryLogDir = tempDrMemoryDir + "/??";
628 this->MemoryTesterDynamicOptions.push_back(drMemoryLogDir);
629 this->MemoryTesterOutputFile = drMemoryLogDir;
631 // Use logdir found in memory tester options
632 auto logdirLocation = std::next(logdirOption);
633 this->MemoryTesterOutputFile = *logdirLocation;
634 this->MemoryTesterDynamicOptions.push_back(*logdirLocation);
635 this->MemoryTesterOptions.erase(logdirOption, logdirLocation + 1);
637 this->MemoryTesterOutputFile += "/*/results.txt";
639 if (std::find(this->MemoryTesterOptions.begin(),
640 this->MemoryTesterOptions.end(),
641 "-symcache_dir") == this->MemoryTesterOptions.end()) {
642 this->MemoryTesterDynamicOptions.emplace_back("-symcache_dir");
643 std::string drMemoryCacheDir = tempDrMemoryDir + "/cache";
644 this->MemoryTesterDynamicOptions.push_back(drMemoryCacheDir);
647 if (!this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
649 if (!cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
650 "MemoryCheckSuppressionFile"))) {
651 cmCTestLog(this->CTest, ERROR_MESSAGE,
652 "Cannot find memory checker suppression file: "
653 << this->CTest->GetCTestConfiguration(
654 "MemoryCheckSuppressionFile")
658 this->MemoryTesterOptions.emplace_back("-suppress");
659 this->MemoryTesterOptions.push_back(
660 this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile"));
663 this->MemoryTesterOptions.emplace_back("--");
667 case cmCTestMemCheckHandler::PURIFY: {
668 std::string outputFile;
670 if (this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
672 if (!cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
673 "MemoryCheckSuppressionFile"))) {
675 this->CTest, ERROR_MESSAGE,
676 "Cannot find memory checker suppression file: "
678 ->GetCTestConfiguration("MemoryCheckSuppressionFile")
683 std::string filterFiles = "/FilterFiles=" +
684 this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile");
685 this->MemoryTesterOptions.push_back(filterFiles);
687 outputFile = "/SAVETEXTDATA=";
689 outputFile = "-log-file=";
691 outputFile += this->MemoryTesterOutputFile;
692 this->MemoryTesterDynamicOptions.push_back(outputFile);
695 case cmCTestMemCheckHandler::BOUNDS_CHECKER: {
696 this->BoundsCheckerXMLFile = this->MemoryTesterOutputFile;
697 std::string dpbdFile = this->CTest->GetBinaryDir() +
698 "/Testing/Temporary/MemoryChecker.??.DPbd";
699 this->BoundsCheckerDPBDFile = dpbdFile;
700 this->MemoryTesterDynamicOptions.emplace_back("/B");
701 this->MemoryTesterDynamicOptions.push_back(std::move(dpbdFile));
702 this->MemoryTesterDynamicOptions.emplace_back("/X");
703 this->MemoryTesterDynamicOptions.push_back(this->MemoryTesterOutputFile);
704 this->MemoryTesterOptions.emplace_back("/M");
707 case cmCTestMemCheckHandler::CUDA_SANITIZER: {
708 // cuda sanitizer separates flags from arguments by spaces
709 if (this->MemoryTesterOptions.empty()) {
710 this->MemoryTesterOptions.emplace_back("--tool");
711 this->MemoryTesterOptions.emplace_back("memcheck");
712 this->MemoryTesterOptions.emplace_back("--leak-check");
713 this->MemoryTesterOptions.emplace_back("full");
715 this->MemoryTesterDynamicOptions.emplace_back("--log-file");
716 this->MemoryTesterDynamicOptions.push_back(this->MemoryTesterOutputFile);
719 // these are almost the same but the env var used is different
720 case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
721 case cmCTestMemCheckHandler::LEAK_SANITIZER:
722 case cmCTestMemCheckHandler::THREAD_SANITIZER:
723 case cmCTestMemCheckHandler::MEMORY_SANITIZER:
724 case cmCTestMemCheckHandler::UB_SANITIZER: {
725 // To pass arguments to ThreadSanitizer the environment variable
726 // TSAN_OPTIONS is used. This is done with the cmake -E env command.
727 // The MemoryTesterDynamicOptions is setup with the -E env
728 // Then the MemoryTesterEnvironmentVariable gets the
729 // TSAN_OPTIONS string with the log_path in it.
730 this->MemoryTesterDynamicOptions.emplace_back("-E");
731 this->MemoryTesterDynamicOptions.emplace_back("env");
733 std::string extraOptions;
734 std::string suppressionsOption;
735 if (!this->CTest->GetCTestConfiguration("MemoryCheckSanitizerOptions")
738 this->CTest->GetCTestConfiguration("MemoryCheckSanitizerOptions");
740 if (!this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
742 suppressionsOption = ":suppressions=" +
743 this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile");
745 if (this->MemoryTesterStyle ==
746 cmCTestMemCheckHandler::ADDRESS_SANITIZER) {
747 envVar = "ASAN_OPTIONS";
748 } else if (this->MemoryTesterStyle ==
749 cmCTestMemCheckHandler::LEAK_SANITIZER) {
750 envVar = "LSAN_OPTIONS";
751 } else if (this->MemoryTesterStyle ==
752 cmCTestMemCheckHandler::THREAD_SANITIZER) {
753 envVar = "TSAN_OPTIONS";
754 } else if (this->MemoryTesterStyle ==
755 cmCTestMemCheckHandler::MEMORY_SANITIZER) {
756 envVar = "MSAN_OPTIONS";
757 } else if (this->MemoryTesterStyle ==
758 cmCTestMemCheckHandler::UB_SANITIZER) {
759 envVar = "UBSAN_OPTIONS";
761 // Quote log_path with single quotes; see
762 // https://bugs.chromium.org/p/chromium/issues/detail?id=467936
763 std::string outputFile =
764 envVar + "=log_path='" + this->MemoryTesterOutputFile + "'";
765 this->MemoryTesterEnvironmentVariable =
766 outputFile + suppressionsOption + extraOptions;
770 cmCTestLog(this->CTest, ERROR_MESSAGE,
771 "Do not understand memory checker: " << this->MemoryTester
776 this->InitializeResultsVectors();
777 // std::vector<std::string>::size_type cc;
778 // for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ )
780 // this->MemoryTesterGlobalResults[cc] = 0;
785 bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
787 std::vector<int>& results)
789 switch (this->MemoryTesterStyle) {
790 case cmCTestMemCheckHandler::VALGRIND:
791 return this->ProcessMemCheckValgrindOutput(str, log, results);
792 case cmCTestMemCheckHandler::DRMEMORY:
793 return this->ProcessMemCheckDrMemoryOutput(str, log, results);
794 case cmCTestMemCheckHandler::PURIFY:
795 return this->ProcessMemCheckPurifyOutput(str, log, results);
796 case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
797 case cmCTestMemCheckHandler::LEAK_SANITIZER:
798 case cmCTestMemCheckHandler::THREAD_SANITIZER:
799 case cmCTestMemCheckHandler::MEMORY_SANITIZER:
800 case cmCTestMemCheckHandler::UB_SANITIZER:
801 return this->ProcessMemCheckSanitizerOutput(str, log, results);
802 case cmCTestMemCheckHandler::BOUNDS_CHECKER:
803 return this->ProcessMemCheckBoundsCheckerOutput(str, log, results);
804 case cmCTestMemCheckHandler::CUDA_SANITIZER:
805 return this->ProcessMemCheckCudaOutput(str, log, results);
807 log.append("\nMemory checking style used was: ");
808 log.append("None that I know");
814 std::vector<int>::size_type cmCTestMemCheckHandler::FindOrAddWarning(
815 const std::string& warning)
817 for (std::vector<std::string>::size_type i = 0;
818 i < this->ResultStrings.size(); ++i) {
819 if (this->ResultStrings[i] == warning) {
823 this->GlobalResults.push_back(0); // this must stay the same size
824 this->ResultStrings.push_back(warning);
825 this->ResultStringsLong.push_back(warning);
826 return this->ResultStrings.size() - 1;
828 bool cmCTestMemCheckHandler::ProcessMemCheckSanitizerOutput(
829 const std::string& str, std::string& log, std::vector<int>& result)
832 switch (this->MemoryTesterStyle) {
833 case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
834 regex = "ERROR: AddressSanitizer: (.*) on.*";
836 case cmCTestMemCheckHandler::LEAK_SANITIZER:
837 // use leakWarning regex
839 case cmCTestMemCheckHandler::THREAD_SANITIZER:
840 regex = "WARNING: ThreadSanitizer: (.*) \\(pid=.*\\)";
842 case cmCTestMemCheckHandler::MEMORY_SANITIZER:
843 regex = "WARNING: MemorySanitizer: (.*)";
845 case cmCTestMemCheckHandler::UB_SANITIZER:
846 regex = "runtime error: (.*)";
851 cmsys::RegularExpression sanitizerWarning(regex);
852 cmsys::RegularExpression leakWarning("(Direct|Indirect) leak of .*");
854 std::vector<std::string> lines;
855 cmsys::SystemTools::Split(str, lines);
856 std::ostringstream ostr;
858 for (std::string const& l : lines) {
859 std::string resultFound;
860 if (leakWarning.find(l)) {
861 resultFound = leakWarning.match(1) + " leak";
862 } else if (sanitizerWarning.find(l)) {
863 resultFound = sanitizerWarning.match(1);
865 if (!resultFound.empty()) {
866 std::vector<int>::size_type idx = this->FindOrAddWarning(resultFound);
867 if (result.empty() || idx > result.size() - 1) {
873 ostr << "<b>" << this->ResultStrings[idx] << "</b> ";
875 ostr << l << std::endl;
878 this->DefectCount += defects;
881 bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
882 const std::string& str, std::string& log, std::vector<int>& results)
884 std::vector<std::string> lines;
885 cmsys::SystemTools::Split(str, lines);
886 std::ostringstream ostr;
889 cmsys::RegularExpression pfW("^\\[[WEI]\\] ([A-Z][A-Z][A-Z][A-Z]*): ");
893 for (std::string const& l : lines) {
894 std::vector<int>::size_type failure = this->ResultStrings.size();
896 std::vector<int>::size_type cc;
897 for (cc = 0; cc < this->ResultStrings.size(); cc++) {
898 if (pfW.match(1) == this->ResultStrings[cc]) {
903 if (cc == this->ResultStrings.size()) {
904 cmCTestLog(this->CTest, ERROR_MESSAGE,
905 "Unknown Purify memory fault: " << pfW.match(1)
907 ostr << "*** Unknown Purify memory fault: " << pfW.match(1)
911 if (failure != this->ResultStrings.size()) {
912 ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
916 ostr << l << std::endl;
920 this->DefectCount += defects;
924 bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
925 const std::string& str, std::string& log, std::vector<int>& results)
927 std::vector<std::string> lines;
928 cmsys::SystemTools::Split(str, lines);
929 bool unlimitedOutput = false;
930 if (str.find("CTEST_FULL_OUTPUT") != std::string::npos ||
931 this->CustomMaximumFailedTestOutputSize == 0) {
932 unlimitedOutput = true;
935 std::string::size_type cc;
937 std::ostringstream ostr;
942 cmsys::RegularExpression valgrindLine("^==[0-9][0-9]*==");
944 cmsys::RegularExpression vgFIM(
945 R"(== .*Invalid free\(\) / delete / delete\[\])");
946 cmsys::RegularExpression vgFMM(
947 R"(== .*Mismatched free\(\) / delete / delete \[\])");
948 cmsys::RegularExpression vgMLK1(
949 "== .*[0-9,]+ bytes in [0-9,]+ blocks are definitely lost"
950 " in loss record [0-9,]+ of [0-9,]+");
951 cmsys::RegularExpression vgMLK2(
952 "== .*[0-9,]+ \\([0-9,]+ direct, [0-9,]+ indirect\\)"
953 " bytes in [0-9,]+ blocks are definitely lost"
954 " in loss record [0-9,]+ of [0-9,]+");
955 cmsys::RegularExpression vgPAR(
956 "== .*Syscall param .* (contains|points to) unaddressable byte\\(s\\)");
957 cmsys::RegularExpression vgMPK1(
958 "== .*[0-9,]+ bytes in [0-9,]+ blocks are possibly lost in"
959 " loss record [0-9,]+ of [0-9,]+");
960 cmsys::RegularExpression vgMPK2(
961 "== .*[0-9,]+ bytes in [0-9,]+ blocks are still reachable"
962 " in loss record [0-9,]+ of [0-9,]+");
963 cmsys::RegularExpression vgUMC(
964 "== .*Conditional jump or move depends on uninitialised value\\(s\\)");
965 cmsys::RegularExpression vgUMR1(
966 "== .*Use of uninitialised value of size [0-9,]+");
967 cmsys::RegularExpression vgUMR2("== .*Invalid read of size [0-9,]+");
968 cmsys::RegularExpression vgUMR3("== .*Jump to the invalid address ");
969 cmsys::RegularExpression vgUMR4(
970 "== .*Syscall param .* contains "
971 "uninitialised or unaddressable byte\\(s\\)");
972 cmsys::RegularExpression vgUMR5("== .*Syscall param .* uninitialised");
973 cmsys::RegularExpression vgIPW("== .*Invalid write of size [0-9,]+");
974 cmsys::RegularExpression vgABR("== .*pthread_mutex_unlock: mutex is "
975 "locked by a different thread");
976 std::vector<std::string::size_type> nonValGrindOutput;
977 auto sttime = std::chrono::steady_clock::now();
978 cmCTestOptionalLog(this->CTest, DEBUG,
979 "Start test: " << lines.size() << std::endl, this->Quiet);
980 std::string::size_type totalOutputSize = 0;
981 for (cc = 0; cc < lines.size(); cc++) {
982 cmCTestOptionalLog(this->CTest, DEBUG,
983 "test line " << lines[cc] << std::endl, this->Quiet);
985 if (valgrindLine.find(lines[cc])) {
986 cmCTestOptionalLog(this->CTest, DEBUG,
987 "valgrind line " << lines[cc] << std::endl,
989 int failure = cmCTestMemCheckHandler::NO_MEMORY_FAULT;
990 auto& line = lines[cc];
991 if (vgFIM.find(line)) {
992 failure = cmCTestMemCheckHandler::FIM;
993 } else if (vgFMM.find(line)) {
994 failure = cmCTestMemCheckHandler::FMM;
995 } else if (vgMLK1.find(line) || vgMLK2.find(line)) {
996 failure = cmCTestMemCheckHandler::MLK;
997 } else if (vgPAR.find(line)) {
998 failure = cmCTestMemCheckHandler::PAR;
999 } else if (vgMPK1.find(line) || vgMPK2.find(line)) {
1000 failure = cmCTestMemCheckHandler::MPK;
1001 } else if (vgUMC.find(line)) {
1002 failure = cmCTestMemCheckHandler::UMC;
1003 } else if (vgUMR1.find(line) || vgUMR2.find(line) || vgUMR3.find(line) ||
1004 vgUMR4.find(line) || vgUMR5.find(line)) {
1005 failure = cmCTestMemCheckHandler::UMR;
1006 } else if (vgIPW.find(line)) {
1007 failure = cmCTestMemCheckHandler::IPW;
1008 } else if (vgABR.find(line)) {
1009 failure = cmCTestMemCheckHandler::ABR;
1012 if (failure != cmCTestMemCheckHandler::NO_MEMORY_FAULT) {
1013 ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
1017 totalOutputSize += lines[cc].size();
1018 ostr << lines[cc] << std::endl;
1020 nonValGrindOutput.push_back(cc);
1023 // Now put all all the non valgrind output into the test output
1024 // This should be last in case it gets truncated by the output
1026 for (std::string::size_type i : nonValGrindOutput) {
1027 totalOutputSize += lines[i].size();
1028 ostr << lines[i] << std::endl;
1029 if (!unlimitedOutput &&
1031 static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)) {
1033 ostr << "Test Output for this test has been truncated see testing"
1034 " machine logs for full output,\n";
1035 ostr << "or put CTEST_FULL_OUTPUT in the output of "
1036 "this test program.\n";
1037 break; // stop the copy of output if we are full
1040 cmCTestOptionalLog(this->CTest, DEBUG,
1041 "End test (elapsed: "
1042 << cmDurationTo<unsigned int>(
1043 std::chrono::steady_clock::now() - sttime)
1044 << "s)" << std::endl,
1047 this->DefectCount += defects;
1048 return defects == 0;
1051 bool cmCTestMemCheckHandler::ProcessMemCheckDrMemoryOutput(
1052 const std::string& str, std::string& log, std::vector<int>& results)
1054 std::vector<std::string> lines;
1055 cmsys::SystemTools::Split(str, lines);
1057 cmsys::RegularExpression drMemoryError("^Error #[0-9]+");
1059 cmsys::RegularExpression unaddressableAccess("UNADDRESSABLE ACCESS");
1060 cmsys::RegularExpression uninitializedRead("UNINITIALIZED READ");
1061 cmsys::RegularExpression invalidHeapArgument("INVALID HEAP ARGUMENT");
1062 cmsys::RegularExpression leak("LEAK");
1063 cmsys::RegularExpression handleLeak("HANDLE LEAK");
1067 std::ostringstream ostr;
1068 for (const auto& l : lines) {
1069 ostr << l << std::endl;
1070 if (drMemoryError.find(l)) {
1072 if (unaddressableAccess.find(l) || uninitializedRead.find(l)) {
1073 results[cmCTestMemCheckHandler::UMR]++;
1074 } else if (leak.find(l) || handleLeak.find(l)) {
1075 results[cmCTestMemCheckHandler::MLK]++;
1076 } else if (invalidHeapArgument.find(l)) {
1077 results[cmCTestMemCheckHandler::FMM]++;
1084 this->DefectCount += defects;
1085 return defects == 0;
1088 bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
1089 const std::string& str, std::string& log, std::vector<int>& results)
1092 auto sttime = std::chrono::steady_clock::now();
1093 std::vector<std::string> lines;
1094 cmsys::SystemTools::Split(str, lines);
1095 cmCTestOptionalLog(this->CTest, DEBUG,
1096 "Start test: " << lines.size() << std::endl, this->Quiet);
1097 std::vector<std::string>::size_type cc;
1098 for (cc = 0; cc < lines.size(); cc++) {
1099 if (lines[cc] == BOUNDS_CHECKER_MARKER) {
1103 cmBoundsCheckerParser parser(this->CTest);
1104 parser.InitializeParser();
1105 if (cc < lines.size()) {
1106 for (cc++; cc < lines.size(); ++cc) {
1107 std::string& theLine = lines[cc];
1108 // check for command line arguments that are not escaped
1110 if (theLine.find("TargetArgs=") != std::string::npos) {
1111 // skip this because BC gets it wrong and we can't parse it
1112 } else if (!parser.ParseChunk(theLine.c_str(), theLine.size())) {
1113 cmCTestLog(this->CTest, ERROR_MESSAGE,
1114 "Error in ParseChunk: " << theLine << std::endl);
1119 for (int err : parser.Errors) {
1123 cmCTestOptionalLog(this->CTest, DEBUG,
1124 "End test (elapsed: "
1125 << cmDurationTo<unsigned int>(
1126 std::chrono::steady_clock::now() - sttime)
1127 << "s)" << std::endl,
1130 // only put the output of Bounds Checker if there were
1131 // errors or leaks detected
1134 this->DefectCount += defects;
1135 return defects == 0;
1138 bool cmCTestMemCheckHandler::ProcessMemCheckCudaOutput(
1139 const std::string& str, std::string& log, std::vector<int>& results)
1141 std::vector<std::string> lines;
1142 cmsys::SystemTools::Split(str, lines);
1143 bool unlimitedOutput = false;
1144 if (str.find("CTEST_FULL_OUTPUT") != std::string::npos ||
1145 this->CustomMaximumFailedTestOutputSize == 0) {
1146 unlimitedOutput = true;
1149 std::string::size_type cc;
1151 std::ostringstream ostr;
1156 cmsys::RegularExpression memcheckLine("^========");
1158 cmsys::RegularExpression leakExpr("== Leaked [0-9,]+ bytes at");
1160 // list of matchers for output messages that contain variable content
1161 // (addresses, sizes, ...) or can be shortened in general. the first match is
1162 // used as a error name.
1163 std::vector<cmsys::RegularExpression> matchers{
1165 "== Malloc/Free error encountered: (.*)",
1166 "== Program hit error ([^ ]*).* on CUDA API call to",
1167 "== Program hit ([^ ]*).* on CUDA API call to",
1169 "== (Invalid .*) of size [0-9,]+", "== (Fatal UVM [CG]PU fault)",
1171 "== .* (Potential .* hazard detected)", "== .* (Race reported)",
1173 "== (Barrier error)",
1175 "== (Uninitialized .* memory read)", "== (Unused memory)",
1176 "== (Host API memory access error)",
1177 // generic error: ignore ERROR SUMMARY, CUDA-MEMCHECK and others
1181 std::vector<std::string::size_type> nonMemcheckOutput;
1182 auto sttime = std::chrono::steady_clock::now();
1183 cmCTestOptionalLog(this->CTest, DEBUG,
1184 "Start test: " << lines.size() << std::endl, this->Quiet);
1185 std::string::size_type totalOutputSize = 0;
1186 for (cc = 0; cc < lines.size(); cc++) {
1187 cmCTestOptionalLog(this->CTest, DEBUG,
1188 "test line " << lines[cc] << std::endl, this->Quiet);
1190 if (memcheckLine.find(lines[cc])) {
1191 cmCTestOptionalLog(this->CTest, DEBUG,
1192 "cuda sanitizer line " << lines[cc] << std::endl,
1195 auto& line = lines[cc];
1196 if (leakExpr.find(line)) {
1197 failure = static_cast<int>(this->FindOrAddWarning("Memory leak"));
1199 for (auto& matcher : matchers) {
1200 if (matcher.find(line)) {
1202 static_cast<int>(this->FindOrAddWarning(matcher.match(1)));
1209 ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
1210 if (results.empty() ||
1211 static_cast<unsigned>(failure) > results.size() - 1) {
1212 results.push_back(1);
1218 totalOutputSize += lines[cc].size();
1219 ostr << lines[cc] << std::endl;
1221 nonMemcheckOutput.push_back(cc);
1224 // Now put all all the non cuda sanitizer output into the test output
1225 // This should be last in case it gets truncated by the output
1227 for (std::string::size_type i : nonMemcheckOutput) {
1228 totalOutputSize += lines[i].size();
1229 ostr << lines[i] << std::endl;
1230 if (!unlimitedOutput &&
1232 static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)) {
1234 ostr << "Test Output for this test has been truncated see testing"
1235 " machine logs for full output,\n";
1236 ostr << "or put CTEST_FULL_OUTPUT in the output of "
1237 "this test program.\n";
1238 break; // stop the copy of output if we are full
1241 cmCTestOptionalLog(this->CTest, DEBUG,
1242 "End test (elapsed: "
1243 << cmDurationTo<unsigned int>(
1244 std::chrono::steady_clock::now() - sttime)
1245 << "s)" << std::endl,
1248 this->DefectCount += defects;
1249 return defects == 0;
1252 // PostProcessTest memcheck results
1253 void cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res, int test)
1255 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1256 "PostProcessTest memcheck results for : " << res.Name
1259 if (this->MemoryTesterStyle == cmCTestMemCheckHandler::BOUNDS_CHECKER) {
1260 this->PostProcessBoundsCheckerTest(res, test);
1261 } else if (this->MemoryTesterStyle == cmCTestMemCheckHandler::DRMEMORY) {
1262 this->PostProcessDrMemoryTest(res, test);
1264 std::vector<std::string> files;
1265 this->TestOutputFileNames(test, files);
1266 for (std::string const& f : files) {
1267 this->AppendMemTesterOutput(res, f);
1272 // This method puts the bounds checker output file into the output
1274 void cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(
1275 cmCTestTestResult& res, int test)
1277 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1278 "PostProcessBoundsCheckerTest for : " << res.Name
1281 std::vector<std::string> files;
1282 this->TestOutputFileNames(test, files);
1283 if (files.empty()) {
1286 std::string ofile = files[0];
1287 if (ofile.empty()) {
1290 // put a scope around this to close ifs so the file can be removed
1292 cmsys::ifstream ifs(ofile.c_str());
1294 std::string log = "Cannot read memory tester output file: " + ofile;
1295 cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
1298 res.Output += BOUNDS_CHECKER_MARKER;
1301 while (cmSystemTools::GetLineFromStream(ifs, line)) {
1306 cmSystemTools::Delay(1000);
1307 cmSystemTools::RemoveFile(this->BoundsCheckerDPBDFile);
1308 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1309 "Remove: " << this->BoundsCheckerDPBDFile << std::endl,
1311 cmSystemTools::RemoveFile(this->BoundsCheckerXMLFile);
1312 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1313 "Remove: " << this->BoundsCheckerXMLFile << std::endl,
1317 void cmCTestMemCheckHandler::PostProcessDrMemoryTest(
1318 cmCTestTestHandler::cmCTestTestResult& res, int test)
1320 std::string drMemoryLogDir = this->MemoryTesterOutputFile.substr(
1321 0, this->MemoryTesterOutputFile.find("/*/results.txt"));
1323 // replace placeholder of test
1324 std::string::size_type pos = drMemoryLogDir.find("??");
1325 if (pos != std::string::npos) {
1326 drMemoryLogDir.replace(pos, 2, std::to_string(test));
1330 g.FindFiles(drMemoryLogDir + "/resfile.*");
1331 const std::vector<std::string>& files = g.GetFiles();
1333 for (const std::string& f : files) {
1334 cmsys::ifstream ifs(f.c_str());
1336 std::string log = "Cannot read memory tester output file: " + f;
1337 cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
1340 std::string resultFileLocation;
1341 cmSystemTools::GetLineFromStream(ifs, resultFileLocation);
1342 this->AppendMemTesterOutput(res, resultFileLocation);
1344 cmSystemTools::RemoveFile(f);
1348 void cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res,
1349 std::string const& ofile)
1351 if (ofile.empty()) {
1354 // put ifs in scope so file can be deleted if needed
1356 cmsys::ifstream ifs(ofile.c_str());
1358 std::string log = "Cannot read memory tester output file: " + ofile;
1359 cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
1363 while (cmSystemTools::GetLineFromStream(ifs, line)) {
1368 if (this->LogWithPID) {
1369 auto pos = ofile.find_last_of('.');
1370 if (pos != std::string::npos) {
1371 auto ofileWithoutPid = ofile.substr(0, pos);
1372 cmSystemTools::RenameFile(ofile, ofileWithoutPid);
1373 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1374 "Renaming: " << ofile << " to: " << ofileWithoutPid
1381 void cmCTestMemCheckHandler::TestOutputFileNames(
1382 int test, std::vector<std::string>& files)
1384 std::string index = std::to_string(test);
1385 std::string ofile = this->MemoryTesterOutputFile;
1386 std::string::size_type pos = ofile.find("??");
1387 ofile.replace(pos, 2, index);
1388 if (this->LogWithPID) {
1392 if (g.GetFiles().empty()) {
1393 std::string log = "Cannot find memory tester output file: " + ofile;
1394 cmCTestLog(this->CTest, WARNING, log << std::endl);
1397 files = g.GetFiles();
1400 } else if (!cmSystemTools::FileExists(ofile)) {
1401 std::string log = "Cannot find memory tester output file: " + ofile;
1402 cmCTestLog(this->CTest, WARNING, log << std::endl);
1405 files.push_back(std::move(ofile));