blob: 48a49578786e6a09cf4da15305083a0ae2a146e7 [file] [log] [blame]
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2008-2008 Google Inc
opensource@google.com
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
/* Author: Konstantin Serebryany <opensource@google.com>
Author: Timur Iskhodzhanov <opensource@google.com>
This file contains a set of demonstration tests for ThreadSanitizer.
*/
#include <gtest/gtest.h>
#include "test_utils.h"
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
namespace RaceReportDemoTest { // {{{1
Mutex mu1; // This Mutex guards var.
Mutex mu2; // This Mutex is not related to var.
int var; // GUARDED_BY(mu1)
void Thread1() { // Runs in thread named 'test-thread-1'.
MutexLock lock(&mu1); // Correct Mutex.
var = 1;
}
void Thread2() { // Runs in thread named 'test-thread-2'.
MutexLock lock(&mu2); // Wrong Mutex.
var = 2;
}
TEST(DemoTests, RaceReportDemoTest) {
ANNOTATE_TRACE_MEMORY(&var);
var = 0;
MyThread t1(Thread1, NULL, "test-thread-1");
MyThread t2(Thread2, NULL, "test-thread-2");
t1.Start();
t2.Start();
t1.Join();
t2.Join();
}
} // namespace RaceReportDemoTest
// test302: Complex race which happens at least twice. {{{1
namespace test302 {
// In this test we have many different accesses to GLOB and only one access
// is not synchronized properly.
int GLOB = 0;
Mutex MU1;
Mutex MU2;
void Worker() {
for(int i = 0; i < 100; i++) {
switch(i % 4) {
case 0:
// This read is protected correctly.
MU1.Lock(); CHECK(GLOB >= 0); MU1.Unlock();
break;
case 1:
// Here we used the wrong lock! The reason of the race is here.
MU2.Lock(); CHECK(GLOB >= 0); MU2.Unlock();
break;
case 2:
// This read is protected correctly.
MU1.Lock(); CHECK(GLOB >= 0); MU1.Unlock();
break;
case 3:
// This write is protected correctly.
MU1.Lock(); GLOB++; MU1.Unlock();
break;
}
// sleep a bit so that the threads interleave
// and the race happens at least twice.
usleep(100);
}
}
TEST(DemoTests, test302) {
printf("test302: Complex race that happens twice.\n");
MyThread t1(Worker), t2(Worker);
t1.Start();
t2.Start();
t1.Join(); t2.Join();
}
} // namespace test302
// test303: Need to trace the memory to understand the report. {{{1
namespace test303 {
int GLOB = 0;
Mutex MU;
void Worker1() { CHECK(GLOB >= 0); }
void Worker2() { MU.Lock(); GLOB=1; MU.Unlock();}
TEST(DemoTests, test303) {
printf("test303: a race that needs annotations.\n");
ANNOTATE_TRACE_MEMORY(&GLOB);
MyThreadArray t(Worker1, Worker2);
t.Start();
t.Join();
}
} // namespace test303
// test304: Can not trace the memory, since it is a library object. {{{1
namespace test304 {
string *STR;
Mutex MU;
void Worker1() {
sleep(0);
ANNOTATE_CONDVAR_SIGNAL((void*)0xDEADBEAF);
MU.Lock(); CHECK(STR->length() >= 4); MU.Unlock();
}
void Worker2() {
sleep(1);
ANNOTATE_CONDVAR_SIGNAL((void*)0xDEADBEAF);
CHECK(STR->length() >= 4); // Unprotected!
}
void Worker3() {
sleep(2);
ANNOTATE_CONDVAR_SIGNAL((void*)0xDEADBEAF);
MU.Lock(); CHECK(STR->length() >= 4); MU.Unlock();
}
void Worker4() {
sleep(3);
ANNOTATE_CONDVAR_SIGNAL((void*)0xDEADBEAF);
MU.Lock(); *STR += " + a very very long string"; MU.Unlock();
}
TEST(DemoTests, test304) {
STR = new string ("The String");
printf("test304: a race where memory tracing does not work.\n");
MyThreadArray t(Worker1, Worker2, Worker3, Worker4);
t.Start();
t.Join();
printf("%s\n", STR->c_str());
delete STR;
}
} // namespace test304
// test305: A bit more tricky: two locks used inconsistenly. {{{1
namespace test305 {
int GLOB = 0;
// In this test GLOB is protected by MU1 and MU2, but inconsistently.
// The TRACES observed by helgrind are:
// TRACE[1]: Access{T2/S2 wr} -> new State{Mod; #LS=2; #SS=1; T2/S2}
// TRACE[2]: Access{T4/S9 wr} -> new State{Mod; #LS=1; #SS=2; T2/S2, T4/S9}
// TRACE[3]: Access{T5/S13 wr} -> new State{Mod; #LS=1; #SS=3; T2/S2, T4/S9, T5/S13}
// TRACE[4]: Access{T6/S19 wr} -> new State{Mod; #LS=0; #SS=4; T2/S2, T4/S9, T5/S13, T6/S19}
//
// The guilty access is either Worker2() or Worker4(), depending on
// which mutex is supposed to protect GLOB.
Mutex MU1;
Mutex MU2;
void Worker1() { MU1.Lock(); MU2.Lock(); GLOB=1; MU2.Unlock(); MU1.Unlock(); }
void Worker2() { MU1.Lock(); GLOB=2; MU1.Unlock(); }
void Worker3() { MU1.Lock(); MU2.Lock(); GLOB=3; MU2.Unlock(); MU1.Unlock(); }
void Worker4() { MU2.Lock(); GLOB=4; MU2.Unlock(); }
TEST(DemoTests, test305) {
ANNOTATE_TRACE_MEMORY(&GLOB);
printf("test305: simple race.\n");
MyThread t1(Worker1), t2(Worker2), t3(Worker3), t4(Worker4);
t1.Start(); usleep(100);
t2.Start(); usleep(100);
t3.Start(); usleep(100);
t4.Start(); usleep(100);
t1.Join(); t2.Join(); t3.Join(); t4.Join();
}
} // namespace test305
// test306: Two locks are used to protect a var. {{{1
namespace test306 {
int GLOB = 0;
// Thread1 and Thread2 access the var under two locks.
// Thread3 uses no locks.
Mutex MU1;
Mutex MU2;
void Worker1() { MU1.Lock(); MU2.Lock(); GLOB=1; MU2.Unlock(); MU1.Unlock(); }
void Worker2() { MU1.Lock(); MU2.Lock(); GLOB=3; MU2.Unlock(); MU1.Unlock(); }
void Worker3() { GLOB=4; }
TEST(DemoTests, test306) {
ANNOTATE_TRACE_MEMORY(&GLOB);
printf("test306: simple race.\n");
MyThread t1(Worker1), t2(Worker2), t3(Worker3);
t1.Start(); usleep(100);
t2.Start(); usleep(100);
t3.Start(); usleep(100);
t1.Join(); t2.Join(); t3.Join();
}
} // namespace test306
// test307: Simple race, code with control flow {{{1
namespace test307 {
int *GLOB = 0;
volatile /*to fake the compiler*/ bool some_condition = true;
void SomeFunc() { }
int FunctionWithControlFlow() {
int unrelated_stuff = 0;
unrelated_stuff++;
SomeFunc(); // "--keep-history=1" will point somewhere here.
if (some_condition) { // Or here
if (some_condition) {
unrelated_stuff++; // Or here.
unrelated_stuff++;
(*GLOB)++; // "--keep-history=2" will point here (experimental).
}
}
usleep(100000);
return unrelated_stuff;
}
void Worker1() { FunctionWithControlFlow(); }
void Worker2() { Worker1(); }
void Worker3() { Worker2(); }
void Worker4() { Worker3(); }
TEST(DemoTests, test307) {
GLOB = new int;
*GLOB = 1;
printf("test307: simple race, code with control flow\n");
MyThreadArray t1(Worker1, Worker2, Worker3, Worker4);
t1.Start();
t1.Join();
}
} // namespace test307
// test308: Example of double-checked-locking {{{1
namespace test308 {
struct Foo {
int a;
};
static int is_inited = 0;
static Mutex lock;
static Foo *foo;
void InitMe() {
if (!is_inited) {
lock.Lock();
if (!is_inited) {
foo = new Foo;
foo->a = 42;
is_inited = 1;
}
lock.Unlock();
}
}
void UseMe() {
InitMe();
CHECK(foo && foo->a == 42);
}
void Worker1() { UseMe(); }
void Worker2() { UseMe(); }
void Worker3() { UseMe(); }
TEST(DemoTests, test308) {
ANNOTATE_TRACE_MEMORY(&is_inited);
printf("test308: Example of double-checked-locking\n");
MyThreadArray t1(Worker1, Worker2, Worker3);
t1.Start();
t1.Join();
}
} // namespace test308
// test309: Simple race on an STL object. {{{1
namespace test309 {
string GLOB;
void Worker1() {
GLOB="Thread1";
}
void Worker2() {
usleep(100000);
GLOB="Booooooooooo";
}
TEST(DemoTests, test309) {
printf("test309: simple race on an STL object.\n");
MyThread t1(Worker1), t2(Worker2);
t1.Start();
t2.Start();
t1.Join(); t2.Join();
}
} // namespace test309
// test310: One more simple race. {{{1
namespace test310 {
int *PTR = NULL; // GUARDED_BY(mu1)
Mutex mu1; // Protects PTR.
Mutex mu2; // Unrelated to PTR.
Mutex mu3; // Unrelated to PTR.
void Writer1() {
MutexLock lock3(&mu3); // This lock is unrelated to PTR.
MutexLock lock1(&mu1); // Protect PTR.
*PTR = 1;
}
void Writer2() {
MutexLock lock2(&mu2); // This lock is unrelated to PTR.
MutexLock lock1(&mu1); // Protect PTR.
int some_unrelated_stuff = 0;
if (some_unrelated_stuff == 0)
some_unrelated_stuff++;
*PTR = 2;
}
void Reader() {
MutexLock lock2(&mu2); // Oh, gosh, this is a wrong mutex!
CHECK(*PTR <= 2);
}
// Some functions to make the stack trace non-trivial.
void DoWrite1() { Writer1(); }
void Thread1() { DoWrite1(); }
void DoWrite2() { Writer2(); }
void Thread2() { DoWrite2(); }
void DoRead() { Reader(); }
void Thread3() { DoRead(); }
TEST(DemoTests, test310) {
printf("test310: simple race.\n");
PTR = new int;
ANNOTATE_TRACE_MEMORY(PTR);
*PTR = 0;
MyThread t1(Thread1, NULL, "writer1"),
t2(Thread2, NULL, "writer2"),
t3(Thread3, NULL, "buggy reader");
t1.Start();
t2.Start();
usleep(100000); // Let the writers go first.
t3.Start();
t1.Join();
t2.Join();
t3.Join();
}
} // namespace test310
// test311: Yet another simple race. {{{1
namespace test311 {
int *PTR = NULL; // GUARDED_BY(mu1)
Mutex mu1; // Protects PTR.
Mutex mu2; // Unrelated to PTR.
Mutex mu3; // Unrelated to PTR.
void GoodWriter1() { // Runs in Thread1
MutexLock lock3(&mu3); // This lock is unrelated to PTR.
MutexLock lock1(&mu1); // Protect PTR.
*PTR = 1;
}
void GoodWriter2() { // Runs in Thread2
MutexLock lock2(&mu2); // This lock is unrelated to PTR.
MutexLock lock1(&mu1); // Protect PTR.
*PTR = 2;
}
void GoodReader() { // Runs in Thread3
MutexLock lock1(&mu1); // Protect PTR.
CHECK(*PTR >= 0);
}
void BuggyWriter() { // Runs in Thread4
MutexLock lock2(&mu2); // Wrong mutex!
*PTR = 3;
}
// Some functions to make the stack trace non-trivial.
void DoWrite1() { GoodWriter1(); }
void Thread1() { DoWrite1(); }
void DoWrite2() { GoodWriter2(); }
void Thread2() { DoWrite2(); }
void DoGoodRead() { GoodReader(); }
void Thread3() { DoGoodRead(); }
void DoBadWrite() { BuggyWriter(); }
void Thread4() { DoBadWrite(); }
TEST(DemoTests, test311) {
printf("test311: simple race.\n");
PTR = new int;
ANNOTATE_TRACE_MEMORY(PTR);
*PTR = 0;
MyThread t1(Thread1, NULL, "good writer1"),
t2(Thread2, NULL, "good writer2"),
t3(Thread3, NULL, "good reader"),
t4(Thread4, NULL, "buggy writer");
t1.Start();
t3.Start();
// t2 goes after t3. This way a pure happens-before detector has no chance.
usleep(10000);
t2.Start();
usleep(100000); // Let the good folks go first.
t4.Start();
t1.Join();
t2.Join();
t3.Join();
t4.Join();
}
} // namespace test311
// test312: A test with a very deep stack. {{{1
namespace test312 {
int GLOB = 0;
void RaceyWrite() { GLOB++; }
void Func1() { RaceyWrite(); }
void Func2() { Func1(); }
void Func3() { Func2(); }
void Func4() { Func3(); }
void Func5() { Func4(); }
void Func6() { Func5(); }
void Func7() { Func6(); }
void Func8() { Func7(); }
void Func9() { Func8(); }
void Func10() { Func9(); }
void Func11() { Func10(); }
void Func12() { Func11(); }
void Func13() { Func12(); }
void Func14() { Func13(); }
void Func15() { Func14(); }
void Func16() { Func15(); }
void Func17() { Func16(); }
void Func18() { Func17(); }
void Func19() { Func18(); }
void Worker() { Func19(); }
TEST(DemoTests, test312) {
printf("test312: simple race with deep stack.\n");
MyThreadArray t(Worker, Worker, Worker);
t.Start();
t.Join();
}
} // namespace test312
// test313 TP: test for thread graph output {{{1
namespace test313 {
BlockingCounter *blocking_counter;
int GLOB = 0;
// Worker(N) will do 2^N increments of GLOB, each increment in a separate thread
void Worker(int depth) {
CHECK(depth >= 0);
if (depth > 0) {
ThreadPool pool(2);
pool.StartWorkers();
pool.Add(NewCallback(Worker, depth-1));
pool.Add(NewCallback(Worker, depth-1));
} else {
GLOB++; // Race here
}
}
TEST(DemoTests, test313) {
printf("test313: positive\n");
Worker(4);
printf("\tGLOB=%d\n", GLOB);
}
} // namespace test313
// test314: minimalistic test for race in vptr. {{{1
namespace test314 {
// Race on vptr. Will run A::F() or B::F() depending on the timing.
class A {
public:
A() : done_(false) { }
virtual void F() {
printf ("A::F()\n");
}
void Done() {
MutexLock lock(&mu_);
done_ = true;
}
virtual ~A() {
while (true) {
MutexLock lock(&mu_);
if (done_) break;
}
}
private:
Mutex mu_;
bool done_;
};
class B : public A {
public:
virtual void F() {
printf ("B::F()\n");
}
};
static A *a;
void Thread1() {
a->F();
a->Done();
};
void Thread2() {
delete a;
}
TEST(DemoTests, test314) {
printf("test314: race on vptr; May print A::F() or B::F().\n");
{ // Will print B::F()
a = new B;
MyThreadArray t(Thread1, Thread2);
t.Start();
t.Join();
}
{ // Will print A::F()
a = new B;
MyThreadArray t(Thread2, Thread1);
t.Start();
t.Join();
}
}
} // namespace test314
// test315: demo for hybrid's false positive. {{{1
namespace test315 {
int GLOB;
Mutex mu;
int flag;
void Thread1() {
sleep(1);
mu.Lock();
bool f = flag;
mu.Unlock();
if (f) {
GLOB++;
}
}
void Thread2() {
GLOB++;
mu.Lock();
flag = true;
mu.Unlock();
}
TEST(DemoTests, test315) {
GLOB = 0;
printf("test315: false positive of the hybrid state machine\n");
MyThreadArray t(Thread1, Thread2);
t.Start();
t.Join();
}
} // namespace test315