Ver Fonte

Merge topic 'thread-sanitizer'

7c80ce6f Help: Add notes for topic 'thread-sanitizer'
49948f72 ctest_memcheck: Add support for ThreadSanitizer
Brad King há 11 anos atrás
pai
commit
55d3e88fb7

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -344,6 +344,7 @@ Variables for CTest
    /variable/CTEST_MEMORYCHECK_COMMAND
    /variable/CTEST_MEMORYCHECK_COMMAND_OPTIONS
    /variable/CTEST_MEMORYCHECK_SUPPRESSIONS_FILE
+   /variable/CTEST_MEMORYCHECK_TYPE
    /variable/CTEST_NIGHTLY_START_TIME
    /variable/CTEST_P4_CLIENT
    /variable/CTEST_P4_COMMAND

+ 5 - 0
Help/release/dev/thread-sanitizer.rst

@@ -0,0 +1,5 @@
+thread-sanitizer
+----------------
+
+* The :command:`ctest_memcheck` command learned to support
+  ``ThreadSanitizer``.

+ 6 - 0
Help/variable/CTEST_MEMORYCHECK_TYPE.rst

@@ -0,0 +1,6 @@
+CTEST_MEMORYCHECK_TYPE
+-------------------------
+
+Specify the CTest ``MemoryCheckType`` setting
+in a :manual:`ctest(1)` dashboard client script.
+Valid values are Valgrind, Purify, BoundsChecker, and ThreadSanitizer.

+ 2 - 0
Source/CTest/cmCTestMemCheckCommand.cxx

@@ -20,6 +20,8 @@ cmCTestGenericHandler* cmCTestMemCheckCommand::InitializeActualHandler()
   cmCTestGenericHandler* handler
     = this->CTest->GetInitializedHandler("memcheck");
 
+  this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
+    "MemoryCheckType", "CTEST_MEMORYCHECK_TYPE");
   this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
     "MemoryCheckCommand", "CTEST_MEMORYCHECK_COMMAND");
   this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,

+ 306 - 133
Source/CTest/cmCTestMemCheckHandler.cxx

@@ -18,6 +18,7 @@
 #include <cmsys/Process.h>
 #include <cmsys/RegularExpression.hxx>
 #include <cmsys/Base64.h>
+#include <cmsys/Glob.hxx>
 #include <cmsys/FStream.hxx>
 #include "cmMakefile.h"
 #include "cmXMLSafe.h"
@@ -124,60 +125,7 @@ public:
 
 #define BOUNDS_CHECKER_MARKER \
 "******######*****Begin BOUNDS CHECKER XML******######******"
-//----------------------------------------------------------------------
-static const char* cmCTestMemCheckResultStrings[] = {
-  "ABR",
-  "ABW",
-  "ABWL",
-  "COR",
-  "EXU",
-  "FFM",
-  "FIM",
-  "FMM",
-  "FMR",
-  "FMW",
-  "FUM",
-  "IPR",
-  "IPW",
-  "MAF",
-  "MLK",
-  "MPK",
-  "NPR",
-  "ODS",
-  "PAR",
-  "PLK",
-  "UMC",
-  "UMR",
-  0
-};
-
 
-//----------------------------------------------------------------------
-static const char* cmCTestMemCheckResultLongStrings[] = {
-  "Threading Problem",
-  "ABW",
-  "ABWL",
-  "COR",
-  "EXU",
-  "FFM",
-  "FIM",
-  "Mismatched deallocation",
-  "FMR",
-  "FMW",
-  "FUM",
-  "IPR",
-  "IPW",
-  "MAF",
-  "Memory Leak",
-  "Potential Memory Leak",
-  "NPR",
-  "ODS",
-  "Invalid syscall param",
-  "PLK",
-  "Uninitialized Memory Conditional",
-  "Uninitialized Memory Read",
-  0
-};
 
 
 //----------------------------------------------------------------------
@@ -186,12 +134,14 @@ cmCTestMemCheckHandler::cmCTestMemCheckHandler()
   this->MemCheck = true;
   this->CustomMaximumPassedTestOutputSize = 0;
   this->CustomMaximumFailedTestOutputSize = 0;
