/* 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