// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/message_loop.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { namespace { // We use caps here just to ensure that the method name doesn't interfere with // the wildcarded suppressions. class TOOLS_SANITY_TEST_CONCURRENT_THREAD : public PlatformThread::Delegate { public: explicit TOOLS_SANITY_TEST_CONCURRENT_THREAD(bool *value) : value_(value) {} ~TOOLS_SANITY_TEST_CONCURRENT_THREAD() {} void ThreadMain() { *value_ = true; // Sleep for a few milliseconds so the two threads are more likely to live // simultaneously. Otherwise we may miss the report due to mutex // lock/unlock's inside thread creation code in pure-happens-before mode... PlatformThread::Sleep(100); } private: bool *value_; }; void ReadUninitializedValue(char *ptr) { if (*ptr == '\0') { (*ptr)++; } else { (*ptr)--; } } void ReadValueOutOfArrayBoundsLeft(char *ptr) { char c = ptr[-2]; VLOG(1) << "Reading a byte out of bounds: " << c; } void ReadValueOutOfArrayBoundsRight(char *ptr, size_t size) { char c = ptr[size + 1]; VLOG(1) << "Reading a byte out of bounds: " << c; } // This is harmless if you run it under Valgrind thanks to redzones. void WriteValueOutOfArrayBoundsLeft(char *ptr) { ptr[-1] = 42; } // This is harmless if you run it under Valgrind thanks to redzones. void WriteValueOutOfArrayBoundsRight(char *ptr, size_t size) { ptr[size] = 42; } void MakeSomeErrors(char *ptr, size_t size) { ReadUninitializedValue(ptr); ReadValueOutOfArrayBoundsLeft(ptr); ReadValueOutOfArrayBoundsRight(ptr, size); WriteValueOutOfArrayBoundsLeft(ptr); WriteValueOutOfArrayBoundsRight(ptr, size); } } // namespace // A memory leak detector should report an error in this test. TEST(ToolsSanityTest, MemoryLeak) { int *leak = new int[256]; // Leak some memory intentionally. leak[4] = 1; // Make sure the allocated memory is used. } TEST(ToolsSanityTest, AccessesToNewMemory) { // This test may corrupt memory if not run under Valgrind. if (!RunningOnValgrind()) return; char *foo = new char[10]; MakeSomeErrors(foo, 10); delete [] foo; foo[5] = 0; // Use after delete. This won't break anything under Valgrind. } TEST(ToolsSanityTest, AccessesToMallocMemory) { // This test may corrupt memory if not run under Valgrind. if (!RunningOnValgrind()) return; char *foo = reinterpret_cast<char*>(malloc(10)); MakeSomeErrors(foo, 10); free(foo); foo[5] = 0; // Use after free. This won't break anything under Valgrind. } TEST(ToolsSanityTest, ArrayDeletedWithoutBraces) { // This test may corrupt memory if not run under Valgrind. if (!RunningOnValgrind()) return; int *foo = new int[10]; delete foo; } TEST(ToolsSanityTest, SingleElementDeletedWithBraces) { // This test may corrupt memory if not run under Valgrind. if (!RunningOnValgrind()) return; int *foo = new int; delete [] foo; } // A data race detector should report an error in this test. TEST(ToolsSanityTest, DataRace) { bool shared = false; PlatformThreadHandle a; PlatformThreadHandle b; PlatformThread::Delegate *thread1 = new TOOLS_SANITY_TEST_CONCURRENT_THREAD(&shared); PlatformThread::Delegate *thread2 = new TOOLS_SANITY_TEST_CONCURRENT_THREAD(&shared); PlatformThread::Create(0, thread1, &a); PlatformThread::Create(0, thread2, &b); PlatformThread::Join(a); PlatformThread::Join(b); EXPECT_TRUE(shared); delete thread1; delete thread2; } } // namespace base