1 // Copyright (c) 2011, Google Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above 11 // copyright notice, this list of conditions and the following disclaimer 12 // in the documentation and/or other materials provided with the 13 // distribution. 14 // * Neither the name of Google Inc. nor the names of its 15 // contributors may be used to endorse or promote products derived from 16 // this software without specific prior written permission. 17 // 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 // ---- 31 // Author: llib (at) google.com (Bill Clarke) 32 33 #include "config_for_unittests.h" 34 #include <assert.h> 35 #include <stdio.h> 36 #ifdef HAVE_MMAP 37 #include <sys/mman.h> 38 #endif 39 #ifdef HAVE_UNISTD_H 40 #include <unistd.h> // for sleep() 41 #endif 42 #include <algorithm> 43 #include <string> 44 #include <vector> 45 #include <gperftools/malloc_hook.h> 46 #include "malloc_hook-inl.h" 47 #include "base/logging.h" 48 #include "base/simple_mutex.h" 49 #include "base/sysinfo.h" 50 #include "tests/testutil.h" 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 58 namespace { 59 60 using std::string; 61 using std::vector; 62 63 vector<void (*)()> g_testlist; // the tests to run 64 65 #define TEST(a, b) \ 66 struct Test_##a##_##b { \ 67 Test_##a##_##b() { g_testlist.push_back(&Run); } \ 68 static void Run(); \ 69 }; \ 70 static Test_##a##_##b g_test_##a##_##b; \ 71 void Test_##a##_##b::Run() 72 73 74 static int RUN_ALL_TESTS() { 75 vector<void (*)()>::const_iterator it; 76 for (it = g_testlist.begin(); it != g_testlist.end(); ++it) { 77 (*it)(); // The test will error-exit if there's a problem. 78 } 79 fprintf(stderr, "\nPassed %d tests\n\nPASS\n", 80 static_cast<int>(g_testlist.size())); 81 return 0; 82 } 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; 93 using base::internal::kHookListMaxValues; 94 95 // 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 97 // values as integers for testing. 98 typedef base::internal::HookList<MallocHook::NewHook> TestHookList; 99 100 int TestHookList_Traverse(const TestHookList& list, int* output_array, int n) { 101 MallocHook::NewHook values_as_hooks[kHookListMaxValues]; 102 int result = list.Traverse(values_as_hooks, min(n, kHookListMaxValues)); 103 for (int i = 0; i < result; ++i) { 104 output_array[i] = reinterpret_cast<const int&>(values_as_hooks[i]); 105 } 106 return result; 107 } 108 109 bool TestHookList_Add(TestHookList* list, int val) { 110 return list->Add(reinterpret_cast<MallocHook::NewHook>(val)); 111 } 112 113 bool TestHookList_Remove(TestHookList* list, int val) { 114 return list->Remove(reinterpret_cast<MallocHook::NewHook>(val)); 115 } 116 117 // Note that this is almost the same as INIT_HOOK_LIST in malloc_hook.cc without 118 // the cast. 119 #define INIT_HOOK_LIST(initial_value) { 1, { initial_value } } 120 121 TEST(HookListTest, InitialValueExists) { 122 TestHookList list = INIT_HOOK_LIST(69); 123 int values[2] = { 0, 0 }; 124 EXPECT_EQ(1, TestHookList_Traverse(list, values, 2)); 125 EXPECT_EQ(69, values[0]); 126 EXPECT_EQ(1, list.priv_end); 127 } 128 129 TEST(HookListTest, CanRemoveInitialValue) { 130 TestHookList list = INIT_HOOK_LIST(69); 131 ASSERT_TRUE(TestHookList_Remove(&list, 69)); 132 EXPECT_EQ(0, list.priv_end); 133 134 int values[2] = { 0, 0 }; 135 EXPECT_EQ(0, TestHookList_Traverse(list, values, 2)); 136 } 137 138 TEST(HookListTest, AddAppends) { 139 TestHookList list = INIT_HOOK_LIST(69); 140 ASSERT_TRUE(TestHookList_Add(&list, 42)); 141 EXPECT_EQ(2, list.priv_end); 142 143 int values[2] = { 0, 0 }; 144 EXPECT_EQ(2, TestHookList_Traverse(list, values, 2)); 145 EXPECT_EQ(69, values[0]); 146 EXPECT_EQ(42, values[1]); 147 } 148 149 TEST(HookListTest, RemoveWorksAndWillClearSize) { 150 TestHookList list = INIT_HOOK_LIST(69); 151 ASSERT_TRUE(TestHookList_Add(&list, 42)); 152 153 ASSERT_TRUE(TestHookList_Remove(&list, 69)); 154 EXPECT_EQ(2, list.priv_end); 155 156 int values[2] = { 0, 0 }; 157 EXPECT_EQ(1, TestHookList_Traverse(list, values, 2)); 158 EXPECT_EQ(42, values[0]); 159 160 ASSERT_TRUE(TestHookList_Remove(&list, 42)); 161 EXPECT_EQ(0, list.priv_end); 162 EXPECT_EQ(0, TestHookList_Traverse(list, values, 2)); 163 } 164 165 TEST(HookListTest, AddPrependsAfterRemove) { 166 TestHookList list = INIT_HOOK_LIST(69); 167 ASSERT_TRUE(TestHookList_Add(&list, 42)); 168 169 ASSERT_TRUE(TestHookList_Remove(&list, 69)); 170 EXPECT_EQ(2, list.priv_end); 171 172 ASSERT_TRUE(TestHookList_Add(&list, 7)); 173 EXPECT_EQ(2, list.priv_end); 174 175 int values[2] = { 0, 0 }; 176 EXPECT_EQ(2, TestHookList_Traverse(list, values, 2)); 177 EXPECT_EQ(7, values[0]); 178 EXPECT_EQ(42, values[1]); 179 } 180 181 TEST(HookListTest, InvalidAddRejected) { 182 TestHookList list = INIT_HOOK_LIST(69); 183 EXPECT_FALSE(TestHookList_Add(&list, 0)); 184 185 int values[2] = { 0, 0 }; 186 EXPECT_EQ(1, TestHookList_Traverse(list, values, 2)); 187 EXPECT_EQ(69, values[0]); 188 EXPECT_EQ(1, list.priv_end); 189 } 190 191 TEST(HookListTest, FillUpTheList) { 192 TestHookList list = INIT_HOOK_LIST(69); 193 int num_inserts = 0; 194 while (TestHookList_Add(&list, ++num_inserts)) 195 ; 196 EXPECT_EQ(kHookListMaxValues, num_inserts); 197 EXPECT_EQ(kHookListMaxValues, list.priv_end); 198 199 int values[kHookListMaxValues + 1]; 200 EXPECT_EQ(kHookListMaxValues, TestHookList_Traverse(list, values, 201 kHookListMaxValues)); 202 EXPECT_EQ(69, values[0]); 203 for (int i = 1; i < kHookListMaxValues; ++i) { 204 EXPECT_EQ(i, values[i]); 205 } 206 } 207 208 void MultithreadedTestThread(TestHookList* list, int shift, 209 int thread_num) { 210 string message; 211 char buf[64]; 212 for (int i = 1; i < 1000; ++i) { 213 // In each loop, we insert a unique value, check it exists, remove it, and 214 // check it doesn't exist. We also record some stats to log at the end of 215 // each thread. Each insertion location and the length of the list is 216 // non-deterministic (except for the very first one, over all threads, and 217 // after the very last one the list should be empty). 218 int value = (i << shift) + thread_num; 219 EXPECT_TRUE(TestHookList_Add(list, value)); 220 sched_yield(); // Ensure some more interleaving. 221 int values[kHookListMaxValues + 1]; 222 int num_values = TestHookList_Traverse(*list, values, kHookListMaxValues); 223 EXPECT_LT(0, num_values); 224 int value_index; 225 for (value_index = 0; 226 value_index < num_values && values[value_index] != value; 227 ++value_index) 228 ; 229 EXPECT_LT(value_index, num_values); // Should have found value. 230 snprintf(buf, sizeof(buf), "[%d/%d; ", value_index, num_values); 231 message += buf; 232 sched_yield(); 233 EXPECT_TRUE(TestHookList_Remove(list, value)); 234 sched_yield(); 235 num_values = TestHookList_Traverse(*list, values, kHookListMaxValues); 236 for (value_index = 0; 237 value_index < num_values && values[value_index] != value; 238 ++value_index) 239 ; 240 EXPECT_EQ(value_index, num_values); // Should not have found value. 241 snprintf(buf, sizeof(buf), "%d]", num_values); 242 message += buf; 243 sched_yield(); 244 } 245 fprintf(stderr, "thread %d: %s\n", thread_num, message.c_str()); 246 } 247 248 static volatile int num_threads_remaining; 249 static TestHookList list = INIT_HOOK_LIST(69); 250 static Mutex threadcount_lock; 251 252 void MultithreadedTestThreadRunner(int thread_num) { 253 // Wait for all threads to start running. 254 { 255 MutexLock ml(&threadcount_lock); 256 assert(num_threads_remaining > 0); 257 --num_threads_remaining; 258 259 // We should use condvars and the like, but for this test, we'll 260 // go simple and busy-wait. 261 while (num_threads_remaining > 0) { 262 threadcount_lock.Unlock(); 263 Sleep(1); 264 threadcount_lock.Lock(); 265 } 266 } 267 268 // shift is the smallest number such that (1<<shift) > kHookListMaxValues 269 int shift = 0; 270 for (int i = kHookListMaxValues; i > 0; i >>= 1) 271 shift += 1; 272 273 MultithreadedTestThread(&list, shift, thread_num); 274 } 275 276 277 TEST(HookListTest, MultithreadedTest) { 278 ASSERT_TRUE(TestHookList_Remove(&list, 69)); 279 ASSERT_EQ(0, list.priv_end); 280 281 // Run kHookListMaxValues thread, each running MultithreadedTestThread. 282 // First, we need to set up the rest of the globals. 283 num_threads_remaining = kHookListMaxValues; // a global var 284 RunManyThreadsWithId(&MultithreadedTestThreadRunner, num_threads_remaining, 285 1 << 15); 286 287 int values[kHookListMaxValues + 1]; 288 EXPECT_EQ(0, TestHookList_Traverse(list, values, kHookListMaxValues)); 289 EXPECT_EQ(0, list.priv_end); 290 } 291 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 296 int mmap_calls = 0; 297 int mmap_matching_calls = 0; 298 int munmap_calls = 0; 299 int munmap_matching_calls = 0; 300 const int kMmapMagicFd = 1; 301 void* const kMmapMagicPointer = reinterpret_cast<void*>(1); 302 303 int MmapReplacement(const void* start, 304 size_t size, 305 int protection, 306 int flags, 307 int fd, 308 off_t offset, 309 void** result) { 310 ++mmap_calls; 311 if (fd == kMmapMagicFd) { 312 ++mmap_matching_calls; 313 *result = kMmapMagicPointer; 314 return true; 315 } 316 return false; 317 } 318 319 int MunmapReplacement(const void* ptr, size_t size, int* result) { 320 ++munmap_calls; 321 if (ptr == kMmapMagicPointer) { 322 ++munmap_matching_calls; 323 *result = 0; 324 return true; 325 } 326 return false; 327 } 328 329 TEST(MallocMookTest, MmapReplacements) { 330 mmap_calls = mmap_matching_calls = munmap_calls = munmap_matching_calls = 0; 331 MallocHook::SetMmapReplacement(&MmapReplacement); 332 MallocHook::SetMunmapReplacement(&MunmapReplacement); 333 EXPECT_EQ(kMmapMagicPointer, mmap(NULL, 1, PROT_READ, MAP_PRIVATE, 334 kMmapMagicFd, 0)); 335 EXPECT_EQ(1, mmap_matching_calls); 336 337 char* ptr = reinterpret_cast<char*>( 338 mmap(NULL, 1, PROT_READ | PROT_WRITE, 339 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); 340 EXPECT_EQ(2, mmap_calls); 341 EXPECT_EQ(1, mmap_matching_calls); 342 ASSERT_NE(MAP_FAILED, ptr); 343 *ptr = 'a'; 344 345 EXPECT_EQ(0, munmap(kMmapMagicPointer, 1)); 346 EXPECT_EQ(1, munmap_calls); 347 EXPECT_EQ(1, munmap_matching_calls); 348 349 EXPECT_EQ(0, munmap(ptr, 1)); 350 EXPECT_EQ(2, munmap_calls); 351 EXPECT_EQ(1, munmap_matching_calls); 352 353 // 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 355 // 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 357 // whoever owns that memory now. 358 // EXPECT_DEATH(*ptr = 'a', "SIGSEGV"); 359 } 360 #endif // #ifdef HAVE_MMAP && linux && ... 361 362 } // namespace 363 364 int main(int argc, char** argv) { 365 return RUN_ALL_TESTS(); 366 } 367