diff options
Diffstat (limited to 'src/tests')
-rw-r--r-- | src/tests/frag_unittest.cc | 18 | ||||
-rw-r--r-- | src/tests/heap-checker_unittest.cc | 8 | ||||
-rw-r--r-- | src/tests/malloc_hook_test.cc | 345 | ||||
-rw-r--r-- | src/tests/memalign_unittest.cc | 1 | ||||
-rw-r--r-- | src/tests/page_heap_test.cc | 4 | ||||
-rw-r--r-- | src/tests/realloc_unittest.cc | 7 | ||||
-rw-r--r-- | src/tests/system-alloc_unittest.cc | 28 | ||||
-rw-r--r-- | src/tests/tcmalloc_large_unittest.cc | 8 |
8 files changed, 398 insertions, 21 deletions
diff --git a/src/tests/frag_unittest.cc b/src/tests/frag_unittest.cc index 160c41c..5ba02bd 100644 --- a/src/tests/frag_unittest.cc +++ b/src/tests/frag_unittest.cc @@ -50,8 +50,8 @@ using std::vector; int main(int argc, char** argv) { - // Make kAllocSize larger than tcmalloc page size. - static const int kAllocSize = 9 << kPageShift; + // Make kAllocSize one page larger than the maximum small object size. + static const int kAllocSize = kMaxSize + kPageSize; // Allocate 400MB in total. static const int kTotalAlloc = 400 << 20; static const int kAllocIterations = kTotalAlloc / kAllocSize; @@ -62,6 +62,11 @@ int main(int argc, char** argv) { saved[i] = new char[kAllocSize]; } + // Check the current "slack". + size_t slack_before; + MallocExtension::instance()->GetNumericProperty("tcmalloc.slack_bytes", + &slack_before); + // Free alternating ones to fragment heap size_t free_bytes = 0; for (int i = 0; i < saved.size(); i += 2) { @@ -69,10 +74,13 @@ int main(int argc, char** argv) { free_bytes += kAllocSize; } - // Check that slack is within 10% of expected - size_t slack; + // Check that slack delta is within 10% of expected. + size_t slack_after; MallocExtension::instance()->GetNumericProperty("tcmalloc.slack_bytes", - &slack); + &slack_after); + CHECK_GE(slack_after, slack_before); + size_t slack = slack_after - slack_before; + CHECK_GT(double(slack), 0.9*free_bytes); CHECK_LT(double(slack), 1.1*free_bytes); diff --git a/src/tests/heap-checker_unittest.cc b/src/tests/heap-checker_unittest.cc index e33e068..404c9f1 100644 --- a/src/tests/heap-checker_unittest.cc +++ b/src/tests/heap-checker_unittest.cc @@ -86,15 +86,17 @@ #include <pwd.h> #endif +#include <algorithm> #include <iostream> // for cout #include <iomanip> // for hex -#include <set> -#include <map> #include <list> +#include <map> #include <memory> -#include <vector> +#include <set> #include <string> +#include <vector> +#include "base/commandlineflags.h" #include "base/googleinit.h" #include "base/logging.h" #include "base/commandlineflags.h" diff --git a/src/tests/malloc_hook_test.cc b/src/tests/malloc_hook_test.cc new file mode 100644 index 0000000..dc65b68 --- /dev/null +++ b/src/tests/malloc_hook_test.cc @@ -0,0 +1,345 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// ---- +// Author: llib@google.com (Bill Clarke) + +#include "config_for_unittests.h" +#include <assert.h> +#include <stdio.h> +#ifdef HAVE_MMAP +#include <sys/mman.h> +#endif +#include <algorithm> +#include <string> +#include <vector> +#include <google/malloc_hook.h> +#include "malloc_hook-inl.h" +#include "base/logging.h" +#include "base/spinlock.h" +#include "base/sysinfo.h" +#include "tests/testutil.h" + +namespace { + +using std::string; +using std::vector; + +vector<void (*)()> g_testlist; // the tests to run + +#define TEST(a, b) \ + struct Test_##a##_##b { \ + Test_##a##_##b() { g_testlist.push_back(&Run); } \ + static void Run(); \ + }; \ + static Test_##a##_##b g_test_##a##_##b; \ + void Test_##a##_##b::Run() + + +static int RUN_ALL_TESTS() { + vector<void (*)()>::const_iterator it; + for (it = g_testlist.begin(); it != g_testlist.end(); ++it) { + (*it)(); // The test will error-exit if there's a problem. + } + fprintf(stderr, "\nPassed %d tests\n\nPASS\n", + static_cast<int>(g_testlist.size())); + return 0; +} + +using base::internal::kHookListMaxValues; + +// Since HookList is a template and is defined in malloc_hook.cc, we can only +// use an instantiation of it from malloc_hook.cc. We then reinterpret those +// values as integers for testing. +typedef base::internal::HookList<MallocHook::NewHook> TestHookList; + +int TestHookList_Traverse(const TestHookList& list, int* output_array, int n) { + MallocHook::NewHook values_as_hooks[kHookListMaxValues]; + int result = list.Traverse(values_as_hooks, std::min(n, kHookListMaxValues)); + for (int i = 0; i < result; ++i) { + output_array[i] = reinterpret_cast<const int&>(values_as_hooks[i]); + } + return result; +} + +bool TestHookList_Add(TestHookList* list, int val) { + return list->Add(reinterpret_cast<MallocHook::NewHook>(val)); +} + +bool TestHookList_Remove(TestHookList* list, int val) { + return list->Remove(reinterpret_cast<MallocHook::NewHook>(val)); +} + +// Note that this is almost the same as INIT_HOOK_LIST in malloc_hook.cc without +// the cast. +#define INIT_HOOK_LIST(initial_value) { 1, { initial_value } } + +TEST(HookListTest, InitialValueExists) { + TestHookList list = INIT_HOOK_LIST(69); + int values[2] = { 0, 0 }; + EXPECT_EQ(1, TestHookList_Traverse(list, values, 2)); + EXPECT_EQ(69, values[0]); + EXPECT_EQ(1, list.priv_end); +} + +TEST(HookListTest, CanRemoveInitialValue) { + TestHookList list = INIT_HOOK_LIST(69); + ASSERT_TRUE(TestHookList_Remove(&list, 69)); + EXPECT_EQ(0, list.priv_end); + + int values[2] = { 0, 0 }; + EXPECT_EQ(0, TestHookList_Traverse(list, values, 2)); +} + +TEST(HookListTest, AddAppends) { + TestHookList list = INIT_HOOK_LIST(69); + ASSERT_TRUE(TestHookList_Add(&list, 42)); + EXPECT_EQ(2, list.priv_end); + + int values[2] = { 0, 0 }; + EXPECT_EQ(2, TestHookList_Traverse(list, values, 2)); + EXPECT_EQ(69, values[0]); + EXPECT_EQ(42, values[1]); +} + +TEST(HookListTest, RemoveWorksAndWillClearSize) { + TestHookList list = INIT_HOOK_LIST(69); + ASSERT_TRUE(TestHookList_Add(&list, 42)); + + ASSERT_TRUE(TestHookList_Remove(&list, 69)); + EXPECT_EQ(2, list.priv_end); + + int values[2] = { 0, 0 }; + EXPECT_EQ(1, TestHookList_Traverse(list, values, 2)); + EXPECT_EQ(42, values[0]); + + ASSERT_TRUE(TestHookList_Remove(&list, 42)); + EXPECT_EQ(0, list.priv_end); + EXPECT_EQ(0, TestHookList_Traverse(list, values, 2)); +} + +TEST(HookListTest, AddPrependsAfterRemove) { + TestHookList list = INIT_HOOK_LIST(69); + ASSERT_TRUE(TestHookList_Add(&list, 42)); + + ASSERT_TRUE(TestHookList_Remove(&list, 69)); + EXPECT_EQ(2, list.priv_end); + + ASSERT_TRUE(TestHookList_Add(&list, 7)); + EXPECT_EQ(2, list.priv_end); + + int values[2] = { 0, 0 }; + EXPECT_EQ(2, TestHookList_Traverse(list, values, 2)); + EXPECT_EQ(7, values[0]); + EXPECT_EQ(42, values[1]); +} + +TEST(HookListTest, InvalidAddRejected) { + TestHookList list = INIT_HOOK_LIST(69); + EXPECT_FALSE(TestHookList_Add(&list, 0)); + + int values[2] = { 0, 0 }; + EXPECT_EQ(1, TestHookList_Traverse(list, values, 2)); + EXPECT_EQ(69, values[0]); + EXPECT_EQ(1, list.priv_end); +} + +TEST(HookListTest, FillUpTheList) { + TestHookList list = INIT_HOOK_LIST(69); + int num_inserts = 0; + while (TestHookList_Add(&list, ++num_inserts)) + ; + EXPECT_EQ(kHookListMaxValues, num_inserts); + EXPECT_EQ(kHookListMaxValues, list.priv_end); + + int values[kHookListMaxValues + 1]; + EXPECT_EQ(kHookListMaxValues, TestHookList_Traverse(list, values, + kHookListMaxValues)); + EXPECT_EQ(69, values[0]); + for (int i = 1; i < kHookListMaxValues; ++i) { + EXPECT_EQ(i, values[i]); + } +} + +void MultithreadedTestThread(TestHookList* list, int shift, + int thread_num) { + string message; + char buf[64]; + for (int i = 1; i < 1000; ++i) { + // In each loop, we insert a unique value, check it exists, remove it, and + // check it doesn't exist. We also record some stats to log at the end of + // each thread. Each insertion location and the length of the list is + // non-deterministic (except for the very first one, over all threads, and + // after the very last one the list should be empty). + int value = (i << shift) + thread_num; + EXPECT_TRUE(TestHookList_Add(list, value)); + sched_yield(); // Ensure some more interleaving. + int values[kHookListMaxValues + 1]; + int num_values = TestHookList_Traverse(*list, values, kHookListMaxValues); + EXPECT_LT(0, num_values); + int value_index; + for (value_index = 0; + value_index < num_values && values[value_index] != value; + ++value_index) + ; + EXPECT_LT(value_index, num_values); // Should have found value. + snprintf(buf, sizeof(buf), "[%d/%d; ", value_index, num_values); + message += buf; + sched_yield(); + EXPECT_TRUE(TestHookList_Remove(list, value)); + sched_yield(); + num_values = TestHookList_Traverse(*list, values, kHookListMaxValues); + for (value_index = 0; + value_index < num_values && values[value_index] != value; + ++value_index) + ; + EXPECT_EQ(value_index, num_values); // Should not have found value. + snprintf(buf, sizeof(buf), "%d]", num_values); + message += buf; + sched_yield(); + } + fprintf(stderr, "thread %d: %s\n", thread_num, message.c_str()); +} + +static volatile int num_threads_remaining; +static TestHookList list = INIT_HOOK_LIST(69); +static SpinLock threadcount_lock; + +void MultithreadedTestThreadRunner(int thread_num) { + // Wait for all threads to start running. + { + SpinLockHolder h(&threadcount_lock); + assert(num_threads_remaining > 0); + --num_threads_remaining; + + // We should use condvars and the like, but for this test, we'll + // go simple and busy-wait. + while (num_threads_remaining > 0) { + threadcount_lock.Unlock(); + SleepForMilliseconds(100); + threadcount_lock.Lock(); + } + } + + // shift is the smallest number such that (1<<shift) > kHookListMaxValues + int shift = 0; + for (int i = kHookListMaxValues; i > 0; i >>= 1) + shift += 1; + + MultithreadedTestThread(&list, shift, thread_num); +} + + +TEST(HookListTest, MultithreadedTest) { + ASSERT_TRUE(TestHookList_Remove(&list, 69)); + ASSERT_EQ(0, list.priv_end); + + // Run kHookListMaxValues thread, each running MultithreadedTestThread. + // First, we need to set up the rest of the globals. + num_threads_remaining = kHookListMaxValues; // a global var + RunManyThreadsWithId(&MultithreadedTestThreadRunner, num_threads_remaining, + 1 << 15); + + int values[kHookListMaxValues + 1]; + EXPECT_EQ(0, TestHookList_Traverse(list, values, kHookListMaxValues)); + EXPECT_EQ(0, list.priv_end); +} + +#ifdef HAVE_MMAP +int mmap_calls = 0; +int mmap_matching_calls = 0; +int munmap_calls = 0; +int munmap_matching_calls = 0; +const int kMmapMagicFd = 1; +void* const kMmapMagicPointer = reinterpret_cast<void*>(1); + +int MmapReplacement(const void* start, + size_t size, + int protection, + int flags, + int fd, + off_t offset, + void** result) { + ++mmap_calls; + if (fd == kMmapMagicFd) { + ++mmap_matching_calls; + *result = kMmapMagicPointer; + return true; + } + return false; +} + +int MunmapReplacement(const void* ptr, size_t size, int* result) { + ++munmap_calls; + if (ptr == kMmapMagicPointer) { + ++munmap_matching_calls; + *result = 0; + return true; + } + return false; +} + +TEST(MallocMookTest, MmapReplacements) { + mmap_calls = mmap_matching_calls = munmap_calls = munmap_matching_calls = 0; + MallocHook::SetMmapReplacement(&MmapReplacement); + MallocHook::SetMunmapReplacement(&MunmapReplacement); + EXPECT_EQ(kMmapMagicPointer, mmap(NULL, 1, PROT_READ, MAP_PRIVATE, + kMmapMagicFd, 0)); + EXPECT_EQ(1, mmap_matching_calls); + + char* ptr = reinterpret_cast<char*>( + mmap(NULL, 1, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + EXPECT_EQ(2, mmap_calls); + EXPECT_EQ(1, mmap_matching_calls); + ASSERT_NE(MAP_FAILED, ptr); + *ptr = 'a'; + + EXPECT_EQ(0, munmap(kMmapMagicPointer, 1)); + EXPECT_EQ(1, munmap_calls); + EXPECT_EQ(1, munmap_matching_calls); + + EXPECT_EQ(0, munmap(ptr, 1)); + EXPECT_EQ(2, munmap_calls); + EXPECT_EQ(1, munmap_matching_calls); + + // The DEATH test below is flaky, because we've just munmapped the memory, + // making it available for mmap()ing again. There is no guarantee that it + // will stay unmapped, and in fact it gets reused ~10% of the time. + // It the area is reused, then not only we don't die, but we also corrupt + // whoever owns that memory now. + // EXPECT_DEATH(*ptr = 'a', "SIGSEGV"); +} +#endif // #ifdef HAVE_MMAN + +} // namespace + +int main(int argc, char** argv) { + return RUN_ALL_TESTS(); +} diff --git a/src/tests/memalign_unittest.cc b/src/tests/memalign_unittest.cc index 1b707dd..b354bb4 100644 --- a/src/tests/memalign_unittest.cc +++ b/src/tests/memalign_unittest.cc @@ -49,6 +49,7 @@ #include <unistd.h> // for getpagesize() #endif #include "tcmalloc.h" // must come early, to pick up posix_memalign +#include <assert.h> #include <stdlib.h> // defines posix_memalign #include <stdio.h> // for the printf at the end #ifdef HAVE_STDINT_H diff --git a/src/tests/page_heap_test.cc b/src/tests/page_heap_test.cc index 9120b78..9f5f3c8 100644 --- a/src/tests/page_heap_test.cc +++ b/src/tests/page_heap_test.cc @@ -1,11 +1,11 @@ // Copyright 2009 Google Inc. All Rights Reserved. // Author: fikes@google.com (Andrew Fikes) -#include <stdio.h> #include "config_for_unittests.h" +#include "page_heap.h" +#include <stdio.h> #include "base/logging.h" #include "common.h" -#include "page_heap.h" namespace { diff --git a/src/tests/realloc_unittest.cc b/src/tests/realloc_unittest.cc index 20edb50..4267421 100644 --- a/src/tests/realloc_unittest.cc +++ b/src/tests/realloc_unittest.cc @@ -33,13 +33,16 @@ // Test realloc() functionality #include "config_for_unittests.h" +#include <assert.h> // for assert #include <stdio.h> -#include <stdlib.h> -#include <algorithm> // for min() +#include <stddef.h> // for size_t, NULL +#include <stdlib.h> // for free, malloc, realloc +#include <algorithm> // for min #include "base/logging.h" using std::min; + // Fill a buffer of the specified size with a predetermined pattern static void Fill(unsigned char* buffer, int n) { for (int i = 0; i < n; i++) { diff --git a/src/tests/system-alloc_unittest.cc b/src/tests/system-alloc_unittest.cc index da76285..c006425 100644 --- a/src/tests/system-alloc_unittest.cc +++ b/src/tests/system-alloc_unittest.cc @@ -31,6 +31,7 @@ // Author: Arun Sharma #include "config_for_unittests.h" +#include "system-alloc.h" #include <stdio.h> #if defined HAVE_STDINT_H #include <stdint.h> // to get uintptr_t @@ -39,9 +40,9 @@ #endif #include <sys/types.h> #include <algorithm> -#include "base/logging.h" -#include "common.h" -#include "system-alloc.h" +#include "base/logging.h" // for Check_GEImpl, Check_LTImpl, etc +#include <google/malloc_extension.h> // for MallocExtension::instance +#include "common.h" // for kAddressBits class ArraySysAllocator : public SysAllocator { public: @@ -55,6 +56,11 @@ public: void* Alloc(size_t size, size_t *actual_size, size_t alignment) { invoked_ = true; + + if (size > kArraySize) { + return NULL; + } + void *result = &array_[ptr_]; uintptr_t ptr = reinterpret_cast<uintptr_t>(result); @@ -75,8 +81,9 @@ public: return reinterpret_cast<void *>(ptr); } - void DumpStats(TCMalloc_Printer* printer) { + void DumpStats() { } + void FlagsInitialized() {} private: static const int kArraySize = 8 * 1024 * 1024; @@ -89,7 +96,7 @@ const int ArraySysAllocator::kArraySize; ArraySysAllocator a; static void TestBasicInvoked() { - RegisterSystemAllocator(&a, 0); + MallocExtension::instance()->SetSystemAllocator(&a); // An allocation size that is likely to trigger the system allocator. // XXX: this is implementation specific. @@ -112,8 +119,19 @@ TEST(AddressBits, CpuVirtualBits) { } #endif +static void TestBasicRetryFailTest() { + // Check with the allocator still works after a failed allocation. + void* p = malloc(1ULL << 50); // Asking for 1P ram + CHECK(p == NULL); + + char* q = new char[1024]; + CHECK(q != NULL); + delete [] q; +} + int main(int argc, char** argv) { TestBasicInvoked(); + TestBasicRetryFailTest(); printf("PASS\n"); return 0; diff --git a/src/tests/tcmalloc_large_unittest.cc b/src/tests/tcmalloc_large_unittest.cc index 260ac29..ad3482e 100644 --- a/src/tests/tcmalloc_large_unittest.cc +++ b/src/tests/tcmalloc_large_unittest.cc @@ -35,12 +35,12 @@ // For 32 bits, this means allocations near 2^32 bytes and 2^31 bytes. // For 64 bits, this means allocations near 2^64 bytes and 2^63 bytes. -#include <stddef.h> -#include <stdlib.h> +#include <stddef.h> // for size_t, NULL +#include <stdlib.h> // for malloc, free, realloc #include <stdio.h> -#include <set> +#include <set> // for set, etc -#include "base/logging.h" +#include "base/logging.h" // for operator<<, CHECK, etc using std::set; |