+  this->LogWithPID = false;
 }
 
 //----------------------------------------------------------------------
 void cmCTestMemCheckHandler::Initialize()
 {
   this->Superclass::Initialize();
+  this->LogWithPID = false;
   this->CustomMaximumPassedTestOutputSize = 0;
   this->CustomMaximumFailedTestOutputSize = 0;
   this->MemoryTester = "";
@@ -199,12 +149,6 @@ void cmCTestMemCheckHandler::Initialize()
   this->MemoryTesterOptions.clear();
   this->MemoryTesterStyle = UNKNOWN;
   this->MemoryTesterOutputFile = "";
-  int cc;
-  for ( cc = 0; cc < NO_MEMORY_FAULT; cc ++ )
-    {
-    this->MemoryTesterGlobalResults[cc] = 0;
-    }
-
 }
 
 //----------------------------------------------------------------------
@@ -249,8 +193,8 @@ void cmCTestMemCheckHandler::GenerateTestCommand(
   index = stream.str();
   for ( pp = 0; pp < this->MemoryTesterDynamicOptions.size(); pp ++ )
     {
-      std::string arg = this->MemoryTesterDynamicOptions[pp];
-      std::string::size_type pos = arg.find("??");
+    std::string arg = this->MemoryTesterDynamicOptions[pp];
+    std::string::size_type pos = arg.find("??");
     if (pos != std::string::npos)
       {
       arg.replace(pos, 2, index);
@@ -260,17 +204,124 @@ void cmCTestMemCheckHandler::GenerateTestCommand(
     memcheckcommand += arg;
     memcheckcommand += "\"";
     }
+  // Create a copy of the memory tester environment variable.
+  // This is used for memory testing programs that pass options
+  // via environment varaibles.
+  std::string memTesterEnvironmentVariable =
+    this->MemoryTesterEnvironmentVariable;
   for ( pp = 0; pp < this->MemoryTesterOptions.size(); pp ++ )
     {
-    args.push_back(this->MemoryTesterOptions[pp]);
-    memcheckcommand += " \"";
-    memcheckcommand += this->MemoryTesterOptions[pp];
-    memcheckcommand += "\"";
+    if(memTesterEnvironmentVariable.size())
+      {
+      // If we are using env to pass options, append all the options to
+      // this string with space separation.
+      memTesterEnvironmentVariable += " " + this->MemoryTesterOptions[pp];
+      }
+    // for regular options just add them to args and memcheckcommand
+    // which is just used for display
+    else
+      {
+      args.push_back(this->MemoryTesterOptions[pp]);
+      memcheckcommand += " \"";
+      memcheckcommand += this->MemoryTesterOptions[pp];
+      memcheckcommand += "\"";
+      }
+    }
+  // if this is an env option type, then add the env string as a single
+  // argument.
+  if(memTesterEnvironmentVariable.size())
+    {
+    std::string::size_type pos = memTesterEnvironmentVariable.find("??");
+    if (pos != std::string::npos)
+      {
+      memTesterEnvironmentVariable.replace(pos, 2, index);
+      }
+    memcheckcommand += " " + memTesterEnvironmentVariable;
+    args.push_back(memTesterEnvironmentVariable);
     }
   cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Memory check command: "
     << memcheckcommand << std::endl);
 }
 
+//----------------------------------------------------------------------
+void cmCTestMemCheckHandler::InitializeResultsVectors()
+{
+  // fill these members
+//  cmsys::vector<std::string> ResultStrings;
+//  cmsys::vector<std::string> ResultStringsLong;
+//  cmsys::vector<int>         GlobalResults;
+  this->ResultStringsLong.clear();
+  this->ResultStrings.clear();
+  this->GlobalResults.clear();
+  // If we are working with style checkers that dynamically fill
+  // the results strings then return.
+  if(this->MemoryTesterStyle > cmCTestMemCheckHandler::BOUNDS_CHECKER)
+    {
+    return;
+    }
+
+  // define the standard set of errors
+  //----------------------------------------------------------------------
+  static const char* cmCTestMemCheckResultStrings[] = {
+    "ABR",
+    "ABW",
+    "ABWL",
+    "COR",
+    "EXU",
+    "FFM",
+    "FIM",
+    "FMM",
+    "FMR",
+    "FMW",
+    "FUM",
+    "IPR",
+    "IPW",
+    "MAF",
+    "MLK",
+    "MPK",
+    "NPR",
+    "ODS",
+    "PAR",
+    "PLK",
+    "UMC",
+    "UMR",
+    0
+  };
+//----------------------------------------------------------------------
+  static const char* cmCTestMemCheckResultLongStrings[] = {
+    "Threading Problem",
+    "ABW",
+    "ABWL",
+    "COR",
+    "EXU",
+    "FFM",
+    "FIM",
+    "Mismatched deallocation",
+    "FMR",
+    "FMW",
+    "FUM",
+    "IPR",
+    "IPW",
+    "MAF",
+    "Memory Leak",
+    "Potential Memory Leak",
+    "NPR",
+    "ODS",
+    "Invalid syscall param",
+    "PLK",
+    "Uninitialized Memory Conditional",
+    "Uninitialized Memory Read",
+    0
+  };
+  this->GlobalResults.clear();
+  for(int i =0; cmCTestMemCheckResultStrings[i] != 0; ++i)
+    {
+    this->ResultStrings.push_back(cmCTestMemCheckResultStrings[i]);
+    this->ResultStringsLong.push_back(cmCTestMemCheckResultLongStrings[i]);
+    this->GlobalResults.push_back(0);
+    }
+}
+
 //----------------------------------------------------------------------
 void cmCTestMemCheckHandler::PopulateCustomVectors(cmMakefile *mf)
 {
@@ -283,6 +334,8 @@ void cmCTestMemCheckHandler::PopulateCustomVectors(cmMakefile *mf)
   this->CTest->PopulateCustomVector(mf,
                              "CTEST_CUSTOM_MEMCHECK_IGNORE",
                              this->CustomTestsIgnore);
+  this->CTest->SetCTestConfigurationFromCMakeVariable(
+    mf, "CMakeCommand", "CMAKE_COMMAND");
 }
 
 //----------------------------------------------------------------------
@@ -292,7 +345,6 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os)
     {
     return;
     }
