| Index: base/allocator/allocator_shim_unittest.cc
|
| diff --git a/base/allocator/allocator_shim_unittest.cc b/base/allocator/allocator_shim_unittest.cc
|
| index e45b03dc5392ae44087f41e274b62ad207eca4b0..cff7dd7e86f792b132cea690e7e9c9c976ec2c50 100644
|
| --- a/base/allocator/allocator_shim_unittest.cc
|
| +++ b/base/allocator/allocator_shim_unittest.cc
|
| @@ -4,7 +4,6 @@
|
|
|
| #include "base/allocator/allocator_shim.h"
|
|
|
| -#include <malloc.h>
|
| #include <stdlib.h>
|
| #include <string.h>
|
|
|
| @@ -18,12 +17,20 @@
|
| #include "base/synchronization/waitable_event.h"
|
| #include "base/threading/platform_thread.h"
|
| #include "base/threading/thread_local.h"
|
| +#include "build/build_config.h"
|
| #include "testing/gmock/include/gmock/gmock.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| #if defined(OS_WIN)
|
| #include <windows.h>
|
| +#elif defined(OS_MACOSX)
|
| +#include <malloc/malloc.h>
|
| +#include "third_party/apple_apsl/malloc.h"
|
| #else
|
| +#include <malloc.h>
|
| +#endif
|
| +
|
| +#if !defined(OS_WIN)
|
| #include <unistd.h>
|
| #endif
|
|
|
| @@ -83,15 +90,15 @@ class AllocatorShimTest : public testing::Test {
|
| void* address,
|
| size_t size) {
|
| if (instance_) {
|
| - // Address 0x420 is a special sentinel for the NewHandlerConcurrency test.
|
| - // The first time (but only the first one) it is hit it fails, causing the
|
| + // Size 0xFEED a special sentinel for the NewHandlerConcurrency test.
|
| + // Hitting it for the first time will cause a failure, causing the
|
| // invocation of the std::new_handler.
|
| - if (address == reinterpret_cast<void*>(0x420)) {
|
| - if (!instance_->did_fail_realloc_0x420_once->Get()) {
|
| - instance_->did_fail_realloc_0x420_once->Set(true);
|
| + if (size == 0xFEED) {
|
| + if (!instance_->did_fail_realloc_0xfeed_once->Get()) {
|
| + instance_->did_fail_realloc_0xfeed_once->Set(true);
|
| return nullptr;
|
| } else {
|
| - return reinterpret_cast<void*>(0x420ul);
|
| + return address;
|
| }
|
| }
|
|
|
| @@ -109,6 +116,44 @@ class AllocatorShimTest : public testing::Test {
|
| self->next->free_function(self->next, address);
|
| }
|
|
|
| + static size_t MockGetSizeEstimate(const AllocatorDispatch* self,
|
| + void* address) {
|
| + return self->next->get_size_estimate_function(self->next, address);
|
| + }
|
| +
|
| + static unsigned MockBatchMalloc(const AllocatorDispatch* self,
|
| + size_t size,
|
| + void** results,
|
| + unsigned num_requested) {
|
| + if (instance_) {
|
| + instance_->batch_mallocs_intercepted_by_size[size] =
|
| + instance_->batch_mallocs_intercepted_by_size[size] + num_requested;
|
| + }
|
| + return self->next->batch_malloc_function(self->next, size, results,
|
| + num_requested);
|
| + }
|
| +
|
| + static void MockBatchFree(const AllocatorDispatch* self,
|
| + void** to_be_freed,
|
| + unsigned num_to_be_freed) {
|
| + if (instance_) {
|
| + for (unsigned i = 0; i < num_to_be_freed; ++i) {
|
| + ++instance_->batch_frees_intercepted_by_addr[Hash(to_be_freed[i])];
|
| + }
|
| + }
|
| + self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed);
|
| + }
|
| +
|
| + static void MockFreeDefiniteSize(const AllocatorDispatch* self,
|
| + void* ptr,
|
| + size_t size) {
|
| + if (instance_) {
|
| + ++instance_->frees_intercepted_by_addr[Hash(ptr)];
|
| + ++instance_->free_definite_sizes_intercepted_by_size[size];
|
| + }
|
| + self->next->free_definite_size_function(self->next, ptr, size);
|
| + }
|
| +
|
| static void NewHandler() {
|
| if (!instance_)
|
| return;
|
| @@ -127,10 +172,20 @@ class AllocatorShimTest : public testing::Test {
|
| memset(&aligned_allocs_intercepted_by_alignment, 0, array_size);
|
| memset(&reallocs_intercepted_by_size, 0, array_size);
|
| memset(&frees_intercepted_by_addr, 0, array_size);
|
| - did_fail_realloc_0x420_once.reset(new ThreadLocalBoolean());
|
| + memset(&batch_mallocs_intercepted_by_size, 0, array_size);
|
| + memset(&batch_frees_intercepted_by_addr, 0, array_size);
|
| + memset(&free_definite_sizes_intercepted_by_size, 0, array_size);
|
| + did_fail_realloc_0xfeed_once.reset(new ThreadLocalBoolean());
|
| subtle::Release_Store(&num_new_handler_calls, 0);
|
| instance_ = this;
|
| +
|
| + }
|
| +
|
| +#if defined(OS_MACOSX)
|
| + static void SetUpTestCase() {
|
| + InitializeAllocatorShim();
|
| }
|
| +#endif
|
|
|
| void TearDown() override { instance_ = nullptr; }
|
|
|
| @@ -142,7 +197,10 @@ class AllocatorShimTest : public testing::Test {
|
| size_t reallocs_intercepted_by_size[kMaxSizeTracked];
|
| size_t reallocs_intercepted_by_addr[kMaxSizeTracked];
|
| size_t frees_intercepted_by_addr[kMaxSizeTracked];
|
| - std::unique_ptr<ThreadLocalBoolean> did_fail_realloc_0x420_once;
|
| + size_t batch_mallocs_intercepted_by_size[kMaxSizeTracked];
|
| + size_t batch_frees_intercepted_by_addr[kMaxSizeTracked];
|
| + size_t free_definite_sizes_intercepted_by_size[kMaxSizeTracked];
|
| + std::unique_ptr<ThreadLocalBoolean> did_fail_realloc_0xfeed_once;
|
| subtle::Atomic32 num_new_handler_calls;
|
|
|
| private:
|
| @@ -165,8 +223,9 @@ class ThreadDelegateForNewHandlerTest : public PlatformThread::Delegate {
|
|
|
| void ThreadMain() override {
|
| event_->Wait();
|
| - void* res = realloc(reinterpret_cast<void*>(0x420ul), 1);
|
| - EXPECT_EQ(reinterpret_cast<void*>(0x420ul), res);
|
| + void* temp = malloc(1);
|
| + void* res = realloc(temp, 0xFEED);
|
| + EXPECT_EQ(temp, res);
|
| }
|
|
|
| private:
|
| @@ -181,7 +240,11 @@ AllocatorDispatch g_mock_dispatch = {
|
| &AllocatorShimTest::MockAllocAligned, /* alloc_aligned_function */
|
| &AllocatorShimTest::MockRealloc, /* realloc_function */
|
| &AllocatorShimTest::MockFree, /* free_function */
|
| - nullptr, /* next */
|
| + &AllocatorShimTest::MockGetSizeEstimate, /* get_size_estimate_function */
|
| + &AllocatorShimTest::MockBatchMalloc, /* batch_malloc_function */
|
| + &AllocatorShimTest::MockBatchFree, /* batch_free_function */
|
| + &AllocatorShimTest::MockFreeDefiniteSize, /* free_definite_size_function */
|
| + nullptr, /* next */
|
| };
|
|
|
| TEST_F(AllocatorShimTest, InterceptLibcSymbols) {
|
| @@ -196,12 +259,7 @@ TEST_F(AllocatorShimTest, InterceptLibcSymbols) {
|
| ASSERT_GE(zero_allocs_intercepted_by_size[2 * 23], 1u);
|
|
|
| #if !defined(OS_WIN)
|
| - void* memalign_ptr = memalign(128, 53);
|
| - ASSERT_NE(nullptr, memalign_ptr);
|
| - ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(memalign_ptr) % 128);
|
| - ASSERT_GE(aligned_allocs_intercepted_by_alignment[128], 1u);
|
| - ASSERT_GE(aligned_allocs_intercepted_by_size[53], 1u);
|
| -
|
| + const size_t kPageSize = base::GetPageSize();
|
| void* posix_memalign_ptr = nullptr;
|
| int res = posix_memalign(&posix_memalign_ptr, 256, 59);
|
| ASSERT_EQ(0, res);
|
| @@ -212,10 +270,17 @@ TEST_F(AllocatorShimTest, InterceptLibcSymbols) {
|
|
|
| void* valloc_ptr = valloc(61);
|
| ASSERT_NE(nullptr, valloc_ptr);
|
| - const size_t kPageSize = base::GetPageSize();
|
| ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(valloc_ptr) % kPageSize);
|
| ASSERT_GE(aligned_allocs_intercepted_by_alignment[kPageSize], 1u);
|
| ASSERT_GE(aligned_allocs_intercepted_by_size[61], 1u);
|
| +#endif // !OS_WIN
|
| +
|
| +#if !defined(OS_WIN) && !defined(OS_MACOSX)
|
| + void* memalign_ptr = memalign(128, 53);
|
| + ASSERT_NE(nullptr, memalign_ptr);
|
| + ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(memalign_ptr) % 128);
|
| + ASSERT_GE(aligned_allocs_intercepted_by_alignment[128], 1u);
|
| + ASSERT_GE(aligned_allocs_intercepted_by_size[53], 1u);
|
|
|
| void* pvalloc_ptr = pvalloc(67);
|
| ASSERT_NE(nullptr, pvalloc_ptr);
|
| @@ -223,12 +288,9 @@ TEST_F(AllocatorShimTest, InterceptLibcSymbols) {
|
| ASSERT_GE(aligned_allocs_intercepted_by_alignment[kPageSize], 1u);
|
| // pvalloc rounds the size up to the next page.
|
| ASSERT_GE(aligned_allocs_intercepted_by_size[kPageSize], 1u);
|
| -#endif // OS_WIN
|
| +#endif // !OS_WIN && !OS_MACOSX
|
|
|
| - char* realloc_ptr = static_cast<char*>(realloc(nullptr, 71));
|
| - ASSERT_NE(nullptr, realloc_ptr);
|
| - ASSERT_GE(reallocs_intercepted_by_size[71], 1u);
|
| - ASSERT_GE(reallocs_intercepted_by_addr[Hash(nullptr)], 1u);
|
| + char* realloc_ptr = static_cast<char*>(malloc(10));
|
| strcpy(realloc_ptr, "foobar");
|
| void* old_realloc_ptr = realloc_ptr;
|
| realloc_ptr = static_cast<char*>(realloc(realloc_ptr, 73));
|
| @@ -242,19 +304,21 @@ TEST_F(AllocatorShimTest, InterceptLibcSymbols) {
|
| free(zero_alloc_ptr);
|
| ASSERT_GE(frees_intercepted_by_addr[Hash(zero_alloc_ptr)], 1u);
|
|
|
| -#if !defined(OS_WIN)
|
| +#if !defined(OS_WIN) && !defined(OS_MACOSX)
|
| free(memalign_ptr);
|
| ASSERT_GE(frees_intercepted_by_addr[Hash(memalign_ptr)], 1u);
|
|
|
| + free(pvalloc_ptr);
|
| + ASSERT_GE(frees_intercepted_by_addr[Hash(pvalloc_ptr)], 1u);
|
| +#endif // !OS_WIN && !OS_MACOSX
|
| +
|
| +#if !defined(OS_WIN)
|
| free(posix_memalign_ptr);
|
| ASSERT_GE(frees_intercepted_by_addr[Hash(posix_memalign_ptr)], 1u);
|
|
|
| free(valloc_ptr);
|
| ASSERT_GE(frees_intercepted_by_addr[Hash(valloc_ptr)], 1u);
|
| -
|
| - free(pvalloc_ptr);
|
| - ASSERT_GE(frees_intercepted_by_addr[Hash(pvalloc_ptr)], 1u);
|
| -#endif // OS_WIN
|
| +#endif // !OS_WIN
|
|
|
| free(realloc_ptr);
|
| ASSERT_GE(frees_intercepted_by_addr[Hash(realloc_ptr)], 1u);
|
| @@ -267,6 +331,41 @@ TEST_F(AllocatorShimTest, InterceptLibcSymbols) {
|
| free(non_hooked_ptr);
|
| }
|
|
|
| +#if defined(OS_MACOSX)
|
| +TEST_F(AllocatorShimTest, InterceptLibcSymbolsBatchMallocFree) {
|
| + InsertAllocatorDispatch(&g_mock_dispatch);
|
| +
|
| + unsigned count = 13;
|
| + std::vector<void*> results;
|
| + results.resize(count);
|
| + unsigned result_count = malloc_zone_batch_malloc(malloc_default_zone(), 99,
|
| + results.data(), count);
|
| + ASSERT_EQ(count, result_count);
|
| + ASSERT_EQ(count, batch_mallocs_intercepted_by_size[99]);
|
| +
|
| + std::vector<void*> results_copy(results);
|
| + malloc_zone_batch_free(malloc_default_zone(), results.data(), count);
|
| + for (void* result : results_copy) {
|
| + ASSERT_GE(batch_frees_intercepted_by_addr[Hash(result)], 1u);
|
| + }
|
| + RemoveAllocatorDispatchForTesting(&g_mock_dispatch);
|
| +}
|
| +
|
| +TEST_F(AllocatorShimTest, InterceptLibcSymbolsFreeDefiniteSize) {
|
| + InsertAllocatorDispatch(&g_mock_dispatch);
|
| +
|
| + void* alloc_ptr = malloc(19);
|
| + ASSERT_NE(nullptr, alloc_ptr);
|
| + ASSERT_GE(allocs_intercepted_by_size[19], 1u);
|
| +
|
| + ChromeMallocZone* default_zone =
|
| + reinterpret_cast<ChromeMallocZone*>(malloc_default_zone());
|
| + default_zone->free_definite_size(malloc_default_zone(), alloc_ptr, 19);
|
| + ASSERT_GE(free_definite_sizes_intercepted_by_size[19], 1u);
|
| + RemoveAllocatorDispatchForTesting(&g_mock_dispatch);
|
| +}
|
| +#endif // defined(OS_MACOSX)
|
| +
|
| TEST_F(AllocatorShimTest, InterceptCppSymbols) {
|
| InsertAllocatorDispatch(&g_mock_dispatch);
|
|
|
| @@ -304,8 +403,8 @@ TEST_F(AllocatorShimTest, InterceptCppSymbols) {
|
| // This test exercises the case of concurrent OOM failure, which would end up
|
| // invoking std::new_handler concurrently. This is to cover the CallNewHandler()
|
| // paths of allocator_shim.cc and smoke-test its thread safey.
|
| -// The test creates kNumThreads threads. Each of them does just a
|
| -// realloc(0x420).
|
| +// The test creates kNumThreads threads. Each of them mallocs some memory, and
|
| +// then does a realloc(<new memory>, 0xFEED).
|
| // The shim intercepts such realloc and makes it fail only once on each thread.
|
| // We expect to see excactly kNumThreads invocations of the new_handler.
|
| TEST_F(AllocatorShimTest, NewHandlerConcurrency) {
|
|
|