| 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 |