-
   this->CTest->StartXML(os, this->AppendXML);
   os << "<DynamicAnalysis Checker=\"";
   switch ( this->MemoryTesterStyle )
@@ -306,6 +358,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os)
     case cmCTestMemCheckHandler::BOUNDS_CHECKER:
       os << "BoundsChecker";
       break;
+    case cmCTestMemCheckHandler::THREAD_SANITIZER:
+      os << "ThreadSanitizer";
+      break;
     default:
       os << "Unknown";
     }
@@ -333,8 +388,7 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os)
     {
     cmCTestTestResult *result = &this->TestResults[cc];
     std::string memcheckstr;
-    int memcheckresults[cmCTestMemCheckHandler::NO_MEMORY_FAULT];
-    int kk;
+    std::vector<int> memcheckresults(this->ResultStrings.size(), 0);
     bool res = this->ProcessMemCheckOutput(result->Output, memcheckstr,
       memcheckresults);
     if ( res && result->Status == cmCTestMemCheckHandler::COMPLETED )
@@ -345,16 +399,17 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os)
       static_cast<size_t>(this->CustomMaximumFailedTestOutputSize));
     this->WriteTestResultHeader(os, result);
     os << "\t\t<Results>" << std::endl;
-    for ( kk = 0; cmCTestMemCheckResultLongStrings[kk]; kk ++ )
+    for(std::vector<int>::size_type kk = 0;
+        kk < memcheckresults.size(); ++kk)
       {
       if ( memcheckresults[kk] )
         {
-        os << "\t\t\t<Defect type=\"" << cmCTestMemCheckResultLongStrings[kk]
+        os << "\t\t\t<Defect type=\"" << this->ResultStringsLong[kk]
           << "\">"
            << memcheckresults[kk]
            << "</Defect>" << std::endl;
         }
-      this->MemoryTesterGlobalResults[kk] += memcheckresults[kk];
+      this->GlobalResults[kk] += memcheckresults[kk];
       }
 
     std::string logTag;
@@ -383,9 +438,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os)
   cmCTestLog(this->CTest, HANDLER_OUTPUT, "Memory checking results:"
     << std::endl);
   os << "\t<DefectList>" << std::endl;
-  for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ )
+  for ( cc = 0; cc < this->GlobalResults.size(); cc ++ )
     {
-    if ( this->MemoryTesterGlobalResults[cc] )
+    if ( this->GlobalResults[cc] )
       {
 #ifdef cerr
 #  undef cerr
@@ -393,9 +448,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os)
       std::cerr.width(35);
 #define cerr no_cerr
       cmCTestLog(this->CTest, HANDLER_OUTPUT,
-        cmCTestMemCheckResultLongStrings[cc] << " - "
-        << this->MemoryTesterGlobalResults[cc] << std::endl);
-      os << "\t\t<Defect Type=\"" << cmCTestMemCheckResultLongStrings[cc]
+        this->ResultStringsLong[cc] << " - "
+        << this->GlobalResults[cc] << std::endl);
+      os << "\t\t<Defect Type=\"" << this->ResultStringsLong[cc]
         << "\"/>" << std::endl;
       }
     }
