• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
DeleteShmem(const std::string & name)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.
TEST_F(StatsTableTest,VerifySlots)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:
StatsTableThread(std::string name,int id)77   StatsTableThread(std::string name, int id)
78       : SimpleThread(name),
79       id_(id) {}
80   virtual void Run();
81  private:
82   int id_;
83 };
84 
Run()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.
TEST_F(StatsTableTest,FLAKY_MultipleThreads)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 
MULTIPROCESS_TEST_MAIN(StatsTableMultipleProcessMain)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
TEST_F(StatsTableTest,FLAKY_MultipleProcesses)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:
MockStatsCounter(const std::string & name)241   explicit MockStatsCounter(const std::string& name)
242       : StatsCounter(name) {}
Pointer()243   int* Pointer() { return GetPtr(); }
244 };
245 
246 // Test some basic StatsCounter operations
TEST_F(StatsTableTest,StatsCounter)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:
MockStatsCounterTimer(const std::string & name)295   explicit MockStatsCounterTimer(const std::string& name)
296       : StatsCounterTimer(name) {}
297 
start_time()298   TimeTicks start_time() { return start_time_; }
stop_time()299   TimeTicks stop_time() { return stop_time_; }
300 };
301 
302 // Test some basic StatsCounterTimer operations
TEST_F(StatsTableTest,StatsCounterTimer)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
TEST_F(StatsTableTest,StatsRate)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
TEST_F(StatsTableTest,StatsScope)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