mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-26 21:32:10 +00:00
Add support for tsan annotations (thread sanitizer, a valgrind-based tool).
These annotations are disabled entirely when either ENABLE_THREADS is off, or building a release build. When enabled, they add calls to functions with no statements to ManagedStatic's getters. Use these annotations to inform tsan that the race used inside ManagedStatic initialization is actually benign. Thanks to Kostya Serebryany for helping write this patch! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@144567 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
76c8f08567
commit
4d0a9ff365
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "llvm/Support/Atomic.h"
|
#include "llvm/Support/Atomic.h"
|
||||||
#include "llvm/Support/Threading.h"
|
#include "llvm/Support/Threading.h"
|
||||||
|
#include "llvm/Support/Valgrind.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
|
||||||
@ -65,6 +66,7 @@ public:
|
|||||||
void* tmp = Ptr;
|
void* tmp = Ptr;
|
||||||
if (llvm_is_multithreaded()) sys::MemoryFence();
|
if (llvm_is_multithreaded()) sys::MemoryFence();
|
||||||
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
|
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
|
||||||
|
TsanHappensAfter(this);
|
||||||
|
|
||||||
return *static_cast<C*>(Ptr);
|
return *static_cast<C*>(Ptr);
|
||||||
}
|
}
|
||||||
@ -72,6 +74,7 @@ public:
|
|||||||
void* tmp = Ptr;
|
void* tmp = Ptr;
|
||||||
if (llvm_is_multithreaded()) sys::MemoryFence();
|
if (llvm_is_multithreaded()) sys::MemoryFence();
|
||||||
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
|
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
|
||||||
|
TsanHappensAfter(this);
|
||||||
|
|
||||||
return static_cast<C*>(Ptr);
|
return static_cast<C*>(Ptr);
|
||||||
}
|
}
|
||||||
@ -79,6 +82,7 @@ public:
|
|||||||
void* tmp = Ptr;
|
void* tmp = Ptr;
|
||||||
if (llvm_is_multithreaded()) sys::MemoryFence();
|
if (llvm_is_multithreaded()) sys::MemoryFence();
|
||||||
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
|
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
|
||||||
|
TsanHappensAfter(this);
|
||||||
|
|
||||||
return *static_cast<C*>(Ptr);
|
return *static_cast<C*>(Ptr);
|
||||||
}
|
}
|
||||||
@ -86,6 +90,7 @@ public:
|
|||||||
void* tmp = Ptr;
|
void* tmp = Ptr;
|
||||||
if (llvm_is_multithreaded()) sys::MemoryFence();
|
if (llvm_is_multithreaded()) sys::MemoryFence();
|
||||||
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
|
if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
|
||||||
|
TsanHappensAfter(this);
|
||||||
|
|
||||||
return static_cast<C*>(Ptr);
|
return static_cast<C*>(Ptr);
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,25 @@
|
|||||||
#ifndef LLVM_SYSTEM_VALGRIND_H
|
#ifndef LLVM_SYSTEM_VALGRIND_H
|
||||||
#define LLVM_SYSTEM_VALGRIND_H
|
#define LLVM_SYSTEM_VALGRIND_H
|
||||||
|
|
||||||
|
#include "llvm/Support/Compiler.h"
|
||||||
|
#include "llvm/Config/config.h"
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#if ENABLE_THREADS != 0 && !defined(NDEBUG)
|
||||||
|
// tsan (Thread Sanitizer) is a valgrind-based tool that detects these exact
|
||||||
|
// functions by name.
|
||||||
|
extern "C" {
|
||||||
|
LLVM_ATTRIBUTE_NOINLINE void AnnotateHappensAfter(const char *file, int line,
|
||||||
|
const volatile void *cv);
|
||||||
|
LLVM_ATTRIBUTE_NOINLINE void AnnotateHappensBefore(const char *file, int line,
|
||||||
|
const volatile void *cv);
|
||||||
|
LLVM_ATTRIBUTE_NOINLINE void AnnotateIgnoreWritesBegin(const char *file,
|
||||||
|
int line);
|
||||||
|
LLVM_ATTRIBUTE_NOINLINE void AnnotateIgnoreWritesEnd(const char *file,
|
||||||
|
int line);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
namespace sys {
|
namespace sys {
|
||||||
// True if Valgrind is controlling this process.
|
// True if Valgrind is controlling this process.
|
||||||
@ -26,6 +43,34 @@ namespace sys {
|
|||||||
// Discard valgrind's translation of code in the range [Addr .. Addr + Len).
|
// Discard valgrind's translation of code in the range [Addr .. Addr + Len).
|
||||||
// Otherwise valgrind may continue to execute the old version of the code.
|
// Otherwise valgrind may continue to execute the old version of the code.
|
||||||
void ValgrindDiscardTranslations(const void *Addr, size_t Len);
|
void ValgrindDiscardTranslations(const void *Addr, size_t Len);
|
||||||
|
|
||||||
|
#if ENABLE_THREADS != 0 && !defined(NDEBUG)
|
||||||
|
// Thread Sanitizer is a valgrind tool that finds races in code.
|
||||||
|
// See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations .
|
||||||
|
|
||||||
|
// This marker is used to define a happens-before arc. The race detector will
|
||||||
|
// infer an arc from the begin to the end when they share the same pointer
|
||||||
|
// argument.
|
||||||
|
#define TsanHappensBefore(cv) \
|
||||||
|
AnnotateHappensBefore(__FILE__, __LINE__, cv)
|
||||||
|
|
||||||
|
// This marker defines the destination of a happens-before arc.
|
||||||
|
#define TsanHappensAfter(cv) \
|
||||||
|
AnnotateHappensAfter(__FILE__, __LINE__, cv)
|
||||||
|
|
||||||
|
// Ignore any races on writes between here and the next TsanIgnoreWritesEnd.
|
||||||
|
#define TsanIgnoreWritesBegin() \
|
||||||
|
AnnotateIgnoreWritesBegin(__FILE__, __LINE__)
|
||||||
|
|
||||||
|
// Resume checking for racy writes.
|
||||||
|
#define TsanIgnoreWritesEnd() \
|
||||||
|
AnnotateIgnoreWritesEnd(__FILE__, __LINE__)
|
||||||
|
#else
|
||||||
|
#define TsanHappensBefore(cv)
|
||||||
|
#define TsanHappensAfter(cv)
|
||||||
|
#define TsanIgnoreWritesBegin()
|
||||||
|
#define TsanIgnoreWritesEnd()
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,15 @@ void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(),
|
|||||||
if (Ptr == 0) {
|
if (Ptr == 0) {
|
||||||
void* tmp = Creator ? Creator() : 0;
|
void* tmp = Creator ? Creator() : 0;
|
||||||
|
|
||||||
|
TsanHappensBefore(this);
|
||||||
sys::MemoryFence();
|
sys::MemoryFence();
|
||||||
|
|
||||||
|
// This write is racy against the first read in the ManagedStatic
|
||||||
|
// accessors. The race is benign because it does a second read after a
|
||||||
|
// memory fence, at which point it isn't possible to get a partial value.
|
||||||
|
TsanIgnoreWritesBegin();
|
||||||
Ptr = tmp;
|
Ptr = tmp;
|
||||||
|
TsanIgnoreWritesEnd();
|
||||||
DeleterFn = Deleter;
|
DeleterFn = Deleter;
|
||||||
|
|
||||||
// Add to list of managed statics.
|
// Add to list of managed statics.
|
||||||
@ -72,4 +79,3 @@ void llvm::llvm_shutdown() {
|
|||||||
|
|
||||||
if (llvm_is_multithreaded()) llvm_stop_multithreaded();
|
if (llvm_is_multithreaded()) llvm_stop_multithreaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,3 +52,14 @@ void llvm::sys::ValgrindDiscardTranslations(const void *Addr, size_t Len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif // !HAVE_VALGRIND_VALGRIND_H
|
#endif // !HAVE_VALGRIND_VALGRIND_H
|
||||||
|
|
||||||
|
// These functions require no implementation, tsan just looks at the arguments
|
||||||
|
// they're called with.
|
||||||
|
extern "C" {
|
||||||
|
void AnnotateHappensBefore(const char *file, int line,
|
||||||
|
const volatile void *cv) {}
|
||||||
|
void AnnotateHappensAfter(const char *file, int line,
|
||||||
|
const volatile void *cv) {}
|
||||||
|
void AnnotateIgnoreWritesBegin(const char *file, int line) {}
|
||||||
|
void AnnotateIgnoreWritesEnd(const char *file, int line) {}
|
||||||
|
}
|
||||||
|
39
unittests/Support/ManagedStatic.cpp
Normal file
39
unittests/Support/ManagedStatic.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//===- llvm/unittest/Support/ManagedStatic.cpp - ManagedStatic tests ------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "llvm/Support/ManagedStatic.h"
|
||||||
|
#include "llvm/Support/Threading.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
namespace test1 {
|
||||||
|
llvm::ManagedStatic<int> ms;
|
||||||
|
void *helper(void*) {
|
||||||
|
*ms;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Initialize, MultipleThreads) {
|
||||||
|
// Run this test under tsan: http://code.google.com/p/data-race-test/
|
||||||
|
|
||||||
|
llvm_start_multithreaded();
|
||||||
|
pthread_t t1, t2;
|
||||||
|
pthread_create(&t1, NULL, test1::helper, NULL);
|
||||||
|
pthread_create(&t2, NULL, test1::helper, NULL);
|
||||||
|
pthread_join(t1, NULL);
|
||||||
|
pthread_join(t2, NULL);
|
||||||
|
llvm_stop_multithreaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
Loading…
Reference in New Issue
Block a user