@@ -410,13 +465,13 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os)
 
   os << "</DynamicAnalysis>" << std::endl;
   this->CTest->EndXML(os);
-
-
 }
 
 //----------------------------------------------------------------------
 bool cmCTestMemCheckHandler::InitializeMemoryChecking()
 {
+  this->MemoryTesterEnvironmentVariable = "";
+  this->MemoryTester = "";
   // Setup the command
   if ( cmSystemTools::FileExists(this->CTest->GetCTestConfiguration(
         "MemoryCheckCommand").c_str()) )
@@ -426,7 +481,9 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
     std::string testerName =
       cmSystemTools::GetFilenameName(this->MemoryTester);
     // determine the checker type
-    if ( testerName.find("valgrind") != std::string::npos )
+    if ( testerName.find("valgrind") != std::string::npos ||
+         this->CTest->GetCTestConfiguration("MemoryCheckType")
+         == "Valgrind")
       {
         this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
       }
@@ -464,12 +521,38 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
       = this->CTest->GetCTestConfiguration("BoundsCheckerCommand").c_str();
     this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
     }
-  else
+  if ( this->CTest->GetCTestConfiguration("MemoryCheckType")
+       == "ThreadSanitizer")
+    {
+    this->MemoryTester
+      = this->CTest->GetCTestConfiguration("CMakeCommand").c_str();
+    this->MemoryTesterStyle = cmCTestMemCheckHandler::THREAD_SANITIZER;
+    this->LogWithPID = true; // even if we give the log file the pid is added
+    }
+  // Check the MemoryCheckType
+  if(this->MemoryTesterStyle == cmCTestMemCheckHandler::UNKNOWN)
+    {
+    std::string checkType =
+      this->CTest->GetCTestConfiguration("MemoryCheckType");
+    if(checkType == "Purify")
+      {
+      this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY;
+      }
+    else if(checkType == "BoundsChecker")
+      {
+      this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER;
+      }
+    else if(checkType == "Valgrind")
+      {
+      this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND;
+      }
+    }
+  if(this->MemoryTester.size() == 0 )
     {
     cmCTestLog(this->CTest, WARNING,
-      "Memory checker (MemoryCheckCommand) "
-      "not set, or cannot find the specified program."
-      << std::endl);
+               "Memory checker (MemoryCheckCommand) "
+               "not set, or cannot find the specified program."
+               << std::endl);
     return false;
     }
 
@@ -568,6 +651,20 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
       this->MemoryTesterOptions.push_back("/M");
       break;
       }
+    case cmCTestMemCheckHandler::THREAD_SANITIZER:
+      {
+      // To pass arguments to ThreadSanitizer the environment variable
+      // TSAN_OPTIONS is used. This is done with the cmake -E env command.
+      // The MemoryTesterDynamicOptions is setup with the -E env
+      // Then the MemoryTesterEnvironmentVariable gets the
+      // TSAN_OPTIONS string with the log_path in it.
+      this->MemoryTesterDynamicOptions.push_back("-E");
+      this->MemoryTesterDynamicOptions.push_back("env");
+      std::string outputFile = "TSAN_OPTIONS=log_path=\""
+        + this->MemoryTesterOutputFile + "\"";
+      this->MemoryTesterEnvironmentVariable = outputFile;
+      break;
+      }
     default:
       cmCTestLog(this->CTest, ERROR_MESSAGE,
         "Do not understand memory checker: " << this->MemoryTester
@@ -575,24 +672,20 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
       return false;
     }
 
-  std::vector<std::string>::size_type cc;
-  for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ )
-    {
-    this->MemoryTesterGlobalResults[cc] = 0;
-    }
+  this->InitializeResultsVectors();
+  // std::vector<std::string>::size_type cc;
+  // for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ )
+  //   {
+  //   this->MemoryTesterGlobalResults[cc] = 0;
+  //   }
   return true;
 }
 
 //----------------------------------------------------------------------
-bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
-                                               std::string& log, int* results)
+bool cmCTestMemCheckHandler::
+ProcessMemCheckOutput(const std::string& str,
+                      std::string& log, std::vector<int>& results)
 {
-  std::string::size_type cc;
-  for ( cc = 0; cc < cmCTestMemCheckHandler::NO_MEMORY_FAULT; cc ++ )
-    {
-    results[cc] = 0;
-    }
-
   if ( this->MemoryTesterStyle == cmCTestMemCheckHandler::VALGRIND )
     {
     return this->ProcessMemCheckValgrindOutput(str, log, results);
@@ -601,6 +694,11 @@ bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
     {
     return this->ProcessMemCheckPurifyOutput(str, log, results);
     }
+  else if ( this->MemoryTesterStyle ==
+            cmCTestMemCheckHandler::THREAD_SANITIZER )
+    {
+    return this->ProcessMemCheckThreadSanitizerOutput(str, log, results);
+    }
   else if ( this->MemoryTesterStyle ==
     cmCTestMemCheckHandler::BOUNDS_CHECKER )
     {
@@ -612,15 +710,68 @@ bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str,
     log.append("None that I know");
     log = str;
     }
-
-
   return true;
 }
 
+std::vector<int>::size_type cmCTestMemCheckHandler::FindOrAddWarning(
+  const std::string& warning)
+{
+  for(std::vector<std::string>::size_type i =0;
+      i < this->ResultStrings.size(); ++i)
+    {
+    if(this->ResultStrings[i] == warning)
+      {
+      return i;
+      }
+    }
+  this->GlobalResults.push_back(0); // this must stay the same size
+  this->ResultStrings.push_back(warning);
+  this->ResultStringsLong.push_back(warning);
+  return this->ResultStrings.size()-1;
+}
+//----------------------------------------------------------------------
+bool cmCTestMemCheckHandler::ProcessMemCheckThreadSanitizerOutput(
+  const std::string& str, std::string& log,
+  std::vector<int>& result)
+{
+  cmsys::RegularExpression
+    sanitizerWarning("WARNING: ThreadSanitizer: (.*) \\(pid=.*\\)");
+  int defects = 0;
+  std::vector<std::string> lines;
+  cmSystemTools::Split(str.c_str(), lines);
+  cmOStringStream ostr;
+  log = "";
+  for( std::vector<std::string>::iterator i = lines.begin();
+       i != lines.end(); ++i)
+    {
+    if(sanitizerWarning.find(*i))
+      {
+      std::string warning = sanitizerWarning.match(1);
+      std::vector<int>::size_type idx = this->FindOrAddWarning(warning);
+      if(result.size() == 0 || idx > result.size()-1)
+        {
+        result.push_back(1);
+        }
+      else
+        {
+        result[idx]++;
+        }
+      defects++;
+      ostr << "<b>" <<  this->ResultStrings[idx] << "</b> ";
+      }
+    ostr << cmXMLSafe(*i) << std::endl;
+    }
+  log = ostr.str();
+  if(defects)
+    {
+    return false;
+    }
+  return true;
+}
 //----------------------------------------------------------------------
 bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
   const std::string& str, std::string& log,
-  int* results)
+  std::vector<int>& results)
 {
   std::vector<std::string> lines;
   cmSystemTools::Split(str.c_str(), lines);
@@ -634,19 +785,19 @@ bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
   for( std::vector<std::string>::iterator i = lines.begin();
        i != lines.end(); ++i)
     {
-    int failure = cmCTestMemCheckHandler::NO_MEMORY_FAULT;
+    std::vector<int>::size_type failure = this->ResultStrings.size();
     if ( pfW.find(*i) )
       {
-      int cc;
-      for ( cc = 0; cc < cmCTestMemCheckHandler::NO_MEMORY_FAULT; cc ++ )
+      std::vector<int>::size_type cc;
+      for ( cc = 0; cc < this->ResultStrings.size(); cc ++ )
         {
-        if ( pfW.match(1) == cmCTestMemCheckResultStrings[cc] )
+        if ( pfW.match(1) == this->ResultStrings[cc] )
           {
           failure = cc;
           break;
           }
         }
-      if ( cc == cmCTestMemCheckHandler::NO_MEMORY_FAULT )
+      if ( cc == this->ResultStrings.size() )
         {
         cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown Purify memory fault: "
           << pfW.match(1) << std::endl);
@@ -654,9 +805,9 @@ bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
           << std::endl;
         }
       }
-    if ( failure != NO_MEMORY_FAULT )
+    if ( failure != this->ResultStrings.size() )
       {
-      ostr << "<b>" << cmCTestMemCheckResultStrings[failure] << "</b> ";
+      ostr << "<b>" <<  this->ResultStrings[failure] << "</b> ";
       results[failure] ++;
       defects ++;
       }
@@ -674,7 +825,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput(
 //----------------------------------------------------------------------
 bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
   const std::string& str, std::string& log,
-  int* results)
+  std::vector<int>& results)
 {
   std::vector<std::string> lines;
   cmSystemTools::Split(str.c_str(), lines);
@@ -803,7 +954,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
 
       if ( failure != cmCTestMemCheckHandler::NO_MEMORY_FAULT )
         {
-        ostr << "<b>" << cmCTestMemCheckResultStrings[failure] << "</b> ";
+        ostr << "<b>" << this->ResultStrings[failure] << "</b> ";
         results[failure] ++;
         defects ++;
         }
@@ -855,7 +1006,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput(
 //----------------------------------------------------------------------
 bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
   const std::string& str, std::string& log,
-  int* results)
+  std::vector<int>& results)
 {
   log = "";
   double sttime = cmSystemTools::GetTime();
@@ -909,6 +1060,26 @@ bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput(
   return true;
 }
 
+// PostProcessTest memcheck results
+void
+cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res,
+                                        int test)
+{
+  cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+             "PostProcessTest memcheck results for : "
+             << res.Name << std::endl);
+  if(this->MemoryTesterStyle
+     == cmCTestMemCheckHandler::BOUNDS_CHECKER)
+    {
+    this->PostProcessBoundsCheckerTest(res, test);
+    }
+  else
+    {
+    this->AppendMemTesterOutput(res, test);
+    }
+}
+
+
 // This method puts the bounds checker output file into the output
 // for the test
 void
