233 lines
4.5 KiB
C++
233 lines
4.5 KiB
C++
//===-- tsan_mop.cpp ------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file is a part of ThreadSanitizer (TSan), a race detector.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "tsan_interface.h"
|
|
#include "tsan_test_util.h"
|
|
#include "gtest/gtest.h"
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
TEST_F(ThreadSanitizer, SimpleWrite) {
|
|
ScopedThread t;
|
|
MemLoc l;
|
|
t.Write1(l);
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, SimpleWriteWrite) {
|
|
ScopedThread t1, t2;
|
|
MemLoc l1, l2;
|
|
t1.Write1(l1);
|
|
t2.Write1(l2);
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, WriteWriteRace) {
|
|
ScopedThread t1, t2;
|
|
MemLoc l;
|
|
t1.Write1(l);
|
|
t2.Write1(l, true);
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, ReadWriteRace) {
|
|
ScopedThread t1, t2;
|
|
MemLoc l;
|
|
t1.Read1(l);
|
|
t2.Write1(l, true);
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, WriteReadRace) {
|
|
ScopedThread t1, t2;
|
|
MemLoc l;
|
|
t1.Write1(l);
|
|
t2.Read1(l, true);
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, ReadReadNoRace) {
|
|
ScopedThread t1, t2;
|
|
MemLoc l;
|
|
t1.Read1(l);
|
|
t2.Read1(l);
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, WriteThenRead) {
|
|
MemLoc l;
|
|
ScopedThread t1, t2;
|
|
t1.Write1(l);
|
|
t1.Read1(l);
|
|
t2.Read1(l, true);
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, WriteThenLockedRead) {
|
|
UserMutex m(UserMutex::RW);
|
|
MainThread t0;
|
|
t0.Create(m);
|
|
MemLoc l;
|
|
{
|
|
ScopedThread t1, t2;
|
|
|
|
t1.Write8(l);
|
|
|
|
t1.Lock(m);
|
|
t1.Read8(l);
|
|
t1.Unlock(m);
|
|
|
|
t2.Read8(l, true);
|
|
}
|
|
t0.Destroy(m);
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, LockedWriteThenRead) {
|
|
UserMutex m(UserMutex::RW);
|
|
MainThread t0;
|
|
t0.Create(m);
|
|
MemLoc l;
|
|
{
|
|
ScopedThread t1, t2;
|
|
|
|
t1.Lock(m);
|
|
t1.Write8(l);
|
|
t1.Unlock(m);
|
|
|
|
t1.Read8(l);
|
|
|
|
t2.Read8(l, true);
|
|
}
|
|
t0.Destroy(m);
|
|
}
|
|
|
|
|
|
TEST_F(ThreadSanitizer, RaceWithOffset) {
|
|
ScopedThread t1, t2;
|
|
{
|
|
MemLoc l;
|
|
t1.Access(l.loc(), true, 8, false);
|
|
t2.Access((char*)l.loc() + 4, true, 4, true);
|
|
}
|
|
{
|
|
MemLoc l;
|
|
t1.Access(l.loc(), true, 8, false);
|
|
t2.Access((char*)l.loc() + 7, true, 1, true);
|
|
}
|
|
{
|
|
MemLoc l;
|
|
t1.Access((char*)l.loc() + 4, true, 4, false);
|
|
t2.Access((char*)l.loc() + 4, true, 2, true);
|
|
}
|
|
{
|
|
MemLoc l;
|
|
t1.Access((char*)l.loc() + 4, true, 4, false);
|
|
t2.Access((char*)l.loc() + 6, true, 2, true);
|
|
}
|
|
{
|
|
MemLoc l;
|
|
t1.Access((char*)l.loc() + 3, true, 2, false);
|
|
t2.Access((char*)l.loc() + 4, true, 1, true);
|
|
}
|
|
{
|
|
MemLoc l;
|
|
t1.Access((char*)l.loc() + 1, true, 8, false);
|
|
t2.Access((char*)l.loc() + 3, true, 1, true);
|
|
}
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, RaceWithOffset2) {
|
|
ScopedThread t1, t2;
|
|
{
|
|
MemLoc l;
|
|
t1.Access((char*)l.loc(), true, 4, false);
|
|
t2.Access((char*)l.loc() + 2, true, 1, true);
|
|
}
|
|
{
|
|
MemLoc l;
|
|
t1.Access((char*)l.loc() + 2, true, 1, false);
|
|
t2.Access((char*)l.loc(), true, 4, true);
|
|
}
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, NoRaceWithOffset) {
|
|
ScopedThread t1, t2;
|
|
{
|
|
MemLoc l;
|
|
t1.Access(l.loc(), true, 4, false);
|
|
t2.Access((char*)l.loc() + 4, true, 4, false);
|
|
}
|
|
{
|
|
MemLoc l;
|
|
t1.Access((char*)l.loc() + 3, true, 2, false);
|
|
t2.Access((char*)l.loc() + 1, true, 2, false);
|
|
t2.Access((char*)l.loc() + 5, true, 2, false);
|
|
}
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, RaceWithDeadThread) {
|
|
MemLoc l;
|
|
ScopedThread t;
|
|
ScopedThread().Write1(l);
|
|
t.Write1(l, true);
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, BenignRaceOnVptr) {
|
|
void *vptr_storage;
|
|
MemLoc vptr(&vptr_storage), val;
|
|
vptr_storage = val.loc();
|
|
ScopedThread t1, t2;
|
|
t1.VptrUpdate(vptr, val);
|
|
t2.Read8(vptr);
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, HarmfulRaceOnVptr) {
|
|
void *vptr_storage;
|
|
MemLoc vptr(&vptr_storage), val1, val2;
|
|
vptr_storage = val1.loc();
|
|
ScopedThread t1, t2;
|
|
t1.VptrUpdate(vptr, val2);
|
|
t2.Read8(vptr, true);
|
|
}
|
|
|
|
static void foo() {
|
|
volatile int x = 42;
|
|
int x2 = x;
|
|
(void)x2;
|
|
}
|
|
|
|
static void bar() {
|
|
volatile int x = 43;
|
|
int x2 = x;
|
|
(void)x2;
|
|
}
|
|
|
|
TEST_F(ThreadSanitizer, ReportDeadThread) {
|
|
MemLoc l;
|
|
ScopedThread t1;
|
|
{
|
|
ScopedThread t2;
|
|
t2.Call(&foo);
|
|
t2.Call(&bar);
|
|
t2.Write1(l);
|
|
}
|
|
t1.Write1(l, true);
|
|
}
|
|
|
|
struct ClassWithStatic {
|
|
static int Data[4];
|
|
};
|
|
|
|
int ClassWithStatic::Data[4];
|
|
|
|
static void foobarbaz() {}
|
|
|
|
TEST_F(ThreadSanitizer, ReportRace) {
|
|
ScopedThread t1;
|
|
MainThread().Access(&ClassWithStatic::Data, true, 4, false);
|
|
t1.Call(&foobarbaz);
|
|
t1.Access(&ClassWithStatic::Data, true, 2, true);
|
|
t1.Return();
|
|
}
|