stats_table_unittest.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2012 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 "base/metrics/stats_counters.h" 6#include "base/metrics/stats_table.h" 7#include "base/shared_memory.h" 8#include "base/strings/string_piece.h" 9#include "base/strings/stringprintf.h" 10#include "base/strings/utf_string_conversions.h" 11#include "base/test/multiprocess_test.h" 12#include "base/threading/platform_thread.h" 13#include "base/threading/simple_thread.h" 14#include "testing/gtest/include/gtest/gtest.h" 15#include "testing/multiprocess_func_list.h" 16 17namespace base { 18 19class StatsTableTest : public MultiProcessTest { 20 public: 21 void DeleteShmem(const std::string& name) { 22 SharedMemory mem; 23 mem.Delete(name); 24 } 25}; 26 27// Open a StatsTable and verify that we can write to each of the 28// locations in the table. 29TEST_F(StatsTableTest, VerifySlots) { 30 const std::string kTableName = "VerifySlotsStatTable"; 31 const int kMaxThreads = 1; 32 const int kMaxCounter = 5; 33 DeleteShmem(kTableName); 34 StatsTable table(kTableName, kMaxThreads, kMaxCounter); 35 36 // Register a single thread. 37 std::string thread_name = "mainThread"; 38 int slot_id = table.RegisterThread(thread_name); 39 EXPECT_NE(slot_id, 0); 40 41 // Fill up the table with counters. 42 std::string counter_base_name = "counter"; 43 for (int index = 0; index < kMaxCounter; index++) { 44 std::string counter_name = counter_base_name; 45 base::StringAppendF(&counter_name, "counter.ctr%d", index); 46 int counter_id = table.FindCounter(counter_name); 47 EXPECT_GT(counter_id, 0); 48 } 49 50 // Try to allocate an additional thread. Verify it fails. 51 slot_id = table.RegisterThread("too many threads"); 52 EXPECT_EQ(slot_id, 0); 53 54 // Try to allocate an additional counter. Verify it fails. 55 int counter_id = table.FindCounter(counter_base_name); 56 EXPECT_EQ(counter_id, 0); 57 58 DeleteShmem(kTableName); 59} 60 61// CounterZero will continually be set to 0. 62const std::string kCounterZero = "CounterZero"; 63// Counter1313 will continually be set to 1313. 64const std::string kCounter1313 = "Counter1313"; 65// CounterIncrement will be incremented each time. 66const std::string kCounterIncrement = "CounterIncrement"; 67// CounterDecrement will be decremented each time. 68const std::string kCounterDecrement = "CounterDecrement"; 69// CounterMixed will be incremented by odd numbered threads and 70// decremented by even threads. 71const std::string kCounterMixed = "CounterMixed"; 72// The number of thread loops that we will do. 73const int kThreadLoops = 100; 74 75class StatsTableThread : public SimpleThread { 76 public: 77 StatsTableThread(std::string name, int id) 78 : SimpleThread(name), 79 id_(id) {} 80 81 virtual void Run() OVERRIDE; 82 83 private: 84 int id_; 85}; 86 87void StatsTableThread::Run() { 88 // Each thread will open the shared memory and set counters 89 // concurrently in a loop. We'll use some pauses to 90 // mixup the thread scheduling. 91 92 StatsCounter zero_counter(kCounterZero); 93 StatsCounter lucky13_counter(kCounter1313); 94 StatsCounter increment_counter(kCounterIncrement); 95 StatsCounter decrement_counter(kCounterDecrement); 96 for (int index = 0; index < kThreadLoops; index++) { 97 StatsCounter mixed_counter(kCounterMixed); // create this one in the loop 98 zero_counter.Set(0); 99 lucky13_counter.Set(1313); 100 increment_counter.Increment(); 101 decrement_counter.Decrement(); 102 if (id_ % 2) 103 mixed_counter.Decrement(); 104 else 105 mixed_counter.Increment(); 106 PlatformThread::Sleep(TimeDelta::FromMilliseconds(index % 10)); 107 } 108} 109 110// Create a few threads and have them poke on their counters. 111// See http://crbug.com/10611 for more information. 112#if defined(OS_MACOSX) || defined(THREAD_SANITIZER) 113#define MAYBE_MultipleThreads DISABLED_MultipleThreads 114#else 115#define MAYBE_MultipleThreads MultipleThreads 116#endif 117TEST_F(StatsTableTest, MAYBE_MultipleThreads) { 118 // Create a stats table. 119 const std::string kTableName = "MultipleThreadStatTable"; 120 const int kMaxThreads = 20; 121 const int kMaxCounter = 5; 122 DeleteShmem(kTableName); 123 StatsTable table(kTableName, kMaxThreads, kMaxCounter); 124 StatsTable::set_current(&table); 125 126 EXPECT_EQ(0, table.CountThreadsRegistered()); 127 128 // Spin up a set of threads to go bang on the various counters. 129 // After we join the threads, we'll make sure the counters 130 // contain the values we expected. 131 StatsTableThread* threads[kMaxThreads]; 132 133 // Spawn the threads. 134 for (int index = 0; index < kMaxThreads; index++) { 135 threads[index] = new StatsTableThread("MultipleThreadsTest", index); 136 threads[index]->Start(); 137 } 138 139 // Wait for the threads to finish. 140 for (int index = 0; index < kMaxThreads; index++) { 141 threads[index]->Join(); 142 delete threads[index]; 143 } 144 145 StatsCounter zero_counter(kCounterZero); 146 StatsCounter lucky13_counter(kCounter1313); 147 StatsCounter increment_counter(kCounterIncrement); 148 StatsCounter decrement_counter(kCounterDecrement); 149 StatsCounter mixed_counter(kCounterMixed); 150 151 // Verify the various counters are correct. 152 std::string name; 153 name = "c:" + kCounterZero; 154 EXPECT_EQ(0, table.GetCounterValue(name)); 155 name = "c:" + kCounter1313; 156 EXPECT_EQ(1313 * kMaxThreads, 157 table.GetCounterValue(name)); 158 name = "c:" + kCounterIncrement; 159 EXPECT_EQ(kMaxThreads * kThreadLoops, 160 table.GetCounterValue(name)); 161 name = "c:" + kCounterDecrement; 162 EXPECT_EQ(-kMaxThreads * kThreadLoops, 163 table.GetCounterValue(name)); 164 name = "c:" + kCounterMixed; 165 EXPECT_EQ((kMaxThreads % 2) * kThreadLoops, 166 table.GetCounterValue(name)); 167 EXPECT_EQ(0, table.CountThreadsRegistered()); 168 169 DeleteShmem(kTableName); 170} 171 172const std::string kMPTableName = "MultipleProcessStatTable"; 173 174MULTIPROCESS_TEST_MAIN(StatsTableMultipleProcessMain) { 175 // Each process will open the shared memory and set counters 176 // concurrently in a loop. We'll use some pauses to 177 // mixup the scheduling. 178 179 StatsTable table(kMPTableName, 0, 0); 180 StatsTable::set_current(&table); 181 StatsCounter zero_counter(kCounterZero); 182 StatsCounter lucky13_counter(kCounter1313); 183 StatsCounter increment_counter(kCounterIncrement); 184 StatsCounter decrement_counter(kCounterDecrement); 185 for (int index = 0; index < kThreadLoops; index++) { 186 zero_counter.Set(0); 187 lucky13_counter.Set(1313); 188 increment_counter.Increment(); 189 decrement_counter.Decrement(); 190 PlatformThread::Sleep(TimeDelta::FromMilliseconds(index % 10)); 191 } 192 return 0; 193} 194 195// Create a few processes and have them poke on their counters. 196// This test is slow and flaky http://crbug.com/10611 197TEST_F(StatsTableTest, DISABLED_MultipleProcesses) { 198 // Create a stats table. 199 const int kMaxProcs = 20; 200 const int kMaxCounter = 5; 201 DeleteShmem(kMPTableName); 202 StatsTable table(kMPTableName, kMaxProcs, kMaxCounter); 203 StatsTable::set_current(&table); 204 EXPECT_EQ(0, table.CountThreadsRegistered()); 205 206 // Spin up a set of processes to go bang on the various counters. 207 // After we join the processes, we'll make sure the counters 208 // contain the values we expected. 209 ProcessHandle procs[kMaxProcs]; 210 211 // Spawn the processes. 212 for (int16 index = 0; index < kMaxProcs; index++) { 213 procs[index] = this->SpawnChild("StatsTableMultipleProcessMain", false); 214 EXPECT_NE(kNullProcessHandle, procs[index]); 215 } 216 217 // Wait for the processes to finish. 218 for (int index = 0; index < kMaxProcs; index++) { 219 EXPECT_TRUE(WaitForSingleProcess( 220 procs[index], base::TimeDelta::FromMinutes(1))); 221 CloseProcessHandle(procs[index]); 222 } 223 224 StatsCounter zero_counter(kCounterZero); 225 StatsCounter lucky13_counter(kCounter1313); 226 StatsCounter increment_counter(kCounterIncrement); 227 StatsCounter decrement_counter(kCounterDecrement); 228 229 // Verify the various counters are correct. 230 std::string name; 231 name = "c:" + kCounterZero; 232 EXPECT_EQ(0, table.GetCounterValue(name)); 233 name = "c:" + kCounter1313; 234 EXPECT_EQ(1313 * kMaxProcs, 235 table.GetCounterValue(name)); 236 name = "c:" + kCounterIncrement; 237 EXPECT_EQ(kMaxProcs * kThreadLoops, 238 table.GetCounterValue(name)); 239 name = "c:" + kCounterDecrement; 240 EXPECT_EQ(-kMaxProcs * kThreadLoops, 241 table.GetCounterValue(name)); 242 EXPECT_EQ(0, table.CountThreadsRegistered()); 243 244 DeleteShmem(kMPTableName); 245} 246 247class MockStatsCounter : public StatsCounter { 248 public: 249 explicit MockStatsCounter(const std::string& name) 250 : StatsCounter(name) {} 251 int* Pointer() { return GetPtr(); } 252}; 253 254// Test some basic StatsCounter operations 255TEST_F(StatsTableTest, StatsCounter) { 256 // Create a stats table. 257 const std::string kTableName = "StatTable"; 258 const int kMaxThreads = 20; 259 const int kMaxCounter = 5; 260 DeleteShmem(kTableName); 261 StatsTable table(kTableName, kMaxThreads, kMaxCounter); 262 StatsTable::set_current(&table); 263 264 MockStatsCounter foo("foo"); 265 266 // Test initial state. 267 EXPECT_TRUE(foo.Enabled()); 268 ASSERT_NE(foo.Pointer(), static_cast<int*>(0)); 269 EXPECT_EQ(0, *(foo.Pointer())); 270 EXPECT_EQ(0, table.GetCounterValue("c:foo")); 271 272 // Test Increment. 273 while (*(foo.Pointer()) < 123) foo.Increment(); 274 EXPECT_EQ(123, table.GetCounterValue("c:foo")); 275 foo.Add(0); 276 EXPECT_EQ(123, table.GetCounterValue("c:foo")); 277 foo.Add(-1); 278 EXPECT_EQ(122, table.GetCounterValue("c:foo")); 279 280 // Test Set. 281 foo.Set(0); 282 EXPECT_EQ(0, table.GetCounterValue("c:foo")); 283 foo.Set(100); 284 EXPECT_EQ(100, table.GetCounterValue("c:foo")); 285 foo.Set(-1); 286 EXPECT_EQ(-1, table.GetCounterValue("c:foo")); 287 foo.Set(0); 288 EXPECT_EQ(0, table.GetCounterValue("c:foo")); 289 290 // Test Decrement. 291 foo.Subtract(1); 292 EXPECT_EQ(-1, table.GetCounterValue("c:foo")); 293 foo.Subtract(0); 294 EXPECT_EQ(-1, table.GetCounterValue("c:foo")); 295 foo.Subtract(-1); 296 EXPECT_EQ(0, table.GetCounterValue("c:foo")); 297 298 DeleteShmem(kTableName); 299} 300 301class MockStatsCounterTimer : public StatsCounterTimer { 302 public: 303 explicit MockStatsCounterTimer(const std::string& name) 304 : StatsCounterTimer(name) {} 305 306 TimeTicks start_time() { return start_time_; } 307 TimeTicks stop_time() { return stop_time_; } 308}; 309 310// Test some basic StatsCounterTimer operations 311TEST_F(StatsTableTest, StatsCounterTimer) { 312 // Create a stats table. 313 const std::string kTableName = "StatTable"; 314 const int kMaxThreads = 20; 315 const int kMaxCounter = 5; 316 DeleteShmem(kTableName); 317 StatsTable table(kTableName, kMaxThreads, kMaxCounter); 318 StatsTable::set_current(&table); 319 320 MockStatsCounterTimer bar("bar"); 321 322 // Test initial state. 323 EXPECT_FALSE(bar.Running()); 324 EXPECT_TRUE(bar.start_time().is_null()); 325 EXPECT_TRUE(bar.stop_time().is_null()); 326 327 const TimeDelta kDuration = TimeDelta::FromMilliseconds(100); 328 329 // Do some timing. 330 bar.Start(); 331 PlatformThread::Sleep(kDuration); 332 bar.Stop(); 333 EXPECT_GT(table.GetCounterValue("t:bar"), 0); 334 EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:bar")); 335 336 // Verify that timing again is additive. 337 bar.Start(); 338 PlatformThread::Sleep(kDuration); 339 bar.Stop(); 340 EXPECT_GT(table.GetCounterValue("t:bar"), 0); 341 EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:bar")); 342 DeleteShmem(kTableName); 343} 344 345// Test some basic StatsRate operations 346TEST_F(StatsTableTest, StatsRate) { 347 // Create a stats table. 348 const std::string kTableName = "StatTable"; 349 const int kMaxThreads = 20; 350 const int kMaxCounter = 5; 351 DeleteShmem(kTableName); 352 StatsTable table(kTableName, kMaxThreads, kMaxCounter); 353 StatsTable::set_current(&table); 354 355 StatsRate baz("baz"); 356 357 // Test initial state. 358 EXPECT_FALSE(baz.Running()); 359 EXPECT_EQ(0, table.GetCounterValue("c:baz")); 360 EXPECT_EQ(0, table.GetCounterValue("t:baz")); 361 362 const TimeDelta kDuration = TimeDelta::FromMilliseconds(100); 363 364 // Do some timing. 365 baz.Start(); 366 PlatformThread::Sleep(kDuration); 367 baz.Stop(); 368 EXPECT_EQ(1, table.GetCounterValue("c:baz")); 369 EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:baz")); 370 371 // Verify that timing again is additive. 372 baz.Start(); 373 PlatformThread::Sleep(kDuration); 374 baz.Stop(); 375 EXPECT_EQ(2, table.GetCounterValue("c:baz")); 376 EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:baz")); 377 DeleteShmem(kTableName); 378} 379 380// Test some basic StatsScope operations 381TEST_F(StatsTableTest, StatsScope) { 382 // Create a stats table. 383 const std::string kTableName = "StatTable"; 384 const int kMaxThreads = 20; 385 const int kMaxCounter = 5; 386 DeleteShmem(kTableName); 387 StatsTable table(kTableName, kMaxThreads, kMaxCounter); 388 StatsTable::set_current(&table); 389 390 StatsCounterTimer foo("foo"); 391 StatsRate bar("bar"); 392 393 // Test initial state. 394 EXPECT_EQ(0, table.GetCounterValue("t:foo")); 395 EXPECT_EQ(0, table.GetCounterValue("t:bar")); 396 EXPECT_EQ(0, table.GetCounterValue("c:bar")); 397 398 const TimeDelta kDuration = TimeDelta::FromMilliseconds(100); 399 400 // Try a scope. 401 { 402 StatsScope<StatsCounterTimer> timer(foo); 403 StatsScope<StatsRate> timer2(bar); 404 PlatformThread::Sleep(kDuration); 405 } 406 EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:foo")); 407 EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:bar")); 408 EXPECT_EQ(1, table.GetCounterValue("c:bar")); 409 410 // Try a second scope. 411 { 412 StatsScope<StatsCounterTimer> timer(foo); 413 StatsScope<StatsRate> timer2(bar); 414 PlatformThread::Sleep(kDuration); 415 } 416 EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:foo")); 417 EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:bar")); 418 EXPECT_EQ(2, table.GetCounterValue("c:bar")); 419 420 DeleteShmem(kTableName); 421} 422 423} // namespace base 424