@@ -950,36 +1121,17 @@ cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(cmCTestTestResult& res,
     << this->BoundsCheckerXMLFile << std::endl);
 }
 
-void
-cmCTestMemCheckHandler::PostProcessPurifyTest(cmCTestTestResult& res,
-                                              int test)
-{
-  cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
-             "PostProcessPurifyTest for : "
-             << res.Name << std::endl);
-  this->AppendMemTesterOutput(res, test);
-}
-
-void
-cmCTestMemCheckHandler::PostProcessValgrindTest(cmCTestTestResult& res,
-                                                int test)
-{
-  cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
-             "PostProcessValgrindTest for : "
-             << res.Name << std::endl);
-  this->AppendMemTesterOutput(res, test);
-}
-
 void
 cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res,
                                               int test)
 {
   std::string ofile = this->TestOutputFileName(test);
-
   if ( ofile.empty() )
     {
     return;
     }
+  // put ifs in scope so file can be deleted if needed
+  {
   cmsys::ifstream ifs(ofile.c_str());
   if ( !ifs )
     {
@@ -993,6 +1145,12 @@ cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res,
     res.Output += line;
     res.Output += "\n";
     }
+  }
+  if(this->LogWithPID)
+    {
+    cmSystemTools::RemoveFile(ofile.c_str());
+    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Remove: "<< ofile <<"\n");
+    }
 }
 
 std::string
@@ -1005,14 +1163,29 @@ cmCTestMemCheckHandler::TestOutputFileName(int test)
   std::string ofile = this->MemoryTesterOutputFile;
   std::string::size_type pos = ofile.find("??");
   ofile.replace(pos, 2, index);
-
-  if ( !cmSystemTools::FileExists(ofile.c_str()) )
+  if(this->LogWithPID)
+    {
+    ofile += ".*";
+    cmsys::Glob g;
+    g.FindFiles(ofile);
+    if(g.GetFiles().size() == 0)
+      {
+      std::string log = "Cannot find memory tester output file: "
+        + ofile;
+      cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
+      ofile = "";
+      }
+    else
+      {
+      ofile = g.GetFiles()[0];
+      }
+    }
+  else if ( !cmSystemTools::FileExists(ofile.c_str()) )
     {
     std::string log = "Cannot find memory tester output file: "
       + ofile;
     cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl);
     ofile = "";
     }
-
   return ofile;
 }

+ 28 - 8
Source/CTest/cmCTestMemCheckHandler.h

@@ -15,7 +15,10 @@
 
 
 #include "cmCTestTestHandler.h"
+#include "cmStandardIncludes.h"
 #include "cmListFileCache.h"
+#include <vector>
+#include <string>
 
 class cmMakefile;
 
@@ -45,7 +48,9 @@ private:
     UNKNOWN = 0,
     VALGRIND,
     PURIFY,
