malloc_hook_test.cc 12 KB

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