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