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