summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Hoffman <bill.hoffman@kitware.com>2014-07-14 17:01:47 -0400
committerBrad King <brad.king@kitware.com>2014-07-16 11:50:23 -0400
commit44726714322ca0b75628e234229f4583a480d7ec (patch)
tree5e5ffa1fb65bec92047096f68f0d7defe5be1108
parent49bf3e7d8daab2a1e7ba435d011618bd2c516766 (diff)
downloadcmake-44726714322ca0b75628e234229f4583a480d7ec.tar.gz
ctest_memcheck: Add support for memory and leak sanitizer.
This adds support for memory and leak sanitizers. This is built into clang and gcc 4.8 and new compilers. It is activated with a -f switch during compile.
-rw-r--r--Source/CTest/cmCTestMemCheckHandler.cxx74
-rw-r--r--Source/CTest/cmCTestMemCheckHandler.h9
-rw-r--r--Source/cmXMLParser.cxx13
-rw-r--r--Source/cmXMLParser.h9
-rw-r--r--Tests/CTestTestMemcheck/CMakeLists.txt39
-rw-r--r--Tests/CTestTestMemcheck/testAddressSanitizer.cmake55
-rw-r--r--Tests/CTestTestMemcheck/testLeakSanitizer.cmake36
7 files changed, 213 insertions, 22 deletions
diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx
index bcf09adbc8..a38935991e 100644
--- a/Source/CTest/cmCTestMemCheckHandler.cxx
+++ b/Source/CTest/cmCTestMemCheckHandler.cxx
@@ -45,11 +45,23 @@ static CatToErrorType cmCTestMemCheckBoundsChecker[] = {
{0,0}
};
+static void xmlReportError(int line, const char* msg, void* data)
+{
+ cmCTest* ctest = (cmCTest*)data;
+ cmCTestLog(ctest, ERROR_MESSAGE,
+ "Error parsing XML in stream at line "
+ << line << ": " << msg << std::endl);
+}
+
// parse the xml file containing the results of last BoundsChecker run
class cmBoundsCheckerParser : public cmXMLParser
{
public:
- cmBoundsCheckerParser(cmCTest* c) { this->CTest = c;}
+ cmBoundsCheckerParser(cmCTest* c)
+ {
+ this->CTest = c;
+ this->SetErrorCallback(xmlReportError, (void*)c);
+ }
void StartElement(const std::string& name, const char** atts)
{
if(name == "MemoryLeak" ||
@@ -361,6 +373,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os)
case cmCTestMemCheckHandler::THREAD_SANITIZER:
os << "ThreadSanitizer";
break;
+ case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
+ os << "AddressSanitizer";
+ break;
default:
os << "Unknown";
}
@@ -529,6 +544,14 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
this->MemoryTesterStyle = cmCTestMemCheckHandler::THREAD_SANITIZER;
this->LogWithPID = true; // even if we give the log file the pid is added
}
+ if ( this->CTest->GetCTestConfiguration("MemoryCheckType")
+ == "AddressSanitizer")
+ {
+ this->MemoryTester
+ = this->CTest->GetCTestConfiguration("CMakeCommand").c_str();
+ this->MemoryTesterStyle = cmCTestMemCheckHandler::ADDRESS_SANITIZER;
+ this->LogWithPID = true; // even if we give the log file the pid is added
+ }
// Check the MemoryCheckType
if(this->MemoryTesterStyle == cmCTestMemCheckHandler::UNKNOWN)
{
@@ -651,6 +674,9 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
this->MemoryTesterOptions.push_back("/M");
break;
}
+ // these two are almost the same but the env var used
+ // is different
+ case cmCTestMemCheckHandler::ADDRESS_SANITIZER:
case cmCTestMemCheckHandler::THREAD_SANITIZER:
{
// To pass arguments to ThreadSanitizer the environment variable
@@ -660,9 +686,16 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking()
// 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=\""
+ std::string envVar = "TSAN_OPTIONS";
+ std::string extraOptions;
+ if(this->MemoryTesterStyle == cmCTestMemCheckHandler::ADDRESS_SANITIZER)
+ {
+ envVar = "ASAN_OPTIONS";
+ extraOptions = " detect_leaks=1";
+ }
+ std::string outputFile = envVar + "=log_path=\""
+ this->MemoryTesterOutputFile + "\"";
- this->MemoryTesterEnvironmentVariable = outputFile;
+ this->MemoryTesterEnvironmentVariable = outputFile + extraOptions;
break;
}
default:
@@ -695,9 +728,11 @@ ProcessMemCheckOutput(const std::string& str,
return this->ProcessMemCheckPurifyOutput(str, log, results);
}
else if ( this->MemoryTesterStyle ==
- cmCTestMemCheckHandler::THREAD_SANITIZER )
+ cmCTestMemCheckHandler::THREAD_SANITIZER ||
+ this->MemoryTesterStyle ==
+ cmCTestMemCheckHandler::ADDRESS_SANITIZER)
{
- return this->ProcessMemCheckThreadSanitizerOutput(str, log, results);
+ return this->ProcessMemCheckSanitizerOutput(str, log, results);
}
else if ( this->MemoryTesterStyle ==
cmCTestMemCheckHandler::BOUNDS_CHECKER )
@@ -730,12 +765,21 @@ std::vector<int>::size_type cmCTestMemCheckHandler::FindOrAddWarning(
return this->ResultStrings.size()-1;
}
//----------------------------------------------------------------------
-bool cmCTestMemCheckHandler::ProcessMemCheckThreadSanitizerOutput(
+bool cmCTestMemCheckHandler::ProcessMemCheckSanitizerOutput(
const std::string& str, std::string& log,
std::vector<int>& result)
{
- cmsys::RegularExpression
- sanitizerWarning("WARNING: ThreadSanitizer: (.*) \\(pid=.*\\)");
+ std::string regex;
+ if(this->MemoryTesterStyle == cmCTestMemCheckHandler::THREAD_SANITIZER)
+ {
+ regex = "WARNING: ThreadSanitizer: (.*) \\(pid=.*\\)";
+ }
+ else
+ {
+ regex = "ERROR: AddressSanitizer: (.*) on.*";
+ }
+ cmsys::RegularExpression sanitizerWarning(regex);
+ cmsys::RegularExpression leakWarning("(Direct|Indirect) leak of .*");
int defects = 0;
std::vector<std::string> lines;
cmSystemTools::Split(str.c_str(), lines);
@@ -744,10 +788,18 @@ bool cmCTestMemCheckHandler::ProcessMemCheckThreadSanitizerOutput(
for( std::vector<std::string>::iterator i = lines.begin();
i != lines.end(); ++i)
{
- if(sanitizerWarning.find(*i))
+ std::string resultFound;
+ if(leakWarning.find(*i))
+ {
+ resultFound = leakWarning.match(1)+" leak";
+ }
+ else if (sanitizerWarning.find(*i))
+ {
+ resultFound = sanitizerWarning.match(1);
+ }
+ if(resultFound.size())
{
- std::string warning = sanitizerWarning.match(1);
- std::vector<int>::size_type idx = this->FindOrAddWarning(warning);
+ std::vector<int>::size_type idx = this->FindOrAddWarning(resultFound);
if(result.size() == 0 || idx > result.size()-1)
{
result.push_back(1);
diff --git a/Source/CTest/cmCTestMemCheckHandler.h b/Source/CTest/cmCTestMemCheckHandler.h
index ffe57f6e77..2630fde4be 100644
--- a/Source/CTest/cmCTestMemCheckHandler.h
+++ b/Source/CTest/cmCTestMemCheckHandler.h
@@ -50,7 +50,8 @@ private:
PURIFY,
BOUNDS_CHECKER,
// checkers after hear do not use the standard error list
- THREAD_SANITIZER
+ THREAD_SANITIZER,
+ ADDRESS_SANITIZER
};
public:
enum { // Memory faults
@@ -132,9 +133,9 @@ private:
bool ProcessMemCheckPurifyOutput(const std::string& str,
std::string& log,
std::vector<int>& results);
- bool ProcessMemCheckThreadSanitizerOutput(const std::string& str,
- std::string& log,
- std::vector<int>& results);
+ bool ProcessMemCheckSanitizerOutput(const std::string& str,
+ std::string& log,
+ std::vector<int>& results);
bool ProcessMemCheckBoundsCheckerOutput(const std::string& str,
std::string& log,
std::vector<int>& results);
diff --git a/Source/cmXMLParser.cxx b/Source/cmXMLParser.cxx
index a73fd70326..391b874d1a 100644
--- a/Source/cmXMLParser.cxx
+++ b/Source/cmXMLParser.cxx
@@ -20,6 +20,8 @@ cmXMLParser::cmXMLParser()
{
this->Parser = 0;
this->ParseError = 0;
+ this->ReportCallback = 0;
+ this->ReportCallbackData = 0;
}
//----------------------------------------------------------------------------
@@ -233,6 +235,13 @@ void cmXMLParser::ReportXmlParseError()
//----------------------------------------------------------------------------
void cmXMLParser::ReportError(int line, int, const char* msg)
{
- std::cerr << "Error parsing XML in stream at line "
- << line << ": " << msg << std::endl;
+ if(this->ReportCallback)
+ {
+ this->ReportCallback(line, msg, this->ReportCallbackData);
+ }
+ else
+ {
+ std::cerr << "Error parsing XML in stream at line "
+ << line << ": " << msg << std::endl;
+ }
}
diff --git a/Source/cmXMLParser.h b/Source/cmXMLParser.h
index 84a5a7dd6f..e72da66b33 100644
--- a/Source/cmXMLParser.h
+++ b/Source/cmXMLParser.h
@@ -50,11 +50,18 @@ public:
virtual int ParseChunk(const char* inputString,
std::string::size_type length);
virtual int CleanupParser();
-
+ typedef void (*ReportFunction)(int, const char*, void*);
+ void SetErrorCallback(ReportFunction f, void* d)
+ {
+ this->ReportCallback = f;
+ this->ReportCallbackData = d;
+ }
protected:
//! This variable is true if there was a parse error while parsing in
//chunks.
int ParseError;
+ ReportFunction ReportCallback;
+ void* ReportCallbackData;
//1 Expat parser structure. Exists only during call to Parse().
void* Parser;
diff --git a/Tests/CTestTestMemcheck/CMakeLists.txt b/Tests/CTestTestMemcheck/CMakeLists.txt
index f470835f30..d16d43279b 100644
--- a/Tests/CTestTestMemcheck/CMakeLists.txt
+++ b/Tests/CTestTestMemcheck/CMakeLists.txt
@@ -113,9 +113,44 @@ set(CMAKELISTS_EXTRA_CODE
-P \"${CMAKE_CURRENT_SOURCE_DIR}/testThreadSanitizer.cmake\")
")
gen_mc_test_internal(DummyThreadSanitizer "" -DMEMCHECK_TYPE=ThreadSanitizer)
+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.*")
set(CMAKELISTS_EXTRA_CODE )
set(CTEST_EXTRA_CODE)
+# add LeakSanitizer 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}/testLeakSanitizer.cmake\")
+")
+gen_mc_test_internal(DummyLeakSanitizer "" -DMEMCHECK_TYPE=AddressSanitizer)
+set(CMAKELISTS_EXTRA_CODE )
+set(CTEST_EXTRA_CODE)
+set_tests_properties(CTestTestMemcheckDummyLeakSanitizer PROPERTIES
+ PASS_REGULAR_EXPRESSION
+ ".*Memory checking results:.*Direct leak - 2.*Indirect leak - 1.*")
+# add AddressSanitizer 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}/testAddressSanitizer.cmake\")
+")
+gen_mc_test_internal(DummyAddressSanitizer "" -DMEMCHECK_TYPE=AddressSanitizer)
+set(CMAKELISTS_EXTRA_CODE )
+set(CTEST_EXTRA_CODE)
+set_tests_properties(CTestTestMemcheckDummyAddressSanitizer PROPERTIES
+ PASS_REGULAR_EXPRESSION
+ ".*Memory checking results:.*heap-buffer-overflow - 1.*")
+
+
gen_mc_test(DummyPurify "\${PSEUDO_PURIFY}")
gen_mc_test(DummyValgrind "\${PSEUDO_VALGRIND}")
gen_mc_test(DummyBC "\${PSEUDO_BC}")
@@ -202,10 +237,6 @@ 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.
diff --git a/Tests/CTestTestMemcheck/testAddressSanitizer.cmake b/Tests/CTestTestMemcheck/testAddressSanitizer.cmake
new file mode 100644
index 0000000000..a359e28187
--- /dev/null
+++ b/Tests/CTestTestMemcheck/testAddressSanitizer.cmake
@@ -0,0 +1,55 @@
+# this file simulates a program that has been built with thread sanitizer
+# options
+
+message("ASAN_OPTIONS = [$ENV{ASAN_OPTIONS}]")
+string(REGEX REPLACE ".*log_path=\"([^\"]*)\".*" "\\1" LOG_FILE "$ENV{ASAN_OPTIONS}")
+message("LOG_FILE=[${LOG_FILE}]")
+
+# 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
+
+file(APPEND "${LOG_FILE}.2343"
+"=================================================================
+==19278== ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60080000bffc at pc 0x4009f1 bp 0x7fff82de6520 sp 0x7fff82de6518
+WRITE of size 4 at 0x60080000bffc thread T0
+ #0 0x4009f0 (/home/kitware/msan/a.out+0x4009f0)
+ #1 0x7f18b02c876c (/lib/x86_64-linux-gnu/libc-2.15.so+0x2176c)
+ #2 0x400858 (/home/kitware/msan/a.out+0x400858)
+0x60080000bffc is located 4 bytes to the right of 40-byte region [0x60080000bfd0,0x60080000bff8)
+allocated by thread T0 here:
+ #0 0x7f18b088f9ca (/usr/lib/x86_64-linux-gnu/libasan.so.0.0.0+0x119ca)
+ #1 0x4009a2 (/home/kitware/msan/a.out+0x4009a2)
+ #2 0x7f18b02c876c (/lib/x86_64-linux-gnu/libc-2.15.so+0x2176c)
+Shadow bytes around the buggy address:
+ 0x0c017fff97a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c017fff97b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c017fff97c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c017fff97d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c017fff97e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+=>0x0c017fff97f0: fa fa fa fa fa fa fa fa fa fa 00 00 00 00 00[fa]
+ 0x0c017fff9800:fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c017fff9810: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c017fff9820: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c017fff9830: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+ 0x0c017fff9840: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
+Shadow byte legend (one shadow byte represents 8 application bytes):
+ Addressable: 00
+ Partially addressable: 01 02 03 04 05 06 07
+ Heap left redzone: fa
+ Heap righ redzone: fb
+ Freed Heap region: fd
+ Stack left redzone: f1
+ Stack mid redzone: f2
+ Stack right redzone: f3
+ Stack partial redzone: f4
+ Stack after return: f5
+ Stack use after scope: f8
+ Global redzone: f9
+ Global init order: f6
+ Poisoned by user: f7
+ ASan internal: fe
+==19278== ABORTING
+")
diff --git a/Tests/CTestTestMemcheck/testLeakSanitizer.cmake b/Tests/CTestTestMemcheck/testLeakSanitizer.cmake
new file mode 100644
index 0000000000..ca0a264024
--- /dev/null
+++ b/Tests/CTestTestMemcheck/testLeakSanitizer.cmake
@@ -0,0 +1,36 @@
+# this file simulates a program that has been built with thread sanitizer
+# options
+
+message("ASAN_OPTIONS = [$ENV{ASAN_OPTIONS}]")
+string(REGEX REPLACE ".*log_path=\"([^\"]*)\".*" "\\1" LOG_FILE "$ENV{ASAN_OPTIONS}")
+message("LOG_FILE=[${LOG_FILE}]")
+
+# 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
+
+file(APPEND "${LOG_FILE}.2343"
+"=================================================================
+==25308==ERROR: LeakSanitizer: detected memory leaks
+
+Direct leak of 4360 byte(s) in 1 object(s) allocated from:
+ #0 0x46c669 in operator new[](unsigned long) (/home/kitware/msan/a.out+0x46c669)
+ #1 0x4823b4 in main /home/kitware/msan/memcheck.cxx:12
+ #2 0x7fa72bee476c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226
+
+Direct leak of 76 byte(s) in 1 object(s) allocated from:
+ #0 0x46c669 in operator new[](unsigned long) (/home/kitware/msan/a.out+0x46c669)
+ #1 0x4821b8 in foo() /home/kitware/msan/memcheck.cxx:4
+ #2 0x4823f2 in main /home/kitware/msan/memcheck.cxx:14
+ #3 0x7fa72bee476c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226
+
+Indirect leak of 76 byte(s) in 1 object(s) allocated from:
+ #0 0x46c669 in operator new[](unsigned long) (/home/kitware/msan/a.out+0x46c669)
+ #1 0x4821b8 in foo() /home/kitware/msan/memcheck.cxx:4
+ #2 0x4823f2 in main /home/kitware/msan/memcheck.cxx:14
+ #3 0x7fa72bee476c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226
+
+SUMMARY: AddressSanitizer: 4436 byte(s) leaked in 2 allocation(s).
+")