OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "components/metrics/leak_detector/call_stack_table.h" |
| 6 |
| 7 #include <gperftools/custom_allocator.h> |
| 8 |
| 9 #include "base/macros.h" |
| 10 #include "testing/gtest/include/gtest/gtest.h" |
| 11 |
| 12 namespace leak_detector { |
| 13 |
| 14 namespace { |
| 15 |
| 16 // Default threshold used for leak analysis. |
| 17 const int kDefaultLeakThreshold = 5; |
| 18 |
| 19 // Some test call stacks. |
| 20 const void* kRawStack0[] = { |
| 21 reinterpret_cast<const void*>(0xaabbccdd), |
| 22 reinterpret_cast<const void*>(0x11223344), |
| 23 reinterpret_cast<const void*>(0x55667788), |
| 24 reinterpret_cast<const void*>(0x99887766), |
| 25 }; |
| 26 const void* kRawStack1[] = { |
| 27 reinterpret_cast<const void*>(0xdeadbeef), |
| 28 reinterpret_cast<const void*>(0x900df00d), |
| 29 reinterpret_cast<const void*>(0xcafedeed), |
| 30 reinterpret_cast<const void*>(0xdeafbabe), |
| 31 }; |
| 32 const void* kRawStack2[] = { |
| 33 reinterpret_cast<const void*>(0x12345678), |
| 34 reinterpret_cast<const void*>(0xabcdef01), |
| 35 reinterpret_cast<const void*>(0xfdecab98), |
| 36 }; |
| 37 const void* kRawStack3[] = { |
| 38 reinterpret_cast<const void*>(0xdead0001), |
| 39 reinterpret_cast<const void*>(0xbeef0002), |
| 40 reinterpret_cast<const void*>(0x900d0003), |
| 41 reinterpret_cast<const void*>(0xf00d0004), |
| 42 reinterpret_cast<const void*>(0xcafe0005), |
| 43 reinterpret_cast<const void*>(0xdeed0006), |
| 44 reinterpret_cast<const void*>(0xdeaf0007), |
| 45 reinterpret_cast<const void*>(0xbabe0008), |
| 46 }; |
| 47 |
| 48 // Generates a CallStack object from a raw call stack. |
| 49 CallStack GenerateCallStack(uint32_t depth, const void** raw_call_stack) { |
| 50 CallStack new_stack; |
| 51 new_stack.depth = depth; |
| 52 new_stack.stack = raw_call_stack; |
| 53 new_stack.hash = CallStack::ComputeHash()(&new_stack); |
| 54 |
| 55 return new_stack; |
| 56 } |
| 57 |
| 58 // The unit tests require that call stack objects are placed in the proper |
| 59 // sequence in memory. It is an important detail when checking the output of |
| 60 // LeakAnalyzer's suspected leaks, which are ordered by the leak value. |
| 61 // Instantiate the objects as part of an array to ensure their order in memory. |
| 62 const CallStack kCallStacks[] = { |
| 63 GenerateCallStack(arraysize(kRawStack0), kRawStack0), |
| 64 GenerateCallStack(arraysize(kRawStack1), kRawStack1), |
| 65 GenerateCallStack(arraysize(kRawStack2), kRawStack2), |
| 66 GenerateCallStack(arraysize(kRawStack3), kRawStack3), |
| 67 }; |
| 68 |
| 69 // Unit tests should directly reference these pointers to CallStack objects. |
| 70 const CallStack* kStack0 = &kCallStacks[0]; |
| 71 const CallStack* kStack1 = &kCallStacks[1]; |
| 72 const CallStack* kStack2 = &kCallStacks[2]; |
| 73 const CallStack* kStack3 = &kCallStacks[3]; |
| 74 |
| 75 } // namespace |
| 76 |
| 77 class CallStackTableTest : public ::testing::Test { |
| 78 public: |
| 79 CallStackTableTest() {} |
| 80 |
| 81 void SetUp() override { |
| 82 CustomAllocator::InitializeForUnitTest(); |
| 83 } |
| 84 void TearDown() override { |
| 85 CustomAllocator::Shutdown(); |
| 86 } |
| 87 |
| 88 private: |
| 89 DISALLOW_COPY_AND_ASSIGN(CallStackTableTest); |
| 90 }; |
| 91 |
| 92 TEST_F(CallStackTableTest, Hash) { |
| 93 // Ensure increasing order of call stack placement in memory. |
| 94 EXPECT_LT(kStack0, kStack1); |
| 95 EXPECT_LT(kStack1, kStack2); |
| 96 EXPECT_LT(kStack2, kStack3); |
| 97 |
| 98 // Hash function should generate nonzero values. |
| 99 EXPECT_NE(0U, kStack0->hash); |
| 100 EXPECT_NE(0U, kStack1->hash); |
| 101 EXPECT_NE(0U, kStack2->hash); |
| 102 EXPECT_NE(0U, kStack3->hash); |
| 103 |
| 104 // Hash function should generate unique hashes for each call stack. |
| 105 EXPECT_NE(kStack0->hash, kStack1->hash); |
| 106 EXPECT_NE(kStack0->hash, kStack2->hash); |
| 107 EXPECT_NE(kStack0->hash, kStack3->hash); |
| 108 EXPECT_NE(kStack1->hash, kStack2->hash); |
| 109 EXPECT_NE(kStack1->hash, kStack3->hash); |
| 110 EXPECT_NE(kStack2->hash, kStack3->hash); |
| 111 } |
| 112 |
| 113 TEST_F(CallStackTableTest, HashWithReducedDepth) { |
| 114 ASSERT_GT(kStack3->depth, 4U); |
| 115 |
| 116 // Hash function should only operate on the first |CallStack::depth| elements |
| 117 // of CallStack::stack. To test this, reduce the depth value of one of the |
| 118 // stacks and make sure the hash changes. |
| 119 EXPECT_NE(kStack3->hash, |
| 120 GenerateCallStack(kStack3->depth - 1, kStack3->stack).hash); |
| 121 EXPECT_NE(kStack3->hash, |
| 122 GenerateCallStack(kStack3->depth - 2, kStack3->stack).hash); |
| 123 EXPECT_NE(kStack3->hash, |
| 124 GenerateCallStack(kStack3->depth - 3, kStack3->stack).hash); |
| 125 EXPECT_NE(kStack3->hash, |
| 126 GenerateCallStack(kStack3->depth - 4, kStack3->stack).hash); |
| 127 } |
| 128 |
| 129 TEST_F(CallStackTableTest, EmptyTable) { |
| 130 CallStackTable table(kDefaultLeakThreshold); |
| 131 EXPECT_TRUE(table.empty()); |
| 132 |
| 133 EXPECT_EQ(0U, table.num_allocs()); |
| 134 EXPECT_EQ(0U, table.num_frees()); |
| 135 |
| 136 // The table should be able to gracefully handle an attempt to remove a call |
| 137 // stack entry when none exists. |
| 138 table.Remove(kStack0); |
| 139 table.Remove(kStack1); |
| 140 table.Remove(kStack2); |
| 141 table.Remove(kStack3); |
| 142 |
| 143 EXPECT_EQ(0U, table.num_allocs()); |
| 144 EXPECT_EQ(0U, table.num_frees()); |
| 145 } |
| 146 |
| 147 TEST_F(CallStackTableTest, InsertionAndRemoval) { |
| 148 CallStackTable table(kDefaultLeakThreshold); |
| 149 |
| 150 table.Add(kStack0); |
| 151 EXPECT_EQ(1U, table.size()); |
| 152 EXPECT_EQ(1U, table.num_allocs()); |
| 153 table.Add(kStack1); |
| 154 EXPECT_EQ(2U, table.size()); |
| 155 EXPECT_EQ(2U, table.num_allocs()); |
| 156 table.Add(kStack2); |
| 157 EXPECT_EQ(3U, table.size()); |
| 158 EXPECT_EQ(3U, table.num_allocs()); |
| 159 table.Add(kStack3); |
| 160 EXPECT_EQ(4U, table.size()); |
| 161 EXPECT_EQ(4U, table.num_allocs()); |
| 162 |
| 163 // Add some call stacks that have already been added. There should be no |
| 164 // change in the number of entries, as they are aggregated by call stack. |
| 165 table.Add(kStack2); |
| 166 EXPECT_EQ(4U, table.size()); |
| 167 EXPECT_EQ(5U, table.num_allocs()); |
| 168 table.Add(kStack3); |
| 169 EXPECT_EQ(4U, table.size()); |
| 170 EXPECT_EQ(6U, table.num_allocs()); |
| 171 |
| 172 // Start removing entries. |
| 173 EXPECT_EQ(0U, table.num_frees()); |
| 174 |
| 175 table.Remove(kStack0); |
| 176 EXPECT_EQ(3U, table.size()); |
| 177 EXPECT_EQ(1U, table.num_frees()); |
| 178 table.Remove(kStack1); |
| 179 EXPECT_EQ(2U, table.size()); |
| 180 EXPECT_EQ(2U, table.num_frees()); |
| 181 |
| 182 // Removing call stacks with multiple counts will not reduce the overall |
| 183 // number of table entries, until the count reaches 0. |
| 184 table.Remove(kStack2); |
| 185 EXPECT_EQ(2U, table.size()); |
| 186 EXPECT_EQ(3U, table.num_frees()); |
| 187 table.Remove(kStack3); |
| 188 EXPECT_EQ(2U, table.size()); |
| 189 EXPECT_EQ(4U, table.num_frees()); |
| 190 |
| 191 table.Remove(kStack2); |
| 192 EXPECT_EQ(1U, table.size()); |
| 193 EXPECT_EQ(5U, table.num_frees()); |
| 194 table.Remove(kStack3); |
| 195 EXPECT_EQ(0U, table.size()); |
| 196 EXPECT_EQ(6U, table.num_frees()); |
| 197 |
| 198 // Now the table should be empty, but attempt to remove some more and make |
| 199 // sure nothing breaks. |
| 200 table.Remove(kStack0); |
| 201 table.Remove(kStack1); |
| 202 table.Remove(kStack2); |
| 203 table.Remove(kStack3); |
| 204 |
| 205 EXPECT_TRUE(table.empty()); |
| 206 EXPECT_EQ(6U, table.num_allocs()); |
| 207 EXPECT_EQ(6U, table.num_frees()); |
| 208 } |
| 209 |
| 210 TEST_F(CallStackTableTest, MassiveInsertionAndRemoval) { |
| 211 CallStackTable table(kDefaultLeakThreshold); |
| 212 |
| 213 for (int i = 0; i < 100; ++i) |
| 214 table.Add(kStack3); |
| 215 EXPECT_EQ(1U, table.size()); |
| 216 EXPECT_EQ(100U, table.num_allocs()); |
| 217 |
| 218 for (int i = 0; i < 100; ++i) |
| 219 table.Add(kStack2); |
| 220 EXPECT_EQ(2U, table.size()); |
| 221 EXPECT_EQ(200U, table.num_allocs()); |
| 222 |
| 223 for (int i = 0; i < 100; ++i) |
| 224 table.Add(kStack1); |
| 225 EXPECT_EQ(3U, table.size()); |
| 226 EXPECT_EQ(300U, table.num_allocs()); |
| 227 |
| 228 for (int i = 0; i < 100; ++i) |
| 229 table.Add(kStack0); |
| 230 EXPECT_EQ(4U, table.size()); |
| 231 EXPECT_EQ(400U, table.num_allocs()); |
| 232 |
| 233 // Remove them in a different order, by removing one of each stack during one |
| 234 // iteration. The size should not decrease until the last iteration. |
| 235 EXPECT_EQ(0U, table.num_frees()); |
| 236 |
| 237 for (int i = 0; i < 100; ++i) { |
| 238 table.Remove(kStack0); |
| 239 EXPECT_EQ(4U * i + 1, table.num_frees()); |
| 240 |
| 241 table.Remove(kStack1); |
| 242 EXPECT_EQ(4U * i + 2, table.num_frees()); |
| 243 |
| 244 table.Remove(kStack2); |
| 245 EXPECT_EQ(4U * i + 3, table.num_frees()); |
| 246 |
| 247 table.Remove(kStack3); |
| 248 EXPECT_EQ(4U * i + 4, table.num_frees()); |
| 249 } |
| 250 EXPECT_EQ(400U, table.num_frees()); |
| 251 EXPECT_TRUE(table.empty()); |
| 252 |
| 253 // Try to remove some more from an empty table and make sure nothing breaks. |
| 254 table.Remove(kStack0); |
| 255 table.Remove(kStack1); |
| 256 table.Remove(kStack2); |
| 257 table.Remove(kStack3); |
| 258 |
| 259 EXPECT_TRUE(table.empty()); |
| 260 EXPECT_EQ(400U, table.num_allocs()); |
| 261 EXPECT_EQ(400U, table.num_frees()); |
| 262 } |
| 263 |
| 264 TEST_F(CallStackTableTest, DetectLeak) { |
| 265 CallStackTable table(kDefaultLeakThreshold); |
| 266 |
| 267 // Add some base number of entries. |
| 268 for (int i = 0; i < 60; ++i) |
| 269 table.Add(kStack0); |
| 270 for (int i = 0; i < 50; ++i) |
| 271 table.Add(kStack1); |
| 272 for (int i = 0; i < 64; ++i) |
| 273 table.Add(kStack2); |
| 274 for (int i = 0; i < 72; ++i) |
| 275 table.Add(kStack3); |
| 276 |
| 277 table.TestForLeaks(); |
| 278 EXPECT_TRUE(table.leak_analyzer().suspected_leaks().empty()); |
| 279 |
| 280 // Use the following scheme: |
| 281 // - kStack0: increase by 4 each time -- leak suspect |
| 282 // - kStack1: increase by 3 each time -- leak suspect |
| 283 // - kStack2: increase by 1 each time -- not a suspect |
| 284 // - kStack3: alternate between increasing and decreasing - not a suspect |
| 285 bool increase_kstack3 = true; |
| 286 for (int i = 0; i < kDefaultLeakThreshold; ++i) { |
| 287 EXPECT_TRUE(table.leak_analyzer().suspected_leaks().empty()); |
| 288 |
| 289 for (int j = 0; j < 4; ++j) |
| 290 table.Add(kStack0); |
| 291 |
| 292 for (int j = 0; j < 3; ++j) |
| 293 table.Add(kStack1); |
| 294 |
| 295 table.Add(kStack2); |
| 296 |
| 297 // Alternate between adding and removing. |
| 298 if (increase_kstack3) |
| 299 table.Add(kStack3); |
| 300 else |
| 301 table.Remove(kStack3); |
| 302 increase_kstack3 = !increase_kstack3; |
| 303 |
| 304 table.TestForLeaks(); |
| 305 } |
| 306 |
| 307 // Check that the correct leak values have been detected. |
| 308 const auto& leaks = table.leak_analyzer().suspected_leaks(); |
| 309 ASSERT_EQ(2U, leaks.size()); |
| 310 // Suspected leaks are reported in increasing leak value -- in this case, the |
| 311 // CallStack object's address. |
| 312 EXPECT_EQ(kStack0, leaks[0].call_stack()); |
| 313 EXPECT_EQ(kStack1, leaks[1].call_stack()); |
| 314 } |
| 315 |
| 316 } // namespace leak_detector |
OLD | NEW |