tsan: add shadow memory flush + fix few bugs
llvm-svn: 157270
This commit is contained in:
parent
8b3304da56
commit
302cebb8f1
|
@ -1,26 +0,0 @@
|
||||||
#include <pthread.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
volatile int g_data1;
|
|
||||||
volatile int g_data2;
|
|
||||||
volatile int g_data3;
|
|
||||||
volatile int g_data4;
|
|
||||||
|
|
||||||
void *Thread1(void *x) {
|
|
||||||
if (x)
|
|
||||||
usleep(1000000);
|
|
||||||
g_data1 = 42;
|
|
||||||
g_data2 = 43;
|
|
||||||
g_data3 = 44;
|
|
||||||
g_data4 = 45;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
pthread_t t;
|
|
||||||
pthread_create(&t, 0, Thread1, (void*)1);
|
|
||||||
Thread1(0);
|
|
||||||
pthread_join(t, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK: ThreadSanitizer: reported 1 warnings
|
|
|
@ -42,20 +42,20 @@ struct ThreadClock {
|
||||||
ThreadClock();
|
ThreadClock();
|
||||||
|
|
||||||
u64 get(unsigned tid) const {
|
u64 get(unsigned tid) const {
|
||||||
DCHECK(tid < kMaxTidInClock);
|
DCHECK_LT(tid, kMaxTidInClock);
|
||||||
return clk_[tid];
|
return clk_[tid];
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(unsigned tid, u64 v) {
|
void set(unsigned tid, u64 v) {
|
||||||
DCHECK(tid < kMaxTid);
|
DCHECK_LT(tid, kMaxTid);
|
||||||
DCHECK(v >= clk_[tid]);
|
DCHECK_GE(v, clk_[tid]);
|
||||||
clk_[tid] = v;
|
clk_[tid] = v;
|
||||||
if (nclk_ <= tid)
|
if (nclk_ <= tid)
|
||||||
nclk_ = tid + 1;
|
nclk_ = tid + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tick(unsigned tid) {
|
void tick(unsigned tid) {
|
||||||
DCHECK(tid < kMaxTid);
|
DCHECK_LT(tid, kMaxTid);
|
||||||
clk_[tid]++;
|
clk_[tid]++;
|
||||||
if (nclk_ <= tid)
|
if (nclk_ <= tid)
|
||||||
nclk_ = tid + 1;
|
nclk_ = tid + 1;
|
||||||
|
|
|
@ -32,7 +32,7 @@ const uptr kPageSize = 4096;
|
||||||
const int kTidBits = 13;
|
const int kTidBits = 13;
|
||||||
const unsigned kMaxTid = 1 << kTidBits;
|
const unsigned kMaxTid = 1 << kTidBits;
|
||||||
const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit.
|
const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit.
|
||||||
const int kClkBits = 40;
|
const int kClkBits = 43;
|
||||||
|
|
||||||
#ifdef TSAN_SHADOW_COUNT
|
#ifdef TSAN_SHADOW_COUNT
|
||||||
# if TSAN_SHADOW_COUNT == 2 \
|
# if TSAN_SHADOW_COUNT == 2 \
|
||||||
|
|
|
@ -47,6 +47,8 @@ void InitializeFlags(Flags *f, const char *env) {
|
||||||
f->atexit_sleep_ms = 1000;
|
f->atexit_sleep_ms = 1000;
|
||||||
f->verbosity = 0;
|
f->verbosity = 0;
|
||||||
f->profile_memory = "";
|
f->profile_memory = "";
|
||||||
|
f->flush_memory_ms = 0;
|
||||||
|
f->stop_on_start = false;
|
||||||
|
|
||||||
// Let a frontend override.
|
// Let a frontend override.
|
||||||
OverrideFlags(f);
|
OverrideFlags(f);
|
||||||
|
@ -65,6 +67,8 @@ void InitializeFlags(Flags *f, const char *env) {
|
||||||
Flag(env, &f->atexit_sleep_ms, "atexit_sleep_ms");
|
Flag(env, &f->atexit_sleep_ms, "atexit_sleep_ms");
|
||||||
Flag(env, &f->verbosity, "verbosity");
|
Flag(env, &f->verbosity, "verbosity");
|
||||||
Flag(env, &f->profile_memory, "profile_memory");
|
Flag(env, &f->profile_memory, "profile_memory");
|
||||||
|
Flag(env, &f->flush_memory_ms, "flush_memory_ms");
|
||||||
|
Flag(env, &f->stop_on_start, "stop_on_start");
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *GetFlagValue(const char *env, const char *name,
|
static const char *GetFlagValue(const char *env, const char *name,
|
||||||
|
|
|
@ -48,6 +48,10 @@ struct Flags {
|
||||||
int verbosity;
|
int verbosity;
|
||||||
// If set, periodically write memory profile to that file.
|
// If set, periodically write memory profile to that file.
|
||||||
const char *profile_memory;
|
const char *profile_memory;
|
||||||
|
// Flush shadow memory every X ms.
|
||||||
|
int flush_memory_ms;
|
||||||
|
// Stops on start until __tsan_resume() is called (for debugging).
|
||||||
|
bool stop_on_start;
|
||||||
};
|
};
|
||||||
|
|
||||||
Flags *flags();
|
Flags *flags();
|
||||||
|
|
|
@ -65,6 +65,7 @@ static inline uptr ShadowToMem(uptr shadow) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uptr GetShadowMemoryConsumption();
|
uptr GetShadowMemoryConsumption();
|
||||||
|
void FlushShadowMemory();
|
||||||
|
|
||||||
const char *InitializePlatform();
|
const char *InitializePlatform();
|
||||||
void FinalizePlatform();
|
void FinalizePlatform();
|
||||||
|
|
|
@ -63,6 +63,12 @@ uptr GetShadowMemoryConsumption() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlushShadowMemory() {
|
||||||
|
madvise((void*)kLinuxShadowBeg,
|
||||||
|
kLinuxShadowEnd - kLinuxShadowBeg,
|
||||||
|
MADV_DONTNEED);
|
||||||
|
}
|
||||||
|
|
||||||
static void *my_mmap(void *addr, size_t length, int prot, int flags,
|
static void *my_mmap(void *addr, size_t length, int prot, int flags,
|
||||||
int fd, u64 offset) {
|
int fd, u64 offset) {
|
||||||
ScopedInRtl in_rtl;
|
ScopedInRtl in_rtl;
|
||||||
|
|
|
@ -21,10 +21,10 @@
|
||||||
#include "tsan_placement_new.h"
|
#include "tsan_placement_new.h"
|
||||||
#include "tsan_suppressions.h"
|
#include "tsan_suppressions.h"
|
||||||
|
|
||||||
volatile int __tsan_stop = 0;
|
volatile int __tsan_resumed = 0;
|
||||||
|
|
||||||
extern "C" void __tsan_resume() {
|
extern "C" void __tsan_resume() {
|
||||||
__tsan_stop = 0;
|
__tsan_resumed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace __tsan {
|
namespace __tsan {
|
||||||
|
@ -59,7 +59,6 @@ ThreadState::ThreadState(Context *ctx, int tid, u64 epoch,
|
||||||
// , in_rtl()
|
// , in_rtl()
|
||||||
, shadow_stack_pos(&shadow_stack[0])
|
, shadow_stack_pos(&shadow_stack[0])
|
||||||
, tid(tid)
|
, tid(tid)
|
||||||
, func_call_count()
|
|
||||||
, stk_addr(stk_addr)
|
, stk_addr(stk_addr)
|
||||||
, stk_size(stk_size)
|
, stk_size(stk_size)
|
||||||
, tls_addr(tls_addr)
|
, tls_addr(tls_addr)
|
||||||
|
@ -138,6 +137,22 @@ static void InitializeMemoryProfile() {
|
||||||
internal_start_thread(&MemoryProfileThread, (void*)(uptr)fd);
|
internal_start_thread(&MemoryProfileThread, (void*)(uptr)fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void MemoryFlushThread(void *arg) {
|
||||||
|
ScopedInRtl in_rtl;
|
||||||
|
for (int i = 0; ; i++) {
|
||||||
|
internal_sleep_ms(flags()->flush_memory_ms);
|
||||||
|
FlushShadowMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitializeMemoryFlush() {
|
||||||
|
if (flags()->flush_memory_ms == 0)
|
||||||
|
return;
|
||||||
|
if (flags()->flush_memory_ms < 100)
|
||||||
|
flags()->flush_memory_ms = 100;
|
||||||
|
internal_start_thread(&MemoryFlushThread, 0);
|
||||||
|
}
|
||||||
|
|
||||||
void Initialize(ThreadState *thr) {
|
void Initialize(ThreadState *thr) {
|
||||||
// Thread safe because done before all threads exist.
|
// Thread safe because done before all threads exist.
|
||||||
static bool is_initialized = false;
|
static bool is_initialized = false;
|
||||||
|
@ -157,9 +172,10 @@ void Initialize(ThreadState *thr) {
|
||||||
InitializeFlags(&ctx->flags, env);
|
InitializeFlags(&ctx->flags, env);
|
||||||
InitializeSuppressions();
|
InitializeSuppressions();
|
||||||
InitializeMemoryProfile();
|
InitializeMemoryProfile();
|
||||||
|
InitializeMemoryFlush();
|
||||||
|
|
||||||
if (ctx->flags.verbosity)
|
if (ctx->flags.verbosity)
|
||||||
Printf("***** Running under ThreadSanitizer v2 (pid=%d) *****\n", GetPid());
|
Printf("***** Running under ThreadSanitizer v2 (pid %d) *****\n", GetPid());
|
||||||
|
|
||||||
// Initialize thread 0.
|
// Initialize thread 0.
|
||||||
ctx->thread_seq = 0;
|
ctx->thread_seq = 0;
|
||||||
|
@ -169,9 +185,11 @@ void Initialize(ThreadState *thr) {
|
||||||
CHECK_EQ(thr->in_rtl, 1);
|
CHECK_EQ(thr->in_rtl, 1);
|
||||||
ctx->initialized = true;
|
ctx->initialized = true;
|
||||||
|
|
||||||
if (__tsan_stop) {
|
if (flags()->stop_on_start) {
|
||||||
Printf("ThreadSanitizer is suspended at startup.\n");
|
Printf("ThreadSanitizer is suspended at startup (pid %d)."
|
||||||
while (__tsan_stop);
|
" Call __tsan_resume().\n",
|
||||||
|
GetPid());
|
||||||
|
while (__tsan_resumed == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ class FastState {
|
||||||
|
|
||||||
void SetIgnoreBit() { x_ |= kIgnoreBit; }
|
void SetIgnoreBit() { x_ |= kIgnoreBit; }
|
||||||
void ClearIgnoreBit() { x_ &= ~kIgnoreBit; }
|
void ClearIgnoreBit() { x_ &= ~kIgnoreBit; }
|
||||||
bool GetIgnoreBit() { return x_ & kIgnoreBit; }
|
bool GetIgnoreBit() const { return x_ & kIgnoreBit; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Shadow;
|
friend class Shadow;
|
||||||
|
@ -125,25 +125,17 @@ class Shadow: public FastState {
|
||||||
bool IsZero() const { return x_ == 0; }
|
bool IsZero() const { return x_ == 0; }
|
||||||
u64 raw() const { return x_; }
|
u64 raw() const { return x_; }
|
||||||
|
|
||||||
static inline bool TidsAreEqual(Shadow s1, Shadow s2) {
|
static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) {
|
||||||
u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift;
|
u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift;
|
||||||
DCHECK_EQ(shifted_xor == 0, s1.tid() == s2.tid());
|
DCHECK_EQ(shifted_xor == 0, s1.tid() == s2.tid());
|
||||||
return shifted_xor == 0;
|
return shifted_xor == 0;
|
||||||
}
|
}
|
||||||
static inline bool Addr0AndSizeAreEqual(Shadow s1, Shadow s2) {
|
|
||||||
|
static inline bool Addr0AndSizeAreEqual(const Shadow s1, const Shadow s2) {
|
||||||
u64 masked_xor = (s1.x_ ^ s2.x_) & 31;
|
u64 masked_xor = (s1.x_ ^ s2.x_) & 31;
|
||||||
return masked_xor == 0;
|
return masked_xor == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool TwoRangesIntersectSLOW(Shadow s1, Shadow s2) {
|
|
||||||
if (s1.addr0() == s2.addr0()) return true;
|
|
||||||
if (s1.addr0() < s2.addr0() && s1.addr0() + s1.size() > s2.addr0())
|
|
||||||
return true;
|
|
||||||
if (s2.addr0() < s1.addr0() && s2.addr0() + s2.size() > s1.addr0())
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool TwoRangesIntersect(Shadow s1, Shadow s2,
|
static inline bool TwoRangesIntersect(Shadow s1, Shadow s2,
|
||||||
unsigned kS2AccessSize) {
|
unsigned kS2AccessSize) {
|
||||||
bool res = false;
|
bool res = false;
|
||||||
|
@ -201,6 +193,15 @@ class Shadow: public FastState {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u64 size_log() const { return (x_ >> 3) & 3; }
|
u64 size_log() const { return (x_ >> 3) & 3; }
|
||||||
|
|
||||||
|
static bool TwoRangesIntersectSLOW(const Shadow s1, const Shadow s2) {
|
||||||
|
if (s1.addr0() == s2.addr0()) return true;
|
||||||
|
if (s1.addr0() < s2.addr0() && s1.addr0() + s1.size() > s2.addr0())
|
||||||
|
return true;
|
||||||
|
if (s2.addr0() < s1.addr0() && s2.addr0() + s2.size() > s1.addr0())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Freed memory.
|
// Freed memory.
|
||||||
|
@ -248,7 +249,6 @@ struct ThreadState {
|
||||||
u64 stat[StatCnt];
|
u64 stat[StatCnt];
|
||||||
const int tid;
|
const int tid;
|
||||||
int in_rtl;
|
int in_rtl;
|
||||||
int func_call_count;
|
|
||||||
const uptr stk_addr;
|
const uptr stk_addr;
|
||||||
const uptr stk_size;
|
const uptr stk_size;
|
||||||
const uptr tls_addr;
|
const uptr tls_addr;
|
||||||
|
|
|
@ -344,13 +344,6 @@ void ReportRace(ThreadState *thr) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AddRacyStacks(thr, traces, addr_min, addr_max);
|
AddRacyStacks(thr, traces, addr_min, addr_max);
|
||||||
|
|
||||||
// Bump the thread's clock a bit.
|
|
||||||
// This avoids series of similar reports between the same threads
|
|
||||||
// that happen close to each other (e.g. accessing several fields
|
|
||||||
// of the same object).
|
|
||||||
FastState s(thr->racy_state[1]);
|
|
||||||
thr->clock.set(s.tid(), s.epoch() + 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) {
|
void CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) {
|
||||||
|
|
|
@ -92,6 +92,7 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
|
||||||
CHECK_EQ(tctx->status, ThreadStatusDead);
|
CHECK_EQ(tctx->status, ThreadStatusDead);
|
||||||
tctx->status = ThreadStatusInvalid;
|
tctx->status = ThreadStatusInvalid;
|
||||||
tctx->reuse_count++;
|
tctx->reuse_count++;
|
||||||
|
tctx->sync.Reset();
|
||||||
tid = tctx->tid;
|
tid = tctx->tid;
|
||||||
DestroyAndFree(tctx->dead_info);
|
DestroyAndFree(tctx->dead_info);
|
||||||
} else {
|
} else {
|
||||||
|
@ -214,7 +215,7 @@ void ThreadFinish(ThreadState *thr) {
|
||||||
tctx->dead_info->trace.headers[i].stack0.CopyFrom(
|
tctx->dead_info->trace.headers[i].stack0.CopyFrom(
|
||||||
thr->trace.headers[i].stack0);
|
thr->trace.headers[i].stack0);
|
||||||
}
|
}
|
||||||
tctx->epoch1 = thr->clock.get(tctx->tid);
|
tctx->epoch1 = thr->fast_state.epoch();
|
||||||
|
|
||||||
thr->~ThreadState();
|
thr->~ThreadState();
|
||||||
StatAggregate(ctx->stat, thr->stat);
|
StatAggregate(ctx->stat, thr->stat);
|
||||||
|
|
Loading…
Reference in New Issue