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 |