| OLD | NEW |
| 1 // Copyright (c) 2011, Google Inc. | 1 // Copyright (c) 2011, Google Inc. |
| 2 // All rights reserved. | 2 // All rights reserved. |
| 3 // | 3 // |
| 4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
| 5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
| 6 // met: | 6 // met: |
| 7 // | 7 // |
| 8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
| 9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
| 10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 | 29 |
| 30 // ---- | 30 // ---- |
| 31 // Author: llib@google.com (Bill Clarke) | 31 // Author: llib@google.com (Bill Clarke) |
| 32 | 32 |
| 33 #include "config_for_unittests.h" | 33 #include "config_for_unittests.h" |
| 34 #include <assert.h> | 34 #include <assert.h> |
| 35 #include <stdio.h> | 35 #include <stdio.h> |
| 36 #ifdef HAVE_MMAP | 36 #ifdef HAVE_MMAP |
| 37 #include <sys/mman.h> | 37 #include <sys/mman.h> |
| 38 #endif | 38 #endif |
| 39 #ifdef HAVE_UNISTD_H |
| 40 #include <unistd.h> // for sleep() |
| 41 #endif |
| 39 #include <algorithm> | 42 #include <algorithm> |
| 40 #include <string> | 43 #include <string> |
| 41 #include <vector> | 44 #include <vector> |
| 42 #include <google/malloc_hook.h> | 45 #include <gperftools/malloc_hook.h> |
| 43 #include "malloc_hook-inl.h" | 46 #include "malloc_hook-inl.h" |
| 44 #include "base/logging.h" | 47 #include "base/logging.h" |
| 45 #include "base/spinlock.h" | 48 #include "base/simple_mutex.h" |
| 46 #include "base/sysinfo.h" | 49 #include "base/sysinfo.h" |
| 47 #include "tests/testutil.h" | 50 #include "tests/testutil.h" |
| 48 | 51 |
| 52 // On systems (like freebsd) that don't define MAP_ANONYMOUS, use the old |
| 53 // form of the name instead. |
| 54 #ifndef MAP_ANONYMOUS |
| 55 # define MAP_ANONYMOUS MAP_ANON |
| 56 #endif |
| 57 |
| 49 namespace { | 58 namespace { |
| 50 | 59 |
| 51 using std::string; | 60 using std::string; |
| 52 using std::vector; | 61 using std::vector; |
| 53 | 62 |
| 54 vector<void (*)()> g_testlist; // the tests to run | 63 vector<void (*)()> g_testlist; // the tests to run |
| 55 | 64 |
| 56 #define TEST(a, b) \ | 65 #define TEST(a, b) \ |
| 57 struct Test_##a##_##b { \ | 66 struct Test_##a##_##b { \ |
| 58 Test_##a##_##b() { g_testlist.push_back(&Run); } \ | 67 Test_##a##_##b() { g_testlist.push_back(&Run); } \ |
| 59 static void Run(); \ | 68 static void Run(); \ |
| 60 }; \ | 69 }; \ |
| 61 static Test_##a##_##b g_test_##a##_##b; \ | 70 static Test_##a##_##b g_test_##a##_##b; \ |
| 62 void Test_##a##_##b::Run() | 71 void Test_##a##_##b::Run() |
| 63 | 72 |
| 64 | 73 |
| 65 static int RUN_ALL_TESTS() { | 74 static int RUN_ALL_TESTS() { |
| 66 vector<void (*)()>::const_iterator it; | 75 vector<void (*)()>::const_iterator it; |
| 67 for (it = g_testlist.begin(); it != g_testlist.end(); ++it) { | 76 for (it = g_testlist.begin(); it != g_testlist.end(); ++it) { |
| 68 (*it)(); // The test will error-exit if there's a problem. | 77 (*it)(); // The test will error-exit if there's a problem. |
| 69 } | 78 } |
| 70 fprintf(stderr, "\nPassed %d tests\n\nPASS\n", | 79 fprintf(stderr, "\nPassed %d tests\n\nPASS\n", |
| 71 static_cast<int>(g_testlist.size())); | 80 static_cast<int>(g_testlist.size())); |
| 72 return 0; | 81 return 0; |
| 73 } | 82 } |
| 74 | 83 |
| 84 void Sleep(int seconds) { |
| 85 #ifdef _MSC_VER |
| 86 _sleep(seconds * 1000); // Windows's _sleep takes milliseconds argument |
| 87 #else |
| 88 sleep(seconds); |
| 89 #endif |
| 90 } |
| 91 |
| 92 using std::min; |
| 75 using base::internal::kHookListMaxValues; | 93 using base::internal::kHookListMaxValues; |
| 76 | 94 |
| 77 // Since HookList is a template and is defined in malloc_hook.cc, we can only | 95 // Since HookList is a template and is defined in malloc_hook.cc, we can only |
| 78 // use an instantiation of it from malloc_hook.cc. We then reinterpret those | 96 // use an instantiation of it from malloc_hook.cc. We then reinterpret those |
| 79 // values as integers for testing. | 97 // values as integers for testing. |
| 80 typedef base::internal::HookList<MallocHook::NewHook> TestHookList; | 98 typedef base::internal::HookList<MallocHook::NewHook> TestHookList; |
| 81 | 99 |
| 82 int TestHookList_Traverse(const TestHookList& list, int* output_array, int n) { | 100 int TestHookList_Traverse(const TestHookList& list, int* output_array, int n) { |
| 83 MallocHook::NewHook values_as_hooks[kHookListMaxValues]; | 101 MallocHook::NewHook values_as_hooks[kHookListMaxValues]; |
| 84 int result = list.Traverse(values_as_hooks, std::min(n, kHookListMaxValues)); | 102 int result = list.Traverse(values_as_hooks, min(n, kHookListMaxValues)); |
| 85 for (int i = 0; i < result; ++i) { | 103 for (int i = 0; i < result; ++i) { |
| 86 output_array[i] = reinterpret_cast<const int&>(values_as_hooks[i]); | 104 output_array[i] = reinterpret_cast<const int&>(values_as_hooks[i]); |
| 87 } | 105 } |
| 88 return result; | 106 return result; |
| 89 } | 107 } |
| 90 | 108 |
| 91 bool TestHookList_Add(TestHookList* list, int val) { | 109 bool TestHookList_Add(TestHookList* list, int val) { |
| 92 return list->Add(reinterpret_cast<MallocHook::NewHook>(val)); | 110 return list->Add(reinterpret_cast<MallocHook::NewHook>(val)); |
| 93 } | 111 } |
| 94 | 112 |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 222 EXPECT_EQ(value_index, num_values); // Should not have found value. | 240 EXPECT_EQ(value_index, num_values); // Should not have found value. |
| 223 snprintf(buf, sizeof(buf), "%d]", num_values); | 241 snprintf(buf, sizeof(buf), "%d]", num_values); |
| 224 message += buf; | 242 message += buf; |
| 225 sched_yield(); | 243 sched_yield(); |
| 226 } | 244 } |
| 227 fprintf(stderr, "thread %d: %s\n", thread_num, message.c_str()); | 245 fprintf(stderr, "thread %d: %s\n", thread_num, message.c_str()); |
| 228 } | 246 } |
| 229 | 247 |
| 230 static volatile int num_threads_remaining; | 248 static volatile int num_threads_remaining; |
| 231 static TestHookList list = INIT_HOOK_LIST(69); | 249 static TestHookList list = INIT_HOOK_LIST(69); |
| 232 static SpinLock threadcount_lock; | 250 static Mutex threadcount_lock; |
| 233 | 251 |
| 234 void MultithreadedTestThreadRunner(int thread_num) { | 252 void MultithreadedTestThreadRunner(int thread_num) { |
| 235 // Wait for all threads to start running. | 253 // Wait for all threads to start running. |
| 236 { | 254 { |
| 237 SpinLockHolder h(&threadcount_lock); | 255 MutexLock ml(&threadcount_lock); |
| 238 assert(num_threads_remaining > 0); | 256 assert(num_threads_remaining > 0); |
| 239 --num_threads_remaining; | 257 --num_threads_remaining; |
| 240 | 258 |
| 241 // We should use condvars and the like, but for this test, we'll | 259 // We should use condvars and the like, but for this test, we'll |
| 242 // go simple and busy-wait. | 260 // go simple and busy-wait. |
| 243 while (num_threads_remaining > 0) { | 261 while (num_threads_remaining > 0) { |
| 244 threadcount_lock.Unlock(); | 262 threadcount_lock.Unlock(); |
| 245 SleepForMilliseconds(100); | 263 Sleep(1); |
| 246 threadcount_lock.Lock(); | 264 threadcount_lock.Lock(); |
| 247 } | 265 } |
| 248 } | 266 } |
| 249 | 267 |
| 250 // shift is the smallest number such that (1<<shift) > kHookListMaxValues | 268 // shift is the smallest number such that (1<<shift) > kHookListMaxValues |
| 251 int shift = 0; | 269 int shift = 0; |
| 252 for (int i = kHookListMaxValues; i > 0; i >>= 1) | 270 for (int i = kHookListMaxValues; i > 0; i >>= 1) |
| 253 shift += 1; | 271 shift += 1; |
| 254 | 272 |
| 255 MultithreadedTestThread(&list, shift, thread_num); | 273 MultithreadedTestThread(&list, shift, thread_num); |
| 256 } | 274 } |
| 257 | 275 |
| 258 | 276 |
| 259 TEST(HookListTest, MultithreadedTest) { | 277 TEST(HookListTest, MultithreadedTest) { |
| 260 ASSERT_TRUE(TestHookList_Remove(&list, 69)); | 278 ASSERT_TRUE(TestHookList_Remove(&list, 69)); |
| 261 ASSERT_EQ(0, list.priv_end); | 279 ASSERT_EQ(0, list.priv_end); |
| 262 | 280 |
| 263 // Run kHookListMaxValues thread, each running MultithreadedTestThread. | 281 // Run kHookListMaxValues thread, each running MultithreadedTestThread. |
| 264 // First, we need to set up the rest of the globals. | 282 // First, we need to set up the rest of the globals. |
| 265 num_threads_remaining = kHookListMaxValues; // a global var | 283 num_threads_remaining = kHookListMaxValues; // a global var |
| 266 RunManyThreadsWithId(&MultithreadedTestThreadRunner, num_threads_remaining, | 284 RunManyThreadsWithId(&MultithreadedTestThreadRunner, num_threads_remaining, |
| 267 1 << 15); | 285 1 << 15); |
| 268 | 286 |
| 269 int values[kHookListMaxValues + 1]; | 287 int values[kHookListMaxValues + 1]; |
| 270 EXPECT_EQ(0, TestHookList_Traverse(list, values, kHookListMaxValues)); | 288 EXPECT_EQ(0, TestHookList_Traverse(list, values, kHookListMaxValues)); |
| 271 EXPECT_EQ(0, list.priv_end); | 289 EXPECT_EQ(0, list.priv_end); |
| 272 } | 290 } |
| 273 | 291 |
| 274 #ifdef HAVE_MMAP | 292 // We only do mmap-hooking on (some) linux systems. |
| 293 #if defined(HAVE_MMAP) && defined(__linux) && \ |
| 294 (defined(__i386__) || defined(__x86_64__) || defined(__PPC__)) |
| 295 |
| 275 int mmap_calls = 0; | 296 int mmap_calls = 0; |
| 276 int mmap_matching_calls = 0; | 297 int mmap_matching_calls = 0; |
| 277 int munmap_calls = 0; | 298 int munmap_calls = 0; |
| 278 int munmap_matching_calls = 0; | 299 int munmap_matching_calls = 0; |
| 279 const int kMmapMagicFd = 1; | 300 const int kMmapMagicFd = 1; |
| 280 void* const kMmapMagicPointer = reinterpret_cast<void*>(1); | 301 void* const kMmapMagicPointer = reinterpret_cast<void*>(1); |
| 281 | 302 |
| 282 int MmapReplacement(const void* start, | 303 int MmapReplacement(const void* start, |
| 283 size_t size, | 304 size_t size, |
| 284 int protection, | 305 int protection, |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 329 EXPECT_EQ(2, munmap_calls); | 350 EXPECT_EQ(2, munmap_calls); |
| 330 EXPECT_EQ(1, munmap_matching_calls); | 351 EXPECT_EQ(1, munmap_matching_calls); |
| 331 | 352 |
| 332 // The DEATH test below is flaky, because we've just munmapped the memory, | 353 // The DEATH test below is flaky, because we've just munmapped the memory, |
| 333 // making it available for mmap()ing again. There is no guarantee that it | 354 // making it available for mmap()ing again. There is no guarantee that it |
| 334 // will stay unmapped, and in fact it gets reused ~10% of the time. | 355 // will stay unmapped, and in fact it gets reused ~10% of the time. |
| 335 // It the area is reused, then not only we don't die, but we also corrupt | 356 // It the area is reused, then not only we don't die, but we also corrupt |
| 336 // whoever owns that memory now. | 357 // whoever owns that memory now. |
| 337 // EXPECT_DEATH(*ptr = 'a', "SIGSEGV"); | 358 // EXPECT_DEATH(*ptr = 'a', "SIGSEGV"); |
| 338 } | 359 } |
| 339 #endif // #ifdef HAVE_MMAN | 360 #endif // #ifdef HAVE_MMAP && linux && ... |
| 340 | 361 |
| 341 } // namespace | 362 } // namespace |
| 342 | 363 |
| 343 int main(int argc, char** argv) { | 364 int main(int argc, char** argv) { |
| 344 return RUN_ALL_TESTS(); | 365 return RUN_ALL_TESTS(); |
| 345 } | 366 } |
| OLD | NEW |