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