-    BOUNDS_CHECKER
+    BOUNDS_CHECKER,
+    // checkers after hear do not use the standard error list
+    THREAD_SANITIZER
   };
 public:
   enum { // Memory faults
@@ -93,7 +98,17 @@ private:
   std::vector<std::string> MemoryTesterOptions;
   int                      MemoryTesterStyle;
   std::string              MemoryTesterOutputFile;
-  int                      MemoryTesterGlobalResults[NO_MEMORY_FAULT];
+  std::string              MemoryTesterEnvironmentVariable;
+  // these are used to store the types of errors that can show up
+  std::vector<std::string> ResultStrings;
+  std::vector<std::string> ResultStringsLong;
+  std::vector<int>         GlobalResults;
+  bool                     LogWithPID; // does log file add pid
+
+  std::vector<int>::size_type FindOrAddWarning(const std::string& warning);
+  // initialize the ResultStrings and ResultStringsLong for
+  // this type of checker
+  void InitializeResultsVectors();
 
   ///! Initialize memory checking subsystem.
   bool InitializeMemoryChecking();
@@ -110,17 +125,22 @@ private:
   //string. After running, log holds the output and results hold the
   //different memmory errors.
   bool ProcessMemCheckOutput(const std::string& str,
-                             std::string& log, int* results);
+                             std::string& log, std::vector<int>& results);
   bool ProcessMemCheckValgrindOutput(const std::string& str,
-                                     std::string& log, int* results);
+                                     std::string& log,
+                                     std::vector<int>& results);
   bool ProcessMemCheckPurifyOutput(const std::string& str,
-                                   std::string& log, int* results);
+                                   std::string& log,
+                                   std::vector<int>& results);
+  bool ProcessMemCheckThreadSanitizerOutput(const std::string& str,
+                                            std::string& log,
+                                            std::vector<int>& results);
   bool ProcessMemCheckBoundsCheckerOutput(const std::string& str,
-                                          std::string& log, int* results);
+                                          std::string& log,
+                                          std::vector<int>& results);
 
-  void PostProcessPurifyTest(cmCTestTestResult& res, int test);
+  void PostProcessTest(cmCTestTestResult& res, int test);
   void PostProcessBoundsCheckerTest(cmCTestTestResult& res, int test);
-  void PostProcessValgrindTest(cmCTestTestResult& res, int test);
 
   ///! append MemoryTesterOutputFile to the test log
   void AppendMemTesterOutput(cmCTestTestHandler::cmCTestTestResult& res,

+ 1 - 14
Source/CTest/cmCTestRunTest.cxx

@@ -392,20 +392,7 @@ void cmCTestRunTest::MemCheckPostProcess()
              << this->TestResult.Name << std::endl);
   cmCTestMemCheckHandler * handler = static_cast<cmCTestMemCheckHandler*>
     (this->TestHandler);
-  switch ( handler->MemoryTesterStyle )
-    {
-    case cmCTestMemCheckHandler::VALGRIND:
-      handler->PostProcessValgrindTest(this->TestResult, this->Index);
-      break;
-    case cmCTestMemCheckHandler::PURIFY:
-      handler->PostProcessPurifyTest(this->TestResult, this->Index);
-      break;
-    case cmCTestMemCheckHandler::BOUNDS_CHECKER:
-      handler->PostProcessBoundsCheckerTest(this->TestResult, this->Index);
-      break;
-    default:
-      break;
-    }
+  handler->PostProcessTest(this->TestResult, this->Index);
 }
 
 //----------------------------------------------------------------------

+ 18 - 0
Tests/CTestTestMemcheck/CMakeLists.txt

@@ -103,6 +103,19 @@ unset(CTEST_EXTRA_CONFIG)
 unset(CTEST_EXTRA_CODE)
 unset(CMAKELISTS_EXTRA_CODE)
 
