Imported Upstream version 3.25.0
[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   // matchers for messages that aren't defects, but caught by above matchers
1181   std::vector<cmsys::RegularExpression> false_positive_matchers{
1182     "== Error: No attachable process found.*timed-out",
1183     "== Default timeout can be adjusted with --launch-timeout",
1184     "== Error: Target application terminated before first instrumented API",
1185     "== Tracking kernels launched by child processes requires"
1186   };
1187
1188   std::vector<std::string::size_type> nonMemcheckOutput;
1189   auto sttime = std::chrono::steady_clock::now();
1190   cmCTestOptionalLog(this->CTest, DEBUG,
1191                      "Start test: " << lines.size() << std::endl, this->Quiet);
1192   std::string::size_type totalOutputSize = 0;
1193   for (cc = 0; cc < lines.size(); cc++) {
1194     cmCTestOptionalLog(this->CTest, DEBUG,
1195                        "test line " << lines[cc] << std::endl, this->Quiet);
1196
1197     if (memcheckLine.find(lines[cc])) {
1198       cmCTestOptionalLog(this->CTest, DEBUG,
1199                          "cuda sanitizer line " << lines[cc] << std::endl,
1200                          this->Quiet);
1201       int failure = -1;
1202       auto& line = lines[cc];
1203       if (leakExpr.find(line)) {
1204         failure = static_cast<int>(this->FindOrAddWarning("Memory leak"));
1205       } else {
1206         auto match_predicate =
1207           [&line](cmsys::RegularExpression& matcher) -> bool {
1208           return matcher.find(line);
1209         };
1210         auto const pos_matcher =
1211           std::find_if(matchers.begin(), matchers.end(), match_predicate);
1212         if (pos_matcher != matchers.end()) {
1213           if (!std::any_of(false_positive_matchers.begin(),
1214                            false_positive_matchers.end(), match_predicate)) {
1215             failure =
1216               static_cast<int>(this->FindOrAddWarning(pos_matcher->match(1)));
1217           }
1218         }
1219       }
1220
1221       if (failure >= 0) {
1222         ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
1223         if (results.empty() ||
1224             static_cast<unsigned>(failure) > results.size() - 1) {
1225           results.push_back(1);
1226         } else {
1227           results[failure]++;
1228         }
1229         defects++;
1230       }
1231       totalOutputSize += lines[cc].size();
1232       ostr << lines[cc] << std::endl;
1233     } else {
1234       nonMemcheckOutput.push_back(cc);
1235     }
1236   }
1237   // Now put all all the non cuda sanitizer output into the test output
1238   // This should be last in case it gets truncated by the output
1239   // limiting code
1240   for (std::string::size_type i : nonMemcheckOutput) {
1241     totalOutputSize += lines[i].size();
1242     ostr << lines[i] << std::endl;
1243     if (!unlimitedOutput &&
1244         totalOutputSize >
1245           static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)) {
1246       ostr << "....\n";
1247       ostr << "Test Output for this test has been truncated see testing"
1248               " machine logs for full output,\n";
1249       ostr << "or put CTEST_FULL_OUTPUT in the output of "
1250               "this test program.\n";
1251       break; // stop the copy of output if we are full
1252     }
1253   }
1254   cmCTestOptionalLog(this->CTest, DEBUG,
1255                      "End test (elapsed: "
1256                        << cmDurationTo<unsigned int>(
1257                             std::chrono::steady_clock::now() - sttime)
1258                        << "s)" << std::endl,
1259                      this->Quiet);
1260   log = ostr.str();
1261   this->DefectCount += defects;
1262   return defects == 0;
1263 }
1264
1265 // PostProcessTest memcheck results
1266 void cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res, int test)
1267 {
1268   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1269                      "PostProcessTest memcheck results for : " << res.Name
1270                                                                << std::endl,
1271                      this->Quiet);
1272   if (this->MemoryTesterStyle == cmCTestMemCheckHandler::BOUNDS_CHECKER) {
1273     this->PostProcessBoundsCheckerTest(res, test);
1274   } else if (this->MemoryTesterStyle == cmCTestMemCheckHandler::DRMEMORY) {
1275     this->PostProcessDrMemoryTest(res, test);
1276   } else {
1277     std::vector<std::string> files;
1278     this->TestOutputFileNames(test, files);
1279     for (std::string const& f : files) {
1280       this->AppendMemTesterOutput(res, f);
1281     }
1282   }
1283 }
1284
1285 // This method puts the bounds checker output file into the output
1286 // for the test
1287 void cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(
1288   cmCTestTestResult& res, int test)
1289 {
1290   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1291                      "PostProcessBoundsCheckerTest for : " << res.Name
1292                                                            << std::endl,
1293                      this->Quiet);
1294   std::vector<std::string> files;
1295   this->TestOutputFileNames(test, files);
1296   if (files.empty()) {
1297     return;
1298   }
1299   std::string ofile = files[0];
1300   if (ofile.empty()) {
1301     return;
1302   }
1303   // put a scope around this to close ifs so the file can be removed
1304   {
1305     cmsys::ifstream ifs(ofile.c_str());
1306     if (!ifs) {
1307       std::string log = "Cannot read memory tester output file: " + ofile;
1308       cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
1309       return;
1310     }
1311     res.Output += BOUNDS_CHECKER_MARKER;
1312     res.Output += "\n";
1313     std::string line;
1314     while (cmSystemTools::GetLineFromStream(ifs, line)) {
1315       res.Output += line;
1316       res.Output += "\n";
1317     }
1318   }
1319   cmSystemTools::Delay(1000);
1320   cmSystemTools::RemoveFile(this->BoundsCheckerDPBDFile);
1321   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1322                      "Remove: " << this->BoundsCheckerDPBDFile << std::endl,
1323                      this->Quiet);
1324   cmSystemTools::RemoveFile(this->BoundsCheckerXMLFile);
1325   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1326                      "Remove: " << this->BoundsCheckerXMLFile << std::endl,
1327                      this->Quiet);
1328 }
1329
1330 void cmCTestMemCheckHandler::PostProcessDrMemoryTest(
1331   cmCTestTestHandler::cmCTestTestResult& res, int test)
1332 {
1333   std::string drMemoryLogDir = this->MemoryTesterOutputFile.substr(
1334     0, this->MemoryTesterOutputFile.find("/*/results.txt"));
1335
1336   // replace placeholder of test
1337   std::string::size_type pos = drMemoryLogDir.find("??");
1338   if (pos != std::string::npos) {
1339     drMemoryLogDir.replace(pos, 2, std::to_string(test));
1340   }
1341
1342   cmsys::Glob g;
1343   g.FindFiles(drMemoryLogDir + "/resfile.*");
1344   const std::vector<std::string>& files = g.GetFiles();
1345
1346   for (const std::string& f : files) {
1347     cmsys::ifstream ifs(f.c_str());
1348     if (!ifs) {
1349       std::string log = "Cannot read memory tester output file: " + f;
1350       cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
1351       return;
1352     }
1353     std::string resultFileLocation;
1354     cmSystemTools::GetLineFromStream(ifs, resultFileLocation);
1355     this->AppendMemTesterOutput(res, resultFileLocation);
1356     ifs.close();
1357     cmSystemTools::RemoveFile(f);
1358   }
1359 }
1360
1361 void cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res,
1362                                                    std::string const& ofile)
1363 {
1364   if (ofile.empty()) {
1365     return;
1366   }
1367   // put ifs in scope so file can be deleted if needed
1368   {
1369     cmsys::ifstream ifs(ofile.c_str());
1370     if (!ifs) {
1371       std::string log = "Cannot read memory tester output file: " + ofile;
1372       cmCTestLog(this->CTest, ERROR_MESSAGE, log << std::endl);
1373       return;
1374     }
1375     std::string line;
1376     while (cmSystemTools::GetLineFromStream(ifs, line)) {
1377       res.Output += line;
1378       res.Output += "\n";
1379     }
1380   }
1381   if (this->LogWithPID) {
1382     auto pos = ofile.find_last_of('.');
1383     if (pos != std::string::npos) {
1384       auto ofileWithoutPid = ofile.substr(0, pos);
1385       cmSystemTools::RenameFile(ofile, ofileWithoutPid);
1386       cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
1387                          "Renaming: " << ofile << " to: " << ofileWithoutPid
1388                                       << "\n",
1389                          this->Quiet);
1390     }
1391   }
1392 }
1393
1394 void cmCTestMemCheckHandler::TestOutputFileNames(
1395   int test, std::vector<std::string>& files)
1396 {
1397   std::string index = std::to_string(test);
1398   std::string ofile = this->MemoryTesterOutputFile;
1399   std::string::size_type pos = ofile.find("??");
1400   ofile.replace(pos, 2, index);
1401   if (this->LogWithPID) {
1402     ofile += ".*";
1403     cmsys::Glob g;
1404     g.FindFiles(ofile);
1405     if (g.GetFiles().empty()) {
1406       std::string log = "Cannot find memory tester output file: " + ofile;
1407       cmCTestLog(this->CTest, WARNING, log << std::endl);
1408       ofile.clear();
1409     } else {
1410       files = g.GetFiles();
1411       return;
1412     }
1413   } else if (!cmSystemTools::FileExists(ofile)) {
1414     std::string log = "Cannot find memory tester output file: " + ofile;
1415     cmCTestLog(this->CTest, WARNING, log << std::endl);
1416     ofile.clear();
1417   }
1418   files.push_back(std::move(ofile));
1419 }