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