| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/trace_event/heap_profiler_allocation_register.h" | 5 #include "base/trace_event/heap_profiler_allocation_register.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include "base/process/process_metrics.h" | 10 #include "base/process/process_metrics.h" |
| 11 #include "base/trace_event/heap_profiler_allocation_context.h" | 11 #include "base/trace_event/heap_profiler_allocation_context.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 | 13 |
| 14 namespace base { | 14 namespace base { |
| 15 namespace trace_event { | 15 namespace trace_event { |
| 16 | 16 |
| 17 class AllocationRegisterTest : public testing::Test { | 17 class AllocationRegisterTest : public testing::Test { |
| 18 public: | 18 public: |
| 19 static const uint32_t kNumBuckets = AllocationRegister::kNumBuckets; | 19 // Use a lower number of cells for unittests to avoid reserving a virtual |
| 20 // region which is too big. |
| 21 static const uint32_t kNumCellsForTesting = |
| 22 AllocationRegister::kNumBuckets + 100; |
| 20 | 23 |
| 21 // Returns the number of cells that the |AllocationRegister| can store per | 24 // Returns the number of cells that the |AllocationRegister| can store per |
| 22 // system page. | 25 // system page. |
| 23 size_t GetNumCellsPerPage() { | 26 size_t GetNumCellsPerPage() { |
| 24 return GetPageSize() / sizeof(AllocationRegister::Cell); | 27 return GetPageSize() / sizeof(AllocationRegister::Cell); |
| 25 } | 28 } |
| 26 | 29 |
| 27 uint32_t GetHighWaterMark(const AllocationRegister& reg) { | 30 uint32_t GetHighWaterMark(const AllocationRegister& reg) { |
| 28 return reg.next_unused_cell_; | 31 return reg.next_unused_cell_; |
| 29 } | 32 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 49 size_t SumAllSizes(const AllocationRegister& reg) { | 52 size_t SumAllSizes(const AllocationRegister& reg) { |
| 50 size_t sum = 0; | 53 size_t sum = 0; |
| 51 | 54 |
| 52 for (auto i : reg) | 55 for (auto i : reg) |
| 53 sum += i.size; | 56 sum += i.size; |
| 54 | 57 |
| 55 return sum; | 58 return sum; |
| 56 } | 59 } |
| 57 | 60 |
| 58 TEST_F(AllocationRegisterTest, InsertRemove) { | 61 TEST_F(AllocationRegisterTest, InsertRemove) { |
| 59 AllocationRegister reg; | 62 AllocationRegister reg(kNumCellsForTesting); |
| 60 AllocationContext ctx; | 63 AllocationContext ctx; |
| 61 | 64 |
| 62 // Zero-sized allocations should be discarded. | 65 // Zero-sized allocations should be discarded. |
| 63 reg.Insert(reinterpret_cast<void*>(1), 0, ctx); | 66 reg.Insert(reinterpret_cast<void*>(1), 0, ctx); |
| 64 | 67 |
| 65 EXPECT_EQ(0u, OrAllAddresses(reg)); | 68 EXPECT_EQ(0u, OrAllAddresses(reg)); |
| 66 | 69 |
| 67 reg.Insert(reinterpret_cast<void*>(1), 1, ctx); | 70 reg.Insert(reinterpret_cast<void*>(1), 1, ctx); |
| 68 | 71 |
| 69 EXPECT_EQ(1u, OrAllAddresses(reg)); | 72 EXPECT_EQ(1u, OrAllAddresses(reg)); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 83 reg.Remove(reinterpret_cast<void*>(4)); | 86 reg.Remove(reinterpret_cast<void*>(4)); |
| 84 | 87 |
| 85 EXPECT_EQ(1u, OrAllAddresses(reg)); | 88 EXPECT_EQ(1u, OrAllAddresses(reg)); |
| 86 | 89 |
| 87 reg.Remove(reinterpret_cast<void*>(1)); | 90 reg.Remove(reinterpret_cast<void*>(1)); |
| 88 | 91 |
| 89 EXPECT_EQ(0u, OrAllAddresses(reg)); | 92 EXPECT_EQ(0u, OrAllAddresses(reg)); |
| 90 } | 93 } |
| 91 | 94 |
| 92 TEST_F(AllocationRegisterTest, DoubleFreeIsAllowed) { | 95 TEST_F(AllocationRegisterTest, DoubleFreeIsAllowed) { |
| 93 AllocationRegister reg; | 96 AllocationRegister reg(kNumCellsForTesting); |
| 94 AllocationContext ctx; | 97 AllocationContext ctx; |
| 95 | 98 |
| 96 reg.Insert(reinterpret_cast<void*>(1), 1, ctx); | 99 reg.Insert(reinterpret_cast<void*>(1), 1, ctx); |
| 97 reg.Insert(reinterpret_cast<void*>(2), 1, ctx); | 100 reg.Insert(reinterpret_cast<void*>(2), 1, ctx); |
| 98 reg.Remove(reinterpret_cast<void*>(1)); | 101 reg.Remove(reinterpret_cast<void*>(1)); |
| 99 reg.Remove(reinterpret_cast<void*>(1)); // Remove for the second time. | 102 reg.Remove(reinterpret_cast<void*>(1)); // Remove for the second time. |
| 100 reg.Remove(reinterpret_cast<void*>(4)); // Remove never inserted address. | 103 reg.Remove(reinterpret_cast<void*>(4)); // Remove never inserted address. |
| 101 | 104 |
| 102 EXPECT_EQ(2u, OrAllAddresses(reg)); | 105 EXPECT_EQ(2u, OrAllAddresses(reg)); |
| 103 } | 106 } |
| 104 | 107 |
| 105 TEST_F(AllocationRegisterTest, DoubleInsertOverwrites) { | 108 TEST_F(AllocationRegisterTest, DoubleInsertOverwrites) { |
| 106 // TODO(ruuda): Although double insert happens in practice, it should not. | 109 AllocationRegister reg(kNumCellsForTesting); |
| 107 // Find out the cause and ban double insert if possible. | |
| 108 AllocationRegister reg; | |
| 109 AllocationContext ctx; | 110 AllocationContext ctx; |
| 110 StackFrame frame1 = StackFrame::FromTraceEventName("Foo"); | 111 StackFrame frame1 = StackFrame::FromTraceEventName("Foo"); |
| 111 StackFrame frame2 = StackFrame::FromTraceEventName("Bar"); | 112 StackFrame frame2 = StackFrame::FromTraceEventName("Bar"); |
| 112 | 113 |
| 113 ctx.backtrace.frame_count = 1; | 114 ctx.backtrace.frame_count = 1; |
| 114 | 115 |
| 115 ctx.backtrace.frames[0] = frame1; | 116 ctx.backtrace.frames[0] = frame1; |
| 116 reg.Insert(reinterpret_cast<void*>(1), 11, ctx); | 117 reg.Insert(reinterpret_cast<void*>(1), 11, ctx); |
| 117 | 118 |
| 118 { | 119 { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 132 EXPECT_EQ(frame2, elem.context.backtrace.frames[0]); | 133 EXPECT_EQ(frame2, elem.context.backtrace.frames[0]); |
| 133 EXPECT_EQ(13u, elem.size); | 134 EXPECT_EQ(13u, elem.size); |
| 134 EXPECT_EQ(reinterpret_cast<void*>(1), elem.address); | 135 EXPECT_EQ(reinterpret_cast<void*>(1), elem.address); |
| 135 } | 136 } |
| 136 } | 137 } |
| 137 | 138 |
| 138 // Check that even if more entries than the number of buckets are inserted, the | 139 // Check that even if more entries than the number of buckets are inserted, the |
| 139 // register still behaves correctly. | 140 // register still behaves correctly. |
| 140 TEST_F(AllocationRegisterTest, InsertRemoveCollisions) { | 141 TEST_F(AllocationRegisterTest, InsertRemoveCollisions) { |
| 141 size_t expected_sum = 0; | 142 size_t expected_sum = 0; |
| 142 AllocationRegister reg; | 143 AllocationRegister reg(kNumCellsForTesting); |
| 143 AllocationContext ctx; | 144 AllocationContext ctx; |
| 144 | 145 |
| 145 // By inserting 100 more entries than the number of buckets, there will be at | 146 // By inserting 100 more entries than the number of buckets, there will be at |
| 146 // least 100 collisions. | 147 // least 100 collisions (100 = kNumCellsForTesting - kNumBuckets). |
| 147 for (uintptr_t i = 1; i <= kNumBuckets + 100; i++) { | 148 for (uintptr_t i = 1; i <= kNumCellsForTesting; i++) { |
| 148 size_t size = i % 31; | 149 size_t size = i % 31; |
| 149 expected_sum += size; | 150 expected_sum += size; |
| 150 reg.Insert(reinterpret_cast<void*>(i), size, ctx); | 151 reg.Insert(reinterpret_cast<void*>(i), size, ctx); |
| 151 | 152 |
| 152 // Don't check the sum on every iteration to keep the test fast. | 153 // Don't check the sum on every iteration to keep the test fast. |
| 153 if (i % (1 << 14) == 0) | 154 if (i % (1 << 14) == 0) |
| 154 EXPECT_EQ(expected_sum, SumAllSizes(reg)); | 155 EXPECT_EQ(expected_sum, SumAllSizes(reg)); |
| 155 } | 156 } |
| 156 | 157 |
| 157 EXPECT_EQ(expected_sum, SumAllSizes(reg)); | 158 EXPECT_EQ(expected_sum, SumAllSizes(reg)); |
| 158 | 159 |
| 159 for (uintptr_t i = 1; i <= kNumBuckets + 100; i++) { | 160 for (uintptr_t i = 1; i <= kNumCellsForTesting; i++) { |
| 160 size_t size = i % 31; | 161 size_t size = i % 31; |
| 161 expected_sum -= size; | 162 expected_sum -= size; |
| 162 reg.Remove(reinterpret_cast<void*>(i)); | 163 reg.Remove(reinterpret_cast<void*>(i)); |
| 163 | 164 |
| 164 if (i % (1 << 14) == 0) | 165 if (i % (1 << 14) == 0) |
| 165 EXPECT_EQ(expected_sum, SumAllSizes(reg)); | 166 EXPECT_EQ(expected_sum, SumAllSizes(reg)); |
| 166 } | 167 } |
| 167 | 168 |
| 168 EXPECT_EQ(expected_sum, SumAllSizes(reg)); | 169 EXPECT_EQ(expected_sum, SumAllSizes(reg)); |
| 169 } | 170 } |
| 170 | 171 |
| 171 // The previous tests are not particularly good for testing iterators, because | 172 // The previous tests are not particularly good for testing iterators, because |
| 172 // elements are removed and inserted in the same order, meaning that the cells | 173 // elements are removed and inserted in the same order, meaning that the cells |
| 173 // fill up from low to high index, and are then freed from low to high index. | 174 // fill up from low to high index, and are then freed from low to high index. |
| 174 // This test removes entries in a different order, to ensure that the iterator | 175 // This test removes entries in a different order, to ensure that the iterator |
| 175 // skips over the freed cells properly. Then insert again to ensure that the | 176 // skips over the freed cells properly. Then insert again to ensure that the |
| 176 // free list is utilised properly. | 177 // free list is utilised properly. |
| 177 TEST_F(AllocationRegisterTest, InsertRemoveRandomOrder) { | 178 TEST_F(AllocationRegisterTest, InsertRemoveRandomOrder) { |
| 178 size_t expected_sum = 0; | 179 size_t expected_sum = 0; |
| 179 AllocationRegister reg; | 180 AllocationRegister reg(kNumCellsForTesting); |
| 180 AllocationContext ctx; | 181 AllocationContext ctx; |
| 181 | 182 |
| 182 uintptr_t generator = 3; | 183 uintptr_t generator = 3; |
| 183 uintptr_t prime = 1013; | 184 uintptr_t prime = 1013; |
| 184 uint32_t initial_water_mark = GetHighWaterMark(reg); | 185 uint32_t initial_water_mark = GetHighWaterMark(reg); |
| 185 | 186 |
| 186 for (uintptr_t i = 2; i < prime; i++) { | 187 for (uintptr_t i = 2; i < prime; i++) { |
| 187 size_t size = i % 31 + 1; | 188 size_t size = i % 31 + 1; |
| 188 expected_sum += size; | 189 expected_sum += size; |
| 189 reg.Insert(reinterpret_cast<void*>(i), size, ctx); | 190 reg.Insert(reinterpret_cast<void*>(i), size, ctx); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 210 ASSERT_EQ(prime - 2, GetHighWaterMark(reg) - initial_water_mark); | 211 ASSERT_EQ(prime - 2, GetHighWaterMark(reg) - initial_water_mark); |
| 211 | 212 |
| 212 // Inserting one more entry should use a fresh cell again. | 213 // Inserting one more entry should use a fresh cell again. |
| 213 reg.Insert(reinterpret_cast<void*>(prime), 1, ctx); | 214 reg.Insert(reinterpret_cast<void*>(prime), 1, ctx); |
| 214 ASSERT_EQ(prime - 1, GetHighWaterMark(reg) - initial_water_mark); | 215 ASSERT_EQ(prime - 1, GetHighWaterMark(reg) - initial_water_mark); |
| 215 } | 216 } |
| 216 | 217 |
| 217 TEST_F(AllocationRegisterTest, ChangeContextAfterInsertion) { | 218 TEST_F(AllocationRegisterTest, ChangeContextAfterInsertion) { |
| 218 using Allocation = AllocationRegister::Allocation; | 219 using Allocation = AllocationRegister::Allocation; |
| 219 const char kStdString[] = "std::string"; | 220 const char kStdString[] = "std::string"; |
| 220 AllocationRegister reg; | 221 AllocationRegister reg(kNumCellsForTesting); |
| 221 AllocationContext ctx; | 222 AllocationContext ctx; |
| 222 | 223 |
| 223 reg.Insert(reinterpret_cast<void*>(17), 1, ctx); | 224 reg.Insert(reinterpret_cast<void*>(17), 1, ctx); |
| 224 reg.Insert(reinterpret_cast<void*>(19), 2, ctx); | 225 reg.Insert(reinterpret_cast<void*>(19), 2, ctx); |
| 225 reg.Insert(reinterpret_cast<void*>(23), 3, ctx); | 226 reg.Insert(reinterpret_cast<void*>(23), 3, ctx); |
| 226 | 227 |
| 227 // Looking up addresses that were not inserted should return null. | 228 // Looking up addresses that were not inserted should return null. |
| 228 // A null pointer lookup is a valid thing to do. | 229 // A null pointer lookup is a valid thing to do. |
| 229 EXPECT_EQ(nullptr, reg.Get(nullptr)); | 230 EXPECT_EQ(nullptr, reg.Get(nullptr)); |
| 230 EXPECT_EQ(nullptr, reg.Get(reinterpret_cast<void*>(13))); | 231 EXPECT_EQ(nullptr, reg.Get(reinterpret_cast<void*>(13))); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 282 const size_t cells_per_page = GetNumCellsPerPage(); | 283 const size_t cells_per_page = GetNumCellsPerPage(); |
| 283 | 284 |
| 284 ASSERT_DEATH(for (size_t j = 0; j < cells_per_page; j++) { | 285 ASSERT_DEATH(for (size_t j = 0; j < cells_per_page; j++) { |
| 285 reg.Insert(reinterpret_cast<void*>(i + j), 1, ctx); | 286 reg.Insert(reinterpret_cast<void*>(i + j), 1, ctx); |
| 286 }, ""); | 287 }, ""); |
| 287 } | 288 } |
| 288 #endif | 289 #endif |
| 289 | 290 |
| 290 } // namespace trace_event | 291 } // namespace trace_event |
| 291 } // namespace base | 292 } // namespace base |
| OLD | NEW |