788845b3e81455166438306780aff0468c20291e
[platform/upstream/cmake.git] / Source / CTest / cmCTestMemCheckHandler.cxx
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"
4
5 #include <algorithm>
6 #include <chrono>
7 #include <cstring>
8 #include <iostream>
9 #include <iterator>
10 #include <sstream>
11 #include <utility>
12
13 #include <cmext/algorithm>
14
15 #include "cmsys/FStream.hxx"
16 #include "cmsys/Glob.hxx"
17 #include "cmsys/RegularExpression.hxx"
18
19 #include "cmCTest.h"
20 #include "cmDuration.h"
21 #include "cmSystemTools.h"
22 #include "cmXMLParser.h"
23 #include "cmXMLWriter.h"
24
25 struct CatToErrorType
26 {
27   const char* ErrorCategory;
28   int ErrorCode;
29 };
30
31 static CatToErrorType cmCTestMemCheckBoundsChecker[] = {
32   // Error tags
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 },
39   { nullptr, 0 }
40 };
41
42 static void xmlReportError(int line, const char* msg, void* data)
43 {
44   cmCTest* ctest = static_cast<cmCTest*>(data);
45   cmCTestLog(ctest, ERROR_MESSAGE,
46              "Error parsing XML in stream at line " << line << ": " << msg
47                                                     << std::endl);
48 }
49
50 // parse the xml file containing the results of last BoundsChecker run
51 class cmBoundsCheckerParser : public cmXMLParser
52 {
53 public:
54   cmBoundsCheckerParser(cmCTest* c)
55   {
56     this->CTest = c;
57     this->SetErrorCallback(xmlReportError, c);
58   }
59   void StartElement(const std::string& name, const char** atts) override
60   {
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);
65     }
66     // Create the log
67     std::ostringstream ostr;
68     ostr << name << ":\n";
69     int i = 0;
70     for (; atts[i] != nullptr; i += 2) {
71       ostr << "   " << atts[i] << " - " << atts[i + 1] << "\n";
72     }
73     ostr << "\n";
74     this->Log += ostr.str();
75   }
76   void EndElement(const std::string& /*name*/) override {}
77
78   const char* GetAttribute(const char* name, const char** atts)
79   {
80     int i = 0;
81     for (; atts[i] != nullptr; ++i) {
82       if (strcmp(name, atts[i]) == 0) {
83         return atts[i + 1];
84       }
85     }
86     return nullptr;
87   }
88   void ParseError(const char** atts)
89   {
90     CatToErrorType* ptr = cmCTestMemCheckBoundsChecker;
91     const char* cat = this->GetAttribute("ErrorCategory", atts);
92     if (!cat) {
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");
96       return;
97     }
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
102       }
103       ptr++;
104     }
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
109                                                        << std::endl);
110     }
111   }
112   cmCTest* CTest;
113   std::vector<int> Errors;
114   std::string Log;
115 };
116
117 #define BOUNDS_CHECKER_MARKER                                                 \
118   "******######*****Begin BOUNDS CHECKER XML******######******"
119
120 cmCTestMemCheckHandler::cmCTestMemCheckHandler()
121 {
122   this->MemCheck = true;
123   this->CustomMaximumPassedTestOutputSize = 0;
124   this->CustomMaximumFailedTestOutputSize = 0;
125   this->LogWithPID = false;
126 }
127
128 void cmCTestMemCheckHandler::Initialize()
129 {
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;
140 }
141
142 int cmCTestMemCheckHandler::PreProcessHandler()
143 {
144   if (!this->InitializeMemoryChecking()) {
145     return 0;
146   }
147
148   if (!this->ExecuteCommands(this->CustomPreMemCheck)) {
149     cmCTestLog(this->CTest, ERROR_MESSAGE,
150                "Problem executing pre-memcheck command(s)." << std::endl);
151     return 0;
152   }
153   return 1;
154 }
155
156 int cmCTestMemCheckHandler::PostProcessHandler()
157 {
158   if (!this->ExecuteCommands(this->CustomPostMemCheck)) {
159     cmCTestLog(this->CTest, ERROR_MESSAGE,
160                "Problem executing post-memcheck command(s)." << std::endl);
161     return 0;
162   }
163   return 1;
164 }
165
166 void cmCTestMemCheckHandler::GenerateTestCommand(
167   std::vector<std::string>& args, int test)
168 {
169   std::string index = std::to_string(test);
170   std::string memcheckcommand =
171     cmSystemTools::ConvertToOutputPath(this->MemoryTester);
172
173   std::vector<std::string> dirs;
174   bool nextArgIsDir = false;
175
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);
180     }
181     args.push_back(arg);
182     memcheckcommand += " \"";
183     memcheckcommand += arg;
184     memcheckcommand += "\"";
185
186     if (nextArgIsDir) {
187       nextArgIsDir = false;
188       dirs.push_back(arg);
189     }
190
191     if (this->MemoryTesterStyle == cmCTestMemCheckHandler::DRMEMORY &&
192         (arg == "-logdir" || arg == "-symcache_dir")) {
193       nextArgIsDir = true;
194     }
195   }
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;
206     }
207     // for regular options just add them to args and memcheckcommand
208     // which is just used for display
209     else {
210       args.push_back(arg);
211       memcheckcommand += " \"";
212       memcheckcommand += arg;
213       memcheckcommand += "\"";
214     }
215   }
216   // if this is an env option type, then add the env string as a single
217   // argument.
218   if (!memTesterEnvironmentVariable.empty()) {
219     std::string::size_type pos = memTesterEnvironmentVariable.find("??");
220     if (pos != std::string::npos) {
221       memTesterEnvironmentVariable.replace(pos, 2, index);
222     }
223     memcheckcommand += " " + memTesterEnvironmentVariable;
224     args.push_back(memTesterEnvironmentVariable);
225   }
226
227   for (std::string const& dir : dirs) {
228     cmSystemTools::MakeDirectory(dir);
229   }
230
231   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
232                      "Memory check command: " << memcheckcommand << std::endl,
233                      this->Quiet);
234 }
235
236 void cmCTestMemCheckHandler::InitializeResultsVectors()
237 {
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) {
248     return;
249   }
250
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
257   };
258   static const char* cmCTestMemCheckResultLongStrings[] = {
259     "Threading Problem",
260     "ABW",
261     "ABWL",
262     "COR",
263     "EXU",
264     "FFM",
265     "FIM",
266     "Mismatched deallocation",
267     "FMR",
268     "FMW",
269     "FUM",
270     "IPR",
271     "IPW",
272     "MAF",
273     "Memory Leak",
274     "Potential Memory Leak",
275     "NPR",
276     "ODS",
277     "Invalid syscall param",
278     "PLK",
279     "Uninitialized Memory Conditional",
280     "Uninitialized Memory Read",
281     nullptr
282   };
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);
288   }
289 }
290
291 void cmCTestMemCheckHandler::PopulateCustomVectors(cmMakefile* mf)
292 {
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);
298
299   this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_MEMCHECK_IGNORE",
300                                     this->CustomTestsIgnore);
301 }
302
303 int cmCTestMemCheckHandler::GetDefectCount() const
304 {
305   return this->DefectCount;
306 }
307
308 void cmCTestMemCheckHandler::GenerateCTestXML(cmXMLWriter& xml)
309 {
310   if (!this->CTest->GetProduceXML()) {
311     return;
312   }
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");
319       break;
320     case cmCTestMemCheckHandler::DRMEMORY:
321       xml.Attribute("Checker", "DrMemory");
322       break;
323     case cmCTestMemCheckHandler::PURIFY:
324       xml.Attribute("Checker", "Purify");
325       break;
326     case cmCTestMemCheckHandler::BOUNDS_CHECKER:
327       xml.Attribute("Checker", "BoundsChecker");
328       break;
329     case cmCTestMemCheckHandler::CUDA_SANITIZER:
330       xml.Attribute("Checker", "CudaSanitizer");
331       break;
332     case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
333       xml.Attribute("Checker", "AddressSanitizer");
334       break;
335     case cmCTestMemCheckHandler::LEAK_SANITIZER:
336       xml.Attribute("Checker", "LeakSanitizer");
337       break;
338     case cmCTestMemCheckHandler::THREAD_SANITIZER:
339       xml.Attribute("Checker", "ThreadSanitizer");
340       break;
341     case cmCTestMemCheckHandler::MEMORY_SANITIZER:
342       xml.Attribute("Checker", "MemorySanitizer");
343       break;
344     case cmCTestMemCheckHandler::UB_SANITIZER:
345       xml.Attribute("Checker", "UndefinedBehaviorSanitizer");
346       break;
347     default:
348       xml.Attribute("Checker", "Unknown");
349   }
350
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));
358   }
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);
367     bool res =
368       this->ProcessMemCheckOutput(result.Output, memcheckstr, memcheckresults);
369     if (res && result.Status == cmCTestMemCheckHandler::COMPLETED) {
370       continue;
371     }
372     this->CleanTestOutput(
373       memcheckstr,
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();
380          ++kk) {
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
387       }
388       this->GlobalResults[kk] += memcheckresults[kk];
389     }
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,
399                          this->Quiet);
400     }
401     xml.StartElement("Log");
402     if (this->CTest->ShouldCompressTestOutput()) {
403       this->CTest->CompressString(memcheckstr);
404       xml.Attribute("compression", "gzip");
405       xml.Attribute("encoding", "base64");
406     }
407     xml.Content(memcheckstr);
408     xml.EndElement(); // Log
409
410     this->WriteTestResultFooter(xml, result);
411   }
412   cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
413                      "MemCheck log files can be found here: "
414                      "(<#> corresponds to test number)"
415                        << std::endl,
416                      this->Quiet);
417   std::string output = this->MemoryTesterOutputFile;
418   cmSystemTools::ReplaceString(output, "??", "<#>");
419   cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, output << std::endl,
420                      this->Quiet);
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]) {
426       std::cerr.width(35);
427       cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
428                          this->ResultStringsLong[cc]
429                            << " - " << this->GlobalResults[cc] << std::endl,
430                          this->Quiet);
431       xml.StartElement("Defect");
432       xml.Attribute("Type", this->ResultStringsLong[cc]);
433       xml.EndElement();
434     }
435   }
436   xml.EndElement(); // DefectList
437
438   xml.Element("EndDateTime", this->EndTest);
439   xml.Element("EndTestTime", this->EndTestTime);
440   xml.Element(
441     "ElapsedMinutes",
442     std::chrono::duration_cast<std::chrono::minutes>(this->ElapsedTestingTime)
443       .count());
444
445   xml.EndElement(); // DynamicAnalysis
446   this->CTest->EndXML(xml);
447 }
448
449 bool cmCTestMemCheckHandler::InitializeMemoryChecking()
450 {
451   this->MemoryTesterEnvironmentVariable.clear();
452   this->MemoryTester.clear();
453   // Setup the command
454   if (cmSystemTools::FileExists(
455         this->CTest->GetCTestConfiguration("MemoryCheckCommand"))) {
456     this->MemoryTester =
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") ==
466                  "DrMemory") {
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;
475     } else {
476       this->MemoryTesterStyle = cmCTestMemCheckHandler::UNKNOWN;
477     }
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"))) {
492     this->MemoryTester =
493       this->CTest->GetCTestConfiguration("BoundsCheckerCommand");
494     this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
495   } else if (cmSystemTools::FileExists(
496                this->CTest->GetCTestConfiguration("CudaSanitizerCommand"))) {
497     this->MemoryTester =
498       this->CTest->GetCTestConfiguration("CudaSanitizerCommand");
499     this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_SANITIZER;
500   }
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
506   }
507   if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
508       "LeakSanitizer") {
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
512   }
513   if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
514       "ThreadSanitizer") {
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
518   }
519   if (this->CTest->GetCTestConfiguration("MemoryCheckType") ==
520       "MemorySanitizer") {
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
524   }
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
530   }
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;
545     }
546   }
547   if (this->MemoryTester.empty()) {
548     cmCTestOptionalLog(this->CTest, WARNING,
549                        "Memory checker (MemoryCheckCommand) "
550                        "not set, or cannot find the specified program."
551                          << std::endl,
552                        this->Quiet);
553     return false;
554   }
555
556   // Setup the options
557   std::string memoryTesterOptions;
558   if (!this->CTest->GetCTestConfiguration("MemoryCheckCommandOptions")
559          .empty()) {
560     memoryTesterOptions =
561       this->CTest->GetCTestConfiguration("MemoryCheckCommandOptions");
562   } else if (!this->CTest->GetCTestConfiguration("ValgrindCommandOptions")
563                 .empty()) {
564     memoryTesterOptions =
565       this->CTest->GetCTestConfiguration("ValgrindCommandOptions");
566   } else if (!this->CTest->GetCTestConfiguration("DrMemoryCommandOptions")
567                 .empty()) {
568     memoryTesterOptions =
569       this->CTest->GetCTestConfiguration("DrMemoryCommandOptions");
570   } else if (!this->CTest->GetCTestConfiguration("CudaSanitizerCommandOptions")
571                 .empty()) {
572     memoryTesterOptions =
573       this->CTest->GetCTestConfiguration("CudaSanitizerCommandOptions");
574   }
575   this->MemoryTesterOptions =
576     cmSystemTools::ParseArguments(memoryTesterOptions);
577
578   this->MemoryTesterOutputFile =
579     this->CTest->GetBinaryDir() + "/Testing/Temporary/MemoryChecker.??.log";
580
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");
589       }
590       if (!this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
591              .empty()) {
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")
598                        << std::endl);
599           return false;
600         }
601         this->MemoryTesterOptions.push_back(
602           "--suppressions=" +
603           this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile"));
604       }
605       this->MemoryTesterDynamicOptions.push_back("--log-file=" +
606                                                  this->MemoryTesterOutputFile);
607       break;
608     }
609     case cmCTestMemCheckHandler::DRMEMORY: {
610       std::string tempDrMemoryDir =
611         this->CTest->GetBinaryDir() + "/Testing/Temporary/DrMemory";
612
613       if (!cm::contains(this->MemoryTesterOptions, "-quiet")) {
614         this->MemoryTesterOptions.emplace_back("-quiet");
615       }
616
617       if (!cm::contains(this->MemoryTesterOptions, "-batch")) {
618         this->MemoryTesterOptions.emplace_back("-batch");
619       }
620
621       this->MemoryTesterDynamicOptions.emplace_back("-logdir");
622       auto logdirOption =
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;
630       } else {
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);
636       }
637       this->MemoryTesterOutputFile += "/*/results.txt";
638
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);
645       }
646
647       if (!this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
648              .empty()) {
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")
655                        << std::endl);
656           return false;
657         }
658         this->MemoryTesterOptions.emplace_back("-suppress");
659         this->MemoryTesterOptions.push_back(
660           this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile"));
661       }
662
663       this->MemoryTesterOptions.emplace_back("--");
664
665       break;
666     }
667     case cmCTestMemCheckHandler::PURIFY: {
668       std::string outputFile;
669 #ifdef _WIN32
670       if (this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
671             .size()) {
672         if (!cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
673               "MemoryCheckSuppressionFile"))) {
674           cmCTestLog(
675             this->CTest, ERROR_MESSAGE,
676             "Cannot find memory checker suppression file: "
677               << this->CTest
678                    ->GetCTestConfiguration("MemoryCheckSuppressionFile")
679                    .c_str()
680               << std::endl);
681           return false;
682         }
683         std::string filterFiles = "/FilterFiles=" +
684           this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile");
685         this->MemoryTesterOptions.push_back(filterFiles);
686       }
687       outputFile = "/SAVETEXTDATA=";
688 #else
689       outputFile = "-log-file=";
690 #endif
691       outputFile += this->MemoryTesterOutputFile;
692       this->MemoryTesterDynamicOptions.push_back(outputFile);
693       break;
694     }
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");
705       break;
706     }
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");
714       }
715       this->MemoryTesterDynamicOptions.emplace_back("--log-file");
716       this->MemoryTesterDynamicOptions.push_back(this->MemoryTesterOutputFile);
717       break;
718     }
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");
732       std::string envVar;
733       std::string extraOptions;
734       std::string suppressionsOption;
735       if (!this->CTest->GetCTestConfiguration("MemoryCheckSanitizerOptions")
736              .empty()) {
737         extraOptions = ":" +
738           this->CTest->GetCTestConfiguration("MemoryCheckSanitizerOptions");
739       }
740       if (!this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile")
741              .empty()) {
742         suppressionsOption = ":suppressions=" +
743           this->CTest->GetCTestConfiguration("MemoryCheckSuppressionFile");
744       }
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";
760       }
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;
767       break;
768     }
769     default:
770       cmCTestLog(this->CTest, ERROR_MESSAGE,
771                  "Do not understand memory checker: " << this->MemoryTester
772                                                       << std::endl);
773       return false;
774   }
775
776   this->InitializeResultsVectors();
777   // std::vector<std::string>::size_type cc;
778   // for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ )
779   //   {
780   //   this->MemoryTesterGlobalResults[cc] = 0;
781   //   }
782   return true;
783 }
784
785 bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
786                                                    std::string& log,
787                                                    std::vector<int>& results)
788 {
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);
806     default:
807       log.append("\nMemory checking style used was: ");
808       log.append("None that I know");
809       log = str;
810       return true;
811   }
812 }
813
814 std::vector<int>::size_type cmCTestMemCheckHandler::FindOrAddWarning(
815   const std::string& warning)
816 {
817   for (std::vector<std::string>::size_type i = 0;
818        i < this->ResultStrings.size(); ++i) {
819     if (this->ResultStrings[i] == warning) {
820       return i;
821     }
822   }
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;
827 }
828 bool cmCTestMemCheckHandler::ProcessMemCheckSanitizerOutput(
829   const std::string& str, std::string& log, std::vector<int>& result)
830 {
831   std::string regex;
832   switch (this->MemoryTesterStyle) {
833     case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
834       regex = "ERROR: AddressSanitizer: (.*) on.*";
835       break;
836     case cmCTestMemCheckHandler::LEAK_SANITIZER:
837       // use leakWarning regex
838       break;
839     case cmCTestMemCheckHandler::THREAD_SANITIZER:
840       regex = "WARNING: ThreadSanitizer: (.*) \\(pid=.*\\)";
841       break;
842     case cmCTestMemCheckHandler::MEMORY_SANITIZER:
843       regex = "WARNING: MemorySanitizer: (.*)";
844       break;
845     case cmCTestMemCheckHandler::UB_SANITIZER:
846       regex = "runtime error: (.*)";
847       break;
848     default:
849       break;
850   }
851   cmsys::RegularExpression sanitizerWarning(regex);
852   cmsys::RegularExpression leakWarning("(Direct|Indirect) leak of .*");
853   int defects = 0;
854   std::vector<std::string> lines;
855   cmsys::SystemTools::Split(str, lines);
856   std::ostringstream ostr;
857   log.clear();
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);
864     }
865     if (!resultFound.empty()) {
866       std::vector<int>::size_type idx = this->FindOrAddWarning(resultFound);
867       if (result.empty() || idx > result.size() - 1) {
868         result.push_back(1);
869       } else {
870         result[idx]++;
871       }
872       defects++;
873       ostr << "<b>" << this->ResultStrings[idx] << "</b> ";
874     }
875     ostr << l << std::endl;
876   }
877   log = ostr.str();
878   this->DefectCount += defects;
879   return defects == 0;
880 }
881 bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
882   const std::string& str, std::string& log, std::vector<int>& results)
883 {
884   std::vector<std::string> lines;
885   cmsys::SystemTools::Split(str, lines);
886   std::ostringstream ostr;
887   log.clear();
888
889   cmsys::RegularExpression pfW("^\\[[WEI]\\] ([A-Z][A-Z][A-Z][A-Z]*): ");
890
891   int defects = 0;
892
893   for (std::string const& l : lines) {
894     std::vector<int>::size_type failure = this->ResultStrings.size();
895     if (pfW.find(l)) {
896       std::vector<int>::size_type cc;
897       for (cc = 0; cc < this->ResultStrings.size(); cc++) {
898         if (pfW.match(1) == this->ResultStrings[cc]) {
899           failure = cc;
900           break;
901         }
902       }
903       if (cc == this->ResultStrings.size()) {
904         cmCTestLog(this->CTest, ERROR_MESSAGE,
905                    "Unknown Purify memory fault: " << pfW.match(1)
906                                                    << std::endl);
907         ostr << "*** Unknown Purify memory fault: " << pfW.match(1)
908              << std::endl;
909       }
910     }
911     if (failure != this->ResultStrings.size()) {
912       ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
913       results[failure]++;
914       defects++;
915     }
916     ostr << l << std::endl;
917   }
918
919   log = ostr.str();
920   this->DefectCount += defects;
921   return defects == 0;
922 }
923
924 bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
925   const std::string& str, std::string& log, std::vector<int>& results)
926 {
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;
933   }
934
935   std::string::size_type cc;
936
937   std::ostringstream ostr;
938   log.clear();
939
940   int defects = 0;
941
942   cmsys::RegularExpression valgrindLine("^==[0-9][0-9]*==");
943
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);
984
985     if (valgrindLine.find(lines[cc])) {
986       cmCTestOptionalLog(this->CTest, DEBUG,
987                          "valgrind  line " << lines[cc] << std::endl,
988                          this->Quiet);
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;
1010       }
1011
1012       if (failure != cmCTestMemCheckHandler::NO_MEMORY_FAULT) {
1013         ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
1014         results[failure]++;
1015         defects++;
1016       }
1017       totalOutputSize += lines[cc].size();
1018       ostr << lines[cc] << std::endl;
1019     } else {
1020       nonValGrindOutput.push_back(cc);
1021     }
1022   }
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
1025   // limiting code
1026   for (std::string::size_type i : nonValGrindOutput) {
1027     totalOutputSize += lines[i].size();
1028     ostr << lines[i] << std::endl;
1029     if (!unlimitedOutput &&
1030         totalOutputSize >
1031           static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)) {
1032       ostr << "....\n";
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
1038     }
1039   }
1040   cmCTestOptionalLog(this->CTest, DEBUG,
1041                      "End test (elapsed: "
1042                        << cmDurationTo<unsigned int>(
1043                             std::chrono::steady_clock::now() - sttime)
1044                        << "s)" << std::endl,
1045                      this->Quiet);
1046   log = ostr.str();
1047   this->DefectCount += defects;
1048   return defects == 0;
1049 }
1050
1051 bool cmCTestMemCheckHandler::ProcessMemCheckDrMemoryOutput(
1052   const std::string& str, std::string& log, std::vector<int>& results)
1053 {
1054   std::vector<std::string> lines;
1055   cmsys::SystemTools::Split(str, lines);
1056
1057   cmsys::RegularExpression drMemoryError("^Error #[0-9]+");
1058
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");
1064
1065   int defects = 0;
1066
1067   std::ostringstream ostr;
1068   for (const auto& l : lines) {
1069     ostr << l << std::endl;
1070     if (drMemoryError.find(l)) {
1071       defects++;
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]++;
1078       }
1079     }
1080   }
1081
1082   log = ostr.str();
1083
1084   this->DefectCount += defects;
1085   return defects == 0;
1086 }
1087
1088 bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
1089   const std::string& str, std::string& log, std::vector<int>& results)
1090 {
1091   log.clear();
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) {
1100       break;
1101     }
1102   }
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
1109       // correctly by BC
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);
1115       }
1116     }
1117   }
1118   int defects = 0;
1119   for (int err : parser.Errors) {
1120     results[err]++;
1121     defects++;
1122   }
1123   cmCTestOptionalLog(this->CTest, DEBUG,
1124                      "End test (elapsed: "
1125                        << cmDurationTo<unsigned int>(
1126                             std::chrono::steady_clock::now() - sttime)
1127                        << "s)" << std::endl,
1128                      this->Quiet);
1129   if (defects) {
1130     // only put the output of Bounds Checker if there were
1131     // errors or leaks detected
1132     log = parser.Log;
1133   }
1134   this->DefectCount += defects;
1135   return defects == 0;
1136 }
1137
1138 bool cmCTestMemCheckHandler::ProcessMemCheckCudaOutput(
1139   const std::string& str, std::string& log, std::vector<int>& results)
1140 {
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;
1147   }
1148
1149   std::string::size_type cc;
1150
1151   std::ostringstream ostr;
1152   log.clear();
1153
1154   int defects = 0;
1155
1156   cmsys::RegularExpression memcheckLine("^========");
1157
1158   cmsys::RegularExpression leakExpr("== Leaked [0-9,]+ bytes at");
1159
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{
1164     // API errors
1165     "== Malloc/Free error encountered: (.*)",
1166     "== Program hit error ([^ ]*).* on CUDA API call to",
1167     "== Program hit ([^ ]*).* on CUDA API call to",
1168     // memcheck
1169     "== (Invalid .*) of size [0-9,]+", "== (Fatal UVM [CG]PU fault)",
1170     // racecheck
1171     "== .* (Potential .* hazard detected)", "== .* (Race reported)",
1172     // synccheck
1173     "== (Barrier error)",
1174     // initcheck
1175     "== (Uninitialized .* memory read)", "== (Unused memory)",
1176     "== (Host API memory access error)",
1177     // generic error: ignore ERROR SUMMARY, CUDA-MEMCHECK and others
1178     "== ([A-Z][a-z].*)"
1179   };
1180
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);
1189
1190     if (memcheckLine.find(lines[cc])) {
1191       cmCTestOptionalLog(this->CTest, DEBUG,
1192                          "cuda sanitizer line " << lines[cc] << std::endl,
1193                          this->Quiet);
1194       int failure = -1;
1195       auto& line = lines[cc];
1196       if (leakExpr.find(line)) {
1197         failure = static_cast<int>(this->FindOrAddWarning("Memory leak"));
1198       } else {
1199         for (auto& matcher : matchers) {
1200           if (matcher.find(line)) {
1201             failure =
1202               static_cast<int>(this->FindOrAddWarning(matcher.match(1)));
1203             break;
1204           }
1205         }
1206       }
1207
1208       if (failure >= 0) {
1209         ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
1210         if (results.empty() ||
1211             static_cast<unsigned>(failure) > results.size() - 1) {
1212           results.push_back(1);
1213         } else {
1214           results[failure]++;
1215         }
1216         defects++;
1217       }
1218       totalOutputSize += lines[cc].size();
1219       ostr << lines[cc] << std::endl;
1220     } else {
1221       nonMemcheckOutput.push_back(cc);
1222     }
1223   }
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
1226   // limiting code
1227   for (std::string::size_type i : nonMemcheckOutput) {
1228     totalOutputSize += lines[i].size();
1229     ostr << lines[i] << std::endl;
1230     if (!unlimitedOutput &&
1231         totalOutputSize >
1232           static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)) {
1233       ostr << "....\n";
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
1239     }
1240   }
1241   cmCTestOptionalLog(this->CTest, DEBUG,
1242                      "End test (elapsed: "
1243                        << cmDurationTo<unsigned int>(
1244                             std::chrono::steady_clock::now() - sttime)
1245                        << "s)" << std::endl,
1246                      this->Quiet);
1247   log = ostr.str();
1248   this->DefectCount += defects;
1249   return defects == 0;
1250 }
1251
1252 // PostProcessTest memcheck results
1253 void cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res, int test)
1254 {
1255   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1256                      "PostProcessTest memcheck results for : " << res.Name
1257                                                                << std::endl,
1258                      this->Quiet);
1259   if (this->MemoryTesterStyle == cmCTestMemCheckHandler::BOUNDS_CHECKER) {
1260     this->PostProcessBoundsCheckerTest(res, test);
1261   } else if (this->MemoryTesterStyle == cmCTestMemCheckHandler::DRMEMORY) {
1262     this->PostProcessDrMemoryTest(res, test);
1263   } else {
1264     std::vector<std::string> files;
1265     this->TestOutputFileNames(test, files);
1266     for (std::string const& f : files) {
1267       this->AppendMemTesterOutput(res, f);
1268     }
1269   }
1270 }
1271
1272 // This method puts the bounds checker output file into the output
1273 // for the test
1274 void cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(
1275   cmCTestTestResult& res, int test)
1276 {
1277   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1278                      "PostProcessBoundsCheckerTest for : " << res.Name
1279                                                            << std::endl,
1280                      this->Quiet);
1281   std::vector<std::string> files;
1282   this->TestOutputFileNames(test, files);
1283   if (files.empty()) {
1284     return;
1285   }
1286   std::string ofile = files[0];
1287   if (ofile.empty()) {
1288     return;
1289   }
1290   // put a scope around this to close ifs so the file can be removed
1291   {
1292     cmsys::ifstream ifs(ofile.c_str());
1293     if (!ifs) {
1294       std::string log = "Cannot read memory tester output file: " + ofile;
1295       cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
1296       return;
1297     }
1298     res.Output += BOUNDS_CHECKER_MARKER;
1299     res.Output += "\n";
1300     std::string line;
1301     while (cmSystemTools::GetLineFromStream(ifs, line)) {
1302       res.Output += line;
1303       res.Output += "\n";
1304     }
1305   }
1306   cmSystemTools::Delay(1000);
1307   cmSystemTools::RemoveFile(this->BoundsCheckerDPBDFile);
1308   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1309                      "Remove: " << this->BoundsCheckerDPBDFile << std::endl,
1310                      this->Quiet);
1311   cmSystemTools::RemoveFile(this->BoundsCheckerXMLFile);
1312   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1313                      "Remove: " << this->BoundsCheckerXMLFile << std::endl,
1314                      this->Quiet);
1315 }
1316
1317 void cmCTestMemCheckHandler::PostProcessDrMemoryTest(
1318   cmCTestTestHandler::cmCTestTestResult& res, int test)
1319 {
1320   std::string drMemoryLogDir = this->MemoryTesterOutputFile.substr(
1321     0, this->MemoryTesterOutputFile.find("/*/results.txt"));
1322
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));
1327   }
1328
1329   cmsys::Glob g;
1330   g.FindFiles(drMemoryLogDir + "/resfile.*");
1331   const std::vector<std::string>& files = g.GetFiles();
1332
1333   for (const std::string& f : files) {
1334     cmsys::ifstream ifs(f.c_str());
1335     if (!ifs) {
1336       std::string log = "Cannot read memory tester output file: " + f;
1337       cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
1338       return;
1339     }
1340     std::string resultFileLocation;
1341     cmSystemTools::GetLineFromStream(ifs, resultFileLocation);
1342     this->AppendMemTesterOutput(res, resultFileLocation);
1343     ifs.close();
1344     cmSystemTools::RemoveFile(f);
1345   }
1346 }
1347
1348 void cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res,
1349                                                    std::string const& ofile)
1350 {
1351   if (ofile.empty()) {
1352     return;
1353   }
1354   // put ifs in scope so file can be deleted if needed
1355   {
1356     cmsys::ifstream ifs(ofile.c_str());
1357     if (!ifs) {
1358       std::string log = "Cannot read memory tester output file: " + ofile;
1359       cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
1360       return;
1361     }
1362     std::string line;
1363     while (cmSystemTools::GetLineFromStream(ifs, line)) {
1364       res.Output += line;
1365       res.Output += "\n";
1366     }
1367   }
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
1375                                       << "\n",
1376                          this->Quiet);
1377     }
1378   }
1379 }
1380
1381 void cmCTestMemCheckHandler::TestOutputFileNames(
1382   int test, std::vector<std::string>& files)
1383 {
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) {
1389     ofile += ".*";
1390     cmsys::Glob g;
1391     g.FindFiles(ofile);
1392     if (g.GetFiles().empty()) {
1393       std::string log = "Cannot find memory tester output file: " + ofile;
1394       cmCTestLog(this->CTest, WARNING, log << std::endl);
1395       ofile.clear();
1396     } else {
1397       files = g.GetFiles();
1398       return;
1399     }
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);
1403     ofile.clear();
1404   }
1405   files.push_back(std::move(ofile));
1406 }