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@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 58namespace { 59 60using std::string; 61using std::vector; 62 63vector<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 74static 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 84void 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 92using std::min; 93using 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. 98typedef base::internal::HookList<MallocHook::NewHook> TestHookList; 99 100int 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 109bool TestHookList_Add(TestHookList* list, int val) { 110 return list->Add(reinterpret_cast<MallocHook::NewHook>(val)); 111} 112 113bool 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 121TEST(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 129TEST(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 138TEST(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 149TEST(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 165TEST(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 181TEST(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 191TEST(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 208void 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 248static volatile int num_threads_remaining; 249static TestHookList list = INIT_HOOK_LIST(69); 250static Mutex threadcount_lock; 251 252void 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 277TEST(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 296int mmap_calls = 0; 297int mmap_matching_calls = 0; 298int munmap_calls = 0; 299int munmap_matching_calls = 0; 300const int kMmapMagicFd = 1; 301void* const kMmapMagicPointer = reinterpret_cast<void*>(1); 302 303int 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 319int 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 329TEST(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 364int main(int argc, char** argv) { 365 return RUN_ALL_TESTS(); 366} 367