| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/allocator/allocator_shim.h" | 5 #include "base/allocator/allocator_shim.h" |
| 6 | 6 |
| 7 #include <malloc.h> | 7 #include <malloc.h> |
| 8 #include <stdlib.h> | 8 #include <stdlib.h> |
| 9 #include <string.h> | 9 #include <string.h> |
| 10 #include <unistd.h> | 10 #include <unistd.h> |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 if (alignment < kMaxSizeTracked) | 69 if (alignment < kMaxSizeTracked) |
| 70 ++(instance_->aligned_allocs_intercepted_by_alignment[alignment]); | 70 ++(instance_->aligned_allocs_intercepted_by_alignment[alignment]); |
| 71 } | 71 } |
| 72 return self->next->alloc_aligned_function(self->next, alignment, size); | 72 return self->next->alloc_aligned_function(self->next, alignment, size); |
| 73 } | 73 } |
| 74 | 74 |
| 75 static void* MockRealloc(const AllocatorDispatch* self, | 75 static void* MockRealloc(const AllocatorDispatch* self, |
| 76 void* address, | 76 void* address, |
| 77 size_t size) { | 77 size_t size) { |
| 78 if (instance_) { | 78 if (instance_) { |
| 79 // Address 0x42 is a special sentinel for the NewHandlerConcurrency test. | 79 // Address 0x420 is a special sentinel for the NewHandlerConcurrency test. |
| 80 // The first time (but only the first one) it is hit it fails, causing the | 80 // The first time (but only the first one) it is hit it fails, causing the |
| 81 // invocation of the std::new_handler. | 81 // invocation of the std::new_handler. |
| 82 if (address == reinterpret_cast<void*>(0x42)) { | 82 if (address == reinterpret_cast<void*>(0x420)) { |
| 83 if (!instance_->did_fail_realloc_0x42_once->Get()) { | 83 if (!instance_->did_fail_realloc_0x420_once->Get()) { |
| 84 instance_->did_fail_realloc_0x42_once->Set(true); | 84 instance_->did_fail_realloc_0x420_once->Set(true); |
| 85 return nullptr; | 85 return nullptr; |
| 86 } else { | 86 } else { |
| 87 return reinterpret_cast<void*>(0x42ul); | 87 return reinterpret_cast<void*>(0x420ul); |
| 88 } | 88 } |
| 89 } | 89 } |
| 90 | 90 |
| 91 if (size < kMaxSizeTracked) | 91 if (size < kMaxSizeTracked) |
| 92 ++(instance_->reallocs_intercepted_by_size[size]); | 92 ++(instance_->reallocs_intercepted_by_size[size]); |
| 93 ++instance_->reallocs_intercepted_by_addr[Hash(address)]; | 93 ++instance_->reallocs_intercepted_by_addr[Hash(address)]; |
| 94 } | 94 } |
| 95 return self->next->realloc_function(self->next, address, size); | 95 return self->next->realloc_function(self->next, address, size); |
| 96 } | 96 } |
| 97 | 97 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 113 } | 113 } |
| 114 | 114 |
| 115 void SetUp() override { | 115 void SetUp() override { |
| 116 const size_t array_size = kMaxSizeTracked * sizeof(size_t); | 116 const size_t array_size = kMaxSizeTracked * sizeof(size_t); |
| 117 memset(&allocs_intercepted_by_size, 0, array_size); | 117 memset(&allocs_intercepted_by_size, 0, array_size); |
| 118 memset(&zero_allocs_intercepted_by_size, 0, array_size); | 118 memset(&zero_allocs_intercepted_by_size, 0, array_size); |
| 119 memset(&aligned_allocs_intercepted_by_size, 0, array_size); | 119 memset(&aligned_allocs_intercepted_by_size, 0, array_size); |
| 120 memset(&aligned_allocs_intercepted_by_alignment, 0, array_size); | 120 memset(&aligned_allocs_intercepted_by_alignment, 0, array_size); |
| 121 memset(&reallocs_intercepted_by_size, 0, array_size); | 121 memset(&reallocs_intercepted_by_size, 0, array_size); |
| 122 memset(&frees_intercepted_by_addr, 0, array_size); | 122 memset(&frees_intercepted_by_addr, 0, array_size); |
| 123 did_fail_realloc_0x42_once.reset(new ThreadLocalBoolean()); | 123 did_fail_realloc_0x420_once.reset(new ThreadLocalBoolean()); |
| 124 subtle::Release_Store(&num_new_handler_calls, 0); | 124 subtle::Release_Store(&num_new_handler_calls, 0); |
| 125 instance_ = this; | 125 instance_ = this; |
| 126 } | 126 } |
| 127 | 127 |
| 128 void TearDown() override { instance_ = nullptr; } | 128 void TearDown() override { instance_ = nullptr; } |
| 129 | 129 |
| 130 protected: | 130 protected: |
| 131 size_t allocs_intercepted_by_size[kMaxSizeTracked]; | 131 size_t allocs_intercepted_by_size[kMaxSizeTracked]; |
| 132 size_t zero_allocs_intercepted_by_size[kMaxSizeTracked]; | 132 size_t zero_allocs_intercepted_by_size[kMaxSizeTracked]; |
| 133 size_t aligned_allocs_intercepted_by_size[kMaxSizeTracked]; | 133 size_t aligned_allocs_intercepted_by_size[kMaxSizeTracked]; |
| 134 size_t aligned_allocs_intercepted_by_alignment[kMaxSizeTracked]; | 134 size_t aligned_allocs_intercepted_by_alignment[kMaxSizeTracked]; |
| 135 size_t reallocs_intercepted_by_size[kMaxSizeTracked]; | 135 size_t reallocs_intercepted_by_size[kMaxSizeTracked]; |
| 136 size_t reallocs_intercepted_by_addr[kMaxSizeTracked]; | 136 size_t reallocs_intercepted_by_addr[kMaxSizeTracked]; |
| 137 size_t frees_intercepted_by_addr[kMaxSizeTracked]; | 137 size_t frees_intercepted_by_addr[kMaxSizeTracked]; |
| 138 std::unique_ptr<ThreadLocalBoolean> did_fail_realloc_0x42_once; | 138 std::unique_ptr<ThreadLocalBoolean> did_fail_realloc_0x420_once; |
| 139 subtle::Atomic32 num_new_handler_calls; | 139 subtle::Atomic32 num_new_handler_calls; |
| 140 | 140 |
| 141 private: | 141 private: |
| 142 static AllocatorShimTest* instance_; | 142 static AllocatorShimTest* instance_; |
| 143 }; | 143 }; |
| 144 | 144 |
| 145 struct TestStruct1 { | 145 struct TestStruct1 { |
| 146 uint32_t ignored; | 146 uint32_t ignored; |
| 147 uint8_t ignored_2; | 147 uint8_t ignored_2; |
| 148 }; | 148 }; |
| 149 | 149 |
| 150 struct TestStruct2 { | 150 struct TestStruct2 { |
| 151 uint64_t ignored; | 151 uint64_t ignored; |
| 152 uint8_t ignored_3; | 152 uint8_t ignored_3; |
| 153 }; | 153 }; |
| 154 | 154 |
| 155 class ThreadDelegateForNewHandlerTest : public PlatformThread::Delegate { | 155 class ThreadDelegateForNewHandlerTest : public PlatformThread::Delegate { |
| 156 public: | 156 public: |
| 157 ThreadDelegateForNewHandlerTest(WaitableEvent* event) : event_(event) {} | 157 ThreadDelegateForNewHandlerTest(WaitableEvent* event) : event_(event) {} |
| 158 | 158 |
| 159 void ThreadMain() override { | 159 void ThreadMain() override { |
| 160 event_->Wait(); | 160 event_->Wait(); |
| 161 void* res = realloc(reinterpret_cast<void*>(0x42ul), 1); | 161 void* res = realloc(reinterpret_cast<void*>(0x420ul), 1); |
| 162 EXPECT_EQ(0x42u, reinterpret_cast<uintptr_t>(res)); | 162 EXPECT_EQ(reinterpret_cast<void*>(0x420ul), res); |
| 163 } | 163 } |
| 164 | 164 |
| 165 private: | 165 private: |
| 166 WaitableEvent* event_; | 166 WaitableEvent* event_; |
| 167 }; | 167 }; |
| 168 | 168 |
| 169 AllocatorShimTest* AllocatorShimTest::instance_ = nullptr; | 169 AllocatorShimTest* AllocatorShimTest::instance_ = nullptr; |
| 170 | 170 |
| 171 AllocatorDispatch g_mock_dispatch = { | 171 AllocatorDispatch g_mock_dispatch = { |
| 172 &AllocatorShimTest::MockAlloc, /* alloc_function */ | 172 &AllocatorShimTest::MockAlloc, /* alloc_function */ |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 285 | 285 |
| 286 delete[] new_array_nt_ptr; | 286 delete[] new_array_nt_ptr; |
| 287 ASSERT_GE(frees_intercepted_by_addr[Hash(new_array_nt_ptr)], 1u); | 287 ASSERT_GE(frees_intercepted_by_addr[Hash(new_array_nt_ptr)], 1u); |
| 288 | 288 |
| 289 RemoveAllocatorDispatchForTesting(&g_mock_dispatch); | 289 RemoveAllocatorDispatchForTesting(&g_mock_dispatch); |
| 290 } | 290 } |
| 291 | 291 |
| 292 // This test exercises the case of concurrent OOM failure, which would end up | 292 // This test exercises the case of concurrent OOM failure, which would end up |
| 293 // invoking std::new_handler concurrently. This is to cover the CallNewHandler() | 293 // invoking std::new_handler concurrently. This is to cover the CallNewHandler() |
| 294 // paths of allocator_shim.cc and smoke-test its thread safey. | 294 // paths of allocator_shim.cc and smoke-test its thread safey. |
| 295 // The test creates kNumThreads threads. Each of them does just a realloc(0x42). | 295 // The test creates kNumThreads threads. Each of them does just a |
| 296 // realloc(0x420). |
| 296 // The shim intercepts such realloc and makes it fail only once on each thread. | 297 // The shim intercepts such realloc and makes it fail only once on each thread. |
| 297 // We expect to see excactly kNumThreads invocations of the new_handler. | 298 // We expect to see excactly kNumThreads invocations of the new_handler. |
| 298 TEST_F(AllocatorShimTest, NewHandlerConcurrency) { | 299 TEST_F(AllocatorShimTest, NewHandlerConcurrency) { |
| 299 const int kNumThreads = 32; | 300 const int kNumThreads = 32; |
| 300 PlatformThreadHandle threads[kNumThreads]; | 301 PlatformThreadHandle threads[kNumThreads]; |
| 301 | 302 |
| 302 // The WaitableEvent here is used to attempt to trigger all the threads at | 303 // The WaitableEvent here is used to attempt to trigger all the threads at |
| 303 // the same time, after they have been initialized. | 304 // the same time, after they have been initialized. |
| 304 WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL, | 305 WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL, |
| 305 WaitableEvent::InitialState::NOT_SIGNALED); | 306 WaitableEvent::InitialState::NOT_SIGNALED); |
| 306 | 307 |
| 307 ThreadDelegateForNewHandlerTest mock_thread_main(&event); | 308 ThreadDelegateForNewHandlerTest mock_thread_main(&event); |
| 308 | 309 |
| 309 for (int i = 0; i < kNumThreads; ++i) | 310 for (int i = 0; i < kNumThreads; ++i) |
| 310 PlatformThread::Create(0, &mock_thread_main, &threads[i]); | 311 PlatformThread::Create(0, &mock_thread_main, &threads[i]); |
| 311 | 312 |
| 312 std::set_new_handler(&AllocatorShimTest::NewHandler); | 313 std::set_new_handler(&AllocatorShimTest::NewHandler); |
| 313 SetCallNewHandlerOnMallocFailure(true); // It's going to fail on realloc(). | 314 SetCallNewHandlerOnMallocFailure(true); // It's going to fail on realloc(). |
| 314 InsertAllocatorDispatch(&g_mock_dispatch); | 315 InsertAllocatorDispatch(&g_mock_dispatch); |
| 315 event.Signal(); | 316 event.Signal(); |
| 316 for (int i = 0; i < kNumThreads; ++i) | 317 for (int i = 0; i < kNumThreads; ++i) |
| 317 PlatformThread::Join(threads[i]); | 318 PlatformThread::Join(threads[i]); |
| 318 RemoveAllocatorDispatchForTesting(&g_mock_dispatch); | 319 RemoveAllocatorDispatchForTesting(&g_mock_dispatch); |
| 319 ASSERT_EQ(kNumThreads, GetNumberOfNewHandlerCalls()); | 320 ASSERT_EQ(kNumThreads, GetNumberOfNewHandlerCalls()); |
| 320 } | 321 } |
| 321 | 322 |
| 322 } // namespace | 323 } // namespace |
| 323 } // namespace allocator | 324 } // namespace allocator |
| 324 } // namespace base | 325 } // namespace base |
| OLD | NEW |