Merge topic 'thread-sanitizer'
1e005ead
CTest: Fix MemoryCheckType from 'ctest -T MemCheck'44726714
ctest_memcheck: Add support for memory and leak sanitizer.
This commit is contained in:
commit
66e88af6d0
|
@ -71,6 +71,7 @@ Compiler: @CMAKE_CXX_COMPILER@
|
|||
PurifyCommand: @PURIFYCOMMAND@
|
||||
ValgrindCommand: @VALGRIND_COMMAND@
|
||||
ValgrindCommandOptions: @VALGRIND_COMMAND_OPTIONS@
|
||||
MemoryCheckType: @MEMORYCHECK_TYPE@
|
||||
MemoryCheckCommand: @MEMORYCHECK_COMMAND@
|
||||
MemoryCheckCommandOptions: @MEMORYCHECK_COMMAND_OPTIONS@
|
||||
MemoryCheckSuppressionFile: @MEMORYCHECK_SUPPRESSIONS_FILE@
|
||||
|
|
|
@ -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" ||
|
||||
|
@ -334,8 +346,8 @@ void cmCTestMemCheckHandler::PopulateCustomVectors(cmMakefile *mf)
|
|||
this->CTest->PopulateCustomVector(mf,
|
||||
"CTEST_CUSTOM_MEMCHECK_IGNORE",
|
||||
this->CustomTestsIgnore);
|
||||
this->CTest->SetCTestConfigurationFromCMakeVariable(
|
||||
mf, "CMakeCommand", "CMAKE_COMMAND");
|
||||
std::string cmake = cmSystemTools::GetCMakeCommand();
|
||||
this->CTest->SetCTestConfiguration("CMakeCommand", cmake.c_str());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -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))
|
||||
{
|
||||
std::string warning = sanitizerWarning.match(1);
|
||||
std::vector<int>::size_type idx = this->FindOrAddWarning(warning);
|
||||
resultFound = leakWarning.match(1)+" leak";
|
||||
}
|
||||
else if (sanitizerWarning.find(*i))
|
||||
{
|
||||
resultFound = sanitizerWarning.match(1);
|
||||
}
|
||||
if(resultFound.size())
|
||||
{
|
||||
std::vector<int>::size_type idx = this->FindOrAddWarning(resultFound);
|
||||
if(result.size() == 0 || idx > result.size()-1)
|
||||
{
|
||||
result.push_back(1);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
")
|
|
@ -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).
|
||||
")
|
Loading…
Reference in New Issue