+# add ThreadSanitizer test
+set(CTEST_EXTRA_CODE
+"set(CTEST_MEMORYCHECK_COMMAND_OPTIONS \"report_bugs=1 history_size=5 exitcode=55\")
+")
+
+set(CMAKELISTS_EXTRA_CODE
+"add_test(NAME TestSan COMMAND \"${CMAKE_COMMAND}\"
+-P \"${CMAKE_CURRENT_SOURCE_DIR}/testThreadSanitizer.cmake\")
+")
+gen_mc_test_internal(DummyThreadSanitizer "" -DMEMCHECK_TYPE=ThreadSanitizer)
+set(CMAKELISTS_EXTRA_CODE )
+set(CTEST_EXTRA_CODE)
+
 gen_mc_test(DummyPurify "\${PSEUDO_PURIFY}")
 gen_mc_test(DummyValgrind "\${PSEUDO_VALGRIND}")
 gen_mc_test(DummyBC "\${PSEUDO_BC}")
@@ -189,6 +202,11 @@ set_tests_properties(CTestTestMemcheckDummyValgrindTwoTargets PROPERTIES
     PASS_REGULAR_EXPRESSION
     "\nMemory check project ${CTEST_ESCAPED_CMAKE_CURRENT_BINARY_DIR}/DummyValgrindTwoTargets\n.*\n *Start 1: RunCMake\n(.*\n)?Memory check command: .* \"--log-file=${CTEST_ESCAPED_CMAKE_CURRENT_BINARY_DIR}/DummyValgrindTwoTargets/Testing/Temporary/MemoryChecker.1.log\" \"-q\".*\n *Start 2: RunCMakeAgain\n(.*\n)?Memory check command: .* \"--log-file=${CTEST_ESCAPED_CMAKE_CURRENT_BINARY_DIR}/DummyValgrindTwoTargets/Testing/Temporary/MemoryChecker.2.log\" \"-q\".*\n")
 
+set_tests_properties(CTestTestMemcheckDummyThreadSanitizer PROPERTIES
+    PASS_REGULAR_EXPRESSION
+    ".*Memory checking results:.*data race.* - 1.*data race on vptr .ctor/dtor vs virtual call. - 1.*heap-use-after-free - 1.*thread leak - 1.*destroy of a locked mutex - 1.*double lock of a mutex - 1.*unlock of an unlocked mutex .or by a wrong thread. - 1.*read lock of a write locked mutex - 1.*read unlock of a write locked mutex - 1.*signal-unsafe call inside of a signal - 1.*signal handler spoils errno - 1.*lock-order-inversion .potential deadlock. - 1.*")
+
+
 # Xcode 2.x forgets to create the output directory before linking
 # the individual architectures.
 if(CMAKE_OSX_ARCHITECTURES AND XCODE AND NOT "${XCODE_VERSION}" MATCHES "^[^12]")

+ 1 - 0
Tests/CTestTestMemcheck/test.cmake.in

@@ -15,6 +15,7 @@ set(CTEST_COVERAGE_COMMAND              "@COVERAGE_COMMAND@")
 set(CTEST_NOTES_FILES                   "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}")
 
 set(CTEST_MEMORYCHECK_COMMAND "@CHECKER_COMMAND@")
+set(CTEST_MEMORYCHECK_TYPE "${MEMCHECK_TYPE}")
 
 @CTEST_EXTRA_CODE@
 

+ 47 - 0
Tests/CTestTestMemcheck/testThreadSanitizer.cmake

@@ -0,0 +1,47 @@
+# this file simulates a program that has been built with thread sanitizer
+# options
+
+message("TSAN_OPTIONS = [$ENV{TSAN_OPTIONS}]")
+string(REGEX REPLACE ".*log_path=\"([^\"]*)\".*" "\\1" LOG_FILE "$ENV{TSAN_OPTIONS}")
+message("LOG_FILE=[${LOG_FILE}]")
+
+set(error_types
+ "data race"
+ "data race on vptr (ctor/dtor vs virtual call)"
+ "heap-use-after-free"
+ "thread leak"
+ "destroy of a locked mutex"
+  "double lock of a mutex"
+  "unlock of an unlocked mutex (or by a wrong thread)"
+  "read lock of a write locked mutex"
+  "read unlock of a write locked mutex"
+  "signal-unsafe call inside of a signal"
+  "signal handler spoils errno"
+  "lock-order-inversion (potential deadlock)"
+ )
+
+# clear the log file
+file(REMOVE "${LOG_FILE}.2343")
+
+# create an error of each type of thread santizer
+# these names come from tsan_report.cc in llvm
+foreach(error_type ${error_types} )
+
+  file(APPEND "${LOG_FILE}.2343"
+"==================
+WARNING: ThreadSanitizer: ${error_type} (pid=27978)
+  Write of size 4 at 0x7fe017ce906c by thread T1:
+    #0 Thread1 ??:0 (exe+0x000000000bb0)
+    #1 <null> <null>:0 (libtsan.so.0+0x00000001b279)
+
+  Previous write of size 4 at 0x7fe017ce906c by main thread:
+    #0 main ??:0 (exe+0x000000000c3c)
+
+  Thread T1 (tid=27979, running) created by main thread at:
+    #0 <null> <null>:0 (libtsan.so.0+0x00000001ed7b)
+    #1 main ??:0 (exe+0x000000000c2c)
+
+SUMMARY: ThreadSanitizer: ${error_type} ??:0 Thread1
+==================
+")
+endforeach()