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