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