| 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" |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 50 size_t sum = 0; | 50 size_t sum = 0; |
| 51 | 51 |
| 52 for (auto i : reg) | 52 for (auto i : reg) |
| 53 sum += i.size; | 53 sum += i.size; |
| 54 | 54 |
| 55 return sum; | 55 return sum; |
| 56 } | 56 } |
| 57 | 57 |
| 58 TEST_F(AllocationRegisterTest, InsertRemove) { | 58 TEST_F(AllocationRegisterTest, InsertRemove) { |
| 59 AllocationRegister reg; | 59 AllocationRegister reg; |
| 60 AllocationContext ctx = AllocationContext::Empty(); | 60 AllocationContext ctx; |
| 61 | 61 |
| 62 // Zero-sized allocations should be discarded. | 62 // Zero-sized allocations should be discarded. |
| 63 reg.Insert(reinterpret_cast<void*>(1), 0, ctx); | 63 reg.Insert(reinterpret_cast<void*>(1), 0, ctx); |
| 64 | 64 |
| 65 EXPECT_EQ(0u, OrAllAddresses(reg)); | 65 EXPECT_EQ(0u, OrAllAddresses(reg)); |
| 66 | 66 |
| 67 reg.Insert(reinterpret_cast<void*>(1), 1, ctx); | 67 reg.Insert(reinterpret_cast<void*>(1), 1, ctx); |
| 68 | 68 |
| 69 EXPECT_EQ(1u, OrAllAddresses(reg)); | 69 EXPECT_EQ(1u, OrAllAddresses(reg)); |
| 70 | 70 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 84 | 84 |
| 85 EXPECT_EQ(1u, OrAllAddresses(reg)); | 85 EXPECT_EQ(1u, OrAllAddresses(reg)); |
| 86 | 86 |
| 87 reg.Remove(reinterpret_cast<void*>(1)); | 87 reg.Remove(reinterpret_cast<void*>(1)); |
| 88 | 88 |
| 89 EXPECT_EQ(0u, OrAllAddresses(reg)); | 89 EXPECT_EQ(0u, OrAllAddresses(reg)); |
| 90 } | 90 } |
| 91 | 91 |
| 92 TEST_F(AllocationRegisterTest, DoubleFreeIsAllowed) { | 92 TEST_F(AllocationRegisterTest, DoubleFreeIsAllowed) { |
| 93 AllocationRegister reg; | 93 AllocationRegister reg; |
| 94 AllocationContext ctx = AllocationContext::Empty(); | 94 AllocationContext ctx; |
| 95 | 95 |
| 96 reg.Insert(reinterpret_cast<void*>(1), 1, ctx); | 96 reg.Insert(reinterpret_cast<void*>(1), 1, ctx); |
| 97 reg.Insert(reinterpret_cast<void*>(2), 1, ctx); | 97 reg.Insert(reinterpret_cast<void*>(2), 1, ctx); |
| 98 reg.Remove(reinterpret_cast<void*>(1)); | 98 reg.Remove(reinterpret_cast<void*>(1)); |
| 99 reg.Remove(reinterpret_cast<void*>(1)); // Remove for the second time. | 99 reg.Remove(reinterpret_cast<void*>(1)); // Remove for the second time. |
| 100 reg.Remove(reinterpret_cast<void*>(4)); // Remove never inserted address. | 100 reg.Remove(reinterpret_cast<void*>(4)); // Remove never inserted address. |
| 101 | 101 |
| 102 EXPECT_EQ(2u, OrAllAddresses(reg)); | 102 EXPECT_EQ(2u, OrAllAddresses(reg)); |
| 103 } | 103 } |
| 104 | 104 |
| 105 TEST_F(AllocationRegisterTest, DoubleInsertOverwrites) { | 105 TEST_F(AllocationRegisterTest, DoubleInsertOverwrites) { |
| 106 // TODO(ruuda): Although double insert happens in practice, it should not. | 106 // TODO(ruuda): Although double insert happens in practice, it should not. |
| 107 // Find out the cause and ban double insert if possible. | 107 // Find out the cause and ban double insert if possible. |
| 108 AllocationRegister reg; | 108 AllocationRegister reg; |
| 109 AllocationContext ctx = AllocationContext::Empty(); | 109 AllocationContext ctx; |
| 110 StackFrame frame1 = "Foo"; | 110 StackFrame frame1 = StackFrame::FromTraceEventName("Foo"); |
| 111 StackFrame frame2 = "Bar"; | 111 StackFrame frame2 = StackFrame::FromTraceEventName("Bar"); |
| 112 |
| 113 ctx.backtrace.frame_count = 1; |
| 112 | 114 |
| 113 ctx.backtrace.frames[0] = frame1; | 115 ctx.backtrace.frames[0] = frame1; |
| 114 reg.Insert(reinterpret_cast<void*>(1), 11, ctx); | 116 reg.Insert(reinterpret_cast<void*>(1), 11, ctx); |
| 115 | 117 |
| 116 { | 118 { |
| 117 AllocationRegister::Allocation elem = *reg.begin(); | 119 AllocationRegister::Allocation elem = *reg.begin(); |
| 118 | 120 |
| 119 EXPECT_EQ(frame1, elem.context.backtrace.frames[0]); | 121 EXPECT_EQ(frame1, elem.context.backtrace.frames[0]); |
| 120 EXPECT_EQ(11u, elem.size); | 122 EXPECT_EQ(11u, elem.size); |
| 121 EXPECT_EQ(reinterpret_cast<void*>(1), elem.address); | 123 EXPECT_EQ(reinterpret_cast<void*>(1), elem.address); |
| 122 } | 124 } |
| 123 | 125 |
| 124 ctx.backtrace.frames[0] = frame2; | 126 ctx.backtrace.frames[0] = frame2; |
| 125 reg.Insert(reinterpret_cast<void*>(1), 13, ctx); | 127 reg.Insert(reinterpret_cast<void*>(1), 13, ctx); |
| 126 | 128 |
| 127 { | 129 { |
| 128 AllocationRegister::Allocation elem = *reg.begin(); | 130 AllocationRegister::Allocation elem = *reg.begin(); |
| 129 | 131 |
| 130 EXPECT_EQ(frame2, elem.context.backtrace.frames[0]); | 132 EXPECT_EQ(frame2, elem.context.backtrace.frames[0]); |
| 131 EXPECT_EQ(13u, elem.size); | 133 EXPECT_EQ(13u, elem.size); |
| 132 EXPECT_EQ(reinterpret_cast<void*>(1), elem.address); | 134 EXPECT_EQ(reinterpret_cast<void*>(1), elem.address); |
| 133 } | 135 } |
| 134 } | 136 } |
| 135 | 137 |
| 136 // Check that even if more entries than the number of buckets are inserted, the | 138 // Check that even if more entries than the number of buckets are inserted, the |
| 137 // register still behaves correctly. | 139 // register still behaves correctly. |
| 138 TEST_F(AllocationRegisterTest, InsertRemoveCollisions) { | 140 TEST_F(AllocationRegisterTest, InsertRemoveCollisions) { |
| 139 size_t expected_sum = 0; | 141 size_t expected_sum = 0; |
| 140 AllocationRegister reg; | 142 AllocationRegister reg; |
| 141 AllocationContext ctx = AllocationContext::Empty(); | 143 AllocationContext ctx; |
| 142 | 144 |
| 143 // By inserting 100 more entries than the number of buckets, there will be at | 145 // By inserting 100 more entries than the number of buckets, there will be at |
| 144 // least 100 collisions. | 146 // least 100 collisions. |
| 145 for (uintptr_t i = 1; i <= kNumBuckets + 100; i++) { | 147 for (uintptr_t i = 1; i <= kNumBuckets + 100; i++) { |
| 146 size_t size = i % 31; | 148 size_t size = i % 31; |
| 147 expected_sum += size; | 149 expected_sum += size; |
| 148 reg.Insert(reinterpret_cast<void*>(i), size, ctx); | 150 reg.Insert(reinterpret_cast<void*>(i), size, ctx); |
| 149 | 151 |
| 150 // Don't check the sum on every iteration to keep the test fast. | 152 // Don't check the sum on every iteration to keep the test fast. |
| 151 if (i % (1 << 14) == 0) | 153 if (i % (1 << 14) == 0) |
| (...skipping 16 matching lines...) Expand all Loading... |
| 168 | 170 |
| 169 // The previous tests are not particularly good for testing iterators, because | 171 // The previous tests are not particularly good for testing iterators, because |
| 170 // elements are removed and inserted in the same order, meaning that the cells | 172 // elements are removed and inserted in the same order, meaning that the cells |
| 171 // fill up from low to high index, and are then freed from low to high index. | 173 // fill up from low to high index, and are then freed from low to high index. |
| 172 // This test removes entries in a different order, to ensure that the iterator | 174 // This test removes entries in a different order, to ensure that the iterator |
| 173 // skips over the freed cells properly. Then insert again to ensure that the | 175 // skips over the freed cells properly. Then insert again to ensure that the |
| 174 // free list is utilised properly. | 176 // free list is utilised properly. |
| 175 TEST_F(AllocationRegisterTest, InsertRemoveRandomOrder) { | 177 TEST_F(AllocationRegisterTest, InsertRemoveRandomOrder) { |
| 176 size_t expected_sum = 0; | 178 size_t expected_sum = 0; |
| 177 AllocationRegister reg; | 179 AllocationRegister reg; |
| 178 AllocationContext ctx = AllocationContext::Empty(); | 180 AllocationContext ctx; |
| 179 | 181 |
| 180 uintptr_t generator = 3; | 182 uintptr_t generator = 3; |
| 181 uintptr_t prime = 1013; | 183 uintptr_t prime = 1013; |
| 182 uint32_t initial_water_mark = GetHighWaterMark(reg); | 184 uint32_t initial_water_mark = GetHighWaterMark(reg); |
| 183 | 185 |
| 184 for (uintptr_t i = 2; i < prime; i++) { | 186 for (uintptr_t i = 2; i < prime; i++) { |
| 185 size_t size = i % 31 + 1; | 187 size_t size = i % 31 + 1; |
| 186 expected_sum += size; | 188 expected_sum += size; |
| 187 reg.Insert(reinterpret_cast<void*>(i), size, ctx); | 189 reg.Insert(reinterpret_cast<void*>(i), size, ctx); |
| 188 } | 190 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 209 | 211 |
| 210 // Inserting one more entry should use a fresh cell again. | 212 // Inserting one more entry should use a fresh cell again. |
| 211 reg.Insert(reinterpret_cast<void*>(prime), 1, ctx); | 213 reg.Insert(reinterpret_cast<void*>(prime), 1, ctx); |
| 212 ASSERT_EQ(prime - 1, GetHighWaterMark(reg) - initial_water_mark); | 214 ASSERT_EQ(prime - 1, GetHighWaterMark(reg) - initial_water_mark); |
| 213 } | 215 } |
| 214 | 216 |
| 215 TEST_F(AllocationRegisterTest, ChangeContextAfterInsertion) { | 217 TEST_F(AllocationRegisterTest, ChangeContextAfterInsertion) { |
| 216 using Allocation = AllocationRegister::Allocation; | 218 using Allocation = AllocationRegister::Allocation; |
| 217 const char kStdString[] = "std::string"; | 219 const char kStdString[] = "std::string"; |
| 218 AllocationRegister reg; | 220 AllocationRegister reg; |
| 219 AllocationContext ctx = AllocationContext::Empty(); | 221 AllocationContext ctx; |
| 220 | 222 |
| 221 reg.Insert(reinterpret_cast<void*>(17), 1, ctx); | 223 reg.Insert(reinterpret_cast<void*>(17), 1, ctx); |
| 222 reg.Insert(reinterpret_cast<void*>(19), 2, ctx); | 224 reg.Insert(reinterpret_cast<void*>(19), 2, ctx); |
| 223 reg.Insert(reinterpret_cast<void*>(23), 3, ctx); | 225 reg.Insert(reinterpret_cast<void*>(23), 3, ctx); |
| 224 | 226 |
| 225 // Looking up addresses that were not inserted should return null. | 227 // Looking up addresses that were not inserted should return null. |
| 226 // A null pointer lookup is a valid thing to do. | 228 // A null pointer lookup is a valid thing to do. |
| 227 EXPECT_EQ(nullptr, reg.Get(nullptr)); | 229 EXPECT_EQ(nullptr, reg.Get(nullptr)); |
| 228 EXPECT_EQ(nullptr, reg.Get(reinterpret_cast<void*>(13))); | 230 EXPECT_EQ(nullptr, reg.Get(reinterpret_cast<void*>(13))); |
| 229 | 231 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 257 EXPECT_EQ(nullptr, reg.Get(reinterpret_cast<void*>(17))); | 259 EXPECT_EQ(nullptr, reg.Get(reinterpret_cast<void*>(17))); |
| 258 EXPECT_EQ(nullptr, reg.Get(reinterpret_cast<void*>(19))); | 260 EXPECT_EQ(nullptr, reg.Get(reinterpret_cast<void*>(19))); |
| 259 } | 261 } |
| 260 | 262 |
| 261 // Check that the process aborts due to hitting the guard page when inserting | 263 // Check that the process aborts due to hitting the guard page when inserting |
| 262 // too many elements. | 264 // too many elements. |
| 263 #if GTEST_HAS_DEATH_TEST | 265 #if GTEST_HAS_DEATH_TEST |
| 264 TEST_F(AllocationRegisterTest, OverflowDeathTest) { | 266 TEST_F(AllocationRegisterTest, OverflowDeathTest) { |
| 265 // Use a smaller register to prevent OOM errors on low-end devices. | 267 // Use a smaller register to prevent OOM errors on low-end devices. |
| 266 AllocationRegister reg(static_cast<uint32_t>(GetNumCellsPerPage())); | 268 AllocationRegister reg(static_cast<uint32_t>(GetNumCellsPerPage())); |
| 267 AllocationContext ctx = AllocationContext::Empty(); | 269 AllocationContext ctx; |
| 268 uintptr_t i; | 270 uintptr_t i; |
| 269 | 271 |
| 270 // Fill up all of the memory allocated for the register. |GetNumCells(reg)| | 272 // Fill up all of the memory allocated for the register. |GetNumCells(reg)| |
| 271 // minus 1 elements are inserted, because cell 0 is unused, so this should | 273 // minus 1 elements are inserted, because cell 0 is unused, so this should |
| 272 // fill up the available cells exactly. | 274 // fill up the available cells exactly. |
| 273 for (i = 1; i < GetNumCells(reg); i++) { | 275 for (i = 1; i < GetNumCells(reg); i++) { |
| 274 reg.Insert(reinterpret_cast<void*>(i), 1, ctx); | 276 reg.Insert(reinterpret_cast<void*>(i), 1, ctx); |
| 275 } | 277 } |
| 276 | 278 |
| 277 // Adding just one extra element might still work because the allocated memory | 279 // Adding just one extra element might still work because the allocated memory |
| 278 // is rounded up to the page size. Adding a page full of elements should cause | 280 // is rounded up to the page size. Adding a page full of elements should cause |
| 279 // overflow. | 281 // overflow. |
| 280 const size_t cells_per_page = GetNumCellsPerPage(); | 282 const size_t cells_per_page = GetNumCellsPerPage(); |
| 281 | 283 |
| 282 ASSERT_DEATH(for (size_t j = 0; j < cells_per_page; j++) { | 284 ASSERT_DEATH(for (size_t j = 0; j < cells_per_page; j++) { |
| 283 reg.Insert(reinterpret_cast<void*>(i + j), 1, ctx); | 285 reg.Insert(reinterpret_cast<void*>(i + j), 1, ctx); |
| 284 }, ""); | 286 }, ""); |
| 285 } | 287 } |
| 286 #endif | 288 #endif |
| 287 | 289 |
| 288 } // namespace trace_event | 290 } // namespace trace_event |
| 289 } // namespace base | 291 } // namespace base |
| OLD | NEW |