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.
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"
19 class StatsTableTest
: public MultiProcessTest
{
21 void DeleteShmem(const std::string
& name
) {
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
);
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);
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);
50 // Try to allocate an additional thread. Verify it fails.
51 slot_id
= table
.RegisterThread("too many threads");
52 EXPECT_EQ(slot_id
, 0);
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);
58 DeleteShmem(kTableName
);
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;
75 class StatsTableThread
: public SimpleThread
{
77 StatsTableThread(std::string name
, int id
)
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.
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
97 lucky13_counter
.Set(1313);
98 increment_counter
.Increment();
99 decrement_counter
.Decrement();
101 mixed_counter
.Decrement();
103 mixed_counter
.Increment();
104 PlatformThread::Sleep(index
% 10); // short wait
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
);
119 EXPECT_EQ(0, table
.CountThreadsRegistered());
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
];
126 // Spawn the threads.
127 for (int index
= 0; index
< kMaxThreads
; index
++) {
128 threads
[index
] = new StatsTableThread("MultipleThreadsTest", index
);
129 threads
[index
]->Start();
132 // Wait for the threads to finish.
133 for (int index
= 0; index
< kMaxThreads
; index
++) {
134 threads
[index
]->Join();
135 delete threads
[index
];
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
);
144 // Verify the various counters are correct.
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());
162 DeleteShmem(kTableName
);
165 const std::string kMPTableName
= "MultipleProcessStatTable";
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.
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
++) {
180 lucky13_counter
.Set(1313);
181 increment_counter
.Increment();
182 decrement_counter
.Decrement();
183 PlatformThread::Sleep(index
% 10); // short wait
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());
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
];
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
]);
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
]);
216 StatsCounter
zero_counter(kCounterZero
);
217 StatsCounter
lucky13_counter(kCounter1313
);
218 StatsCounter
increment_counter(kCounterIncrement
);
219 StatsCounter
decrement_counter(kCounterDecrement
);
221 // Verify the various counters are correct.
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());
236 DeleteShmem(kMPTableName
);
239 class MockStatsCounter
: public StatsCounter
{
241 explicit MockStatsCounter(const std::string
& name
)
242 : StatsCounter(name
) {}
243 int* Pointer() { return GetPtr(); }
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
);
256 MockStatsCounter
foo("foo");
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"));
265 while (*(foo
.Pointer()) < 123) foo
.Increment();
266 EXPECT_EQ(123, table
.GetCounterValue("c:foo"));
268 EXPECT_EQ(123, table
.GetCounterValue("c:foo"));
270 EXPECT_EQ(122, table
.GetCounterValue("c:foo"));
274 EXPECT_EQ(0, table
.GetCounterValue("c:foo"));
276 EXPECT_EQ(100, table
.GetCounterValue("c:foo"));
278 EXPECT_EQ(-1, table
.GetCounterValue("c:foo"));
280 EXPECT_EQ(0, table
.GetCounterValue("c:foo"));
284 EXPECT_EQ(-1, table
.GetCounterValue("c:foo"));
286 EXPECT_EQ(-1, table
.GetCounterValue("c:foo"));
288 EXPECT_EQ(0, table
.GetCounterValue("c:foo"));
290 DeleteShmem(kTableName
);
293 class MockStatsCounterTimer
: public StatsCounterTimer
{
295 explicit MockStatsCounterTimer(const std::string
& name
)
296 : StatsCounterTimer(name
) {}
298 TimeTicks
start_time() { return start_time_
; }
299 TimeTicks
stop_time() { return stop_time_
; }
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
);
311 MockStatsCounterTimer
bar("bar");
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());
318 const int kRunMs
= 100;
322 PlatformThread::Sleep(kRunMs
);
324 EXPECT_GT(table
.GetCounterValue("t:bar"), 0);
325 EXPECT_LE(kRunMs
, table
.GetCounterValue("t:bar"));
327 // Verify that timing again is additive.
329 PlatformThread::Sleep(kRunMs
);
331 EXPECT_GT(table
.GetCounterValue("t:bar"), 0);
332 EXPECT_LE(kRunMs
* 2, table
.GetCounterValue("t:bar"));
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
);
344 StatsRate
baz("baz");
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"));
351 const int kRunMs
= 100;
355 PlatformThread::Sleep(kRunMs
);
357 EXPECT_EQ(1, table
.GetCounterValue("c:baz"));
358 EXPECT_LE(kRunMs
, table
.GetCounterValue("t:baz"));
360 // Verify that timing again is additive.
362 PlatformThread::Sleep(kRunMs
);
364 EXPECT_EQ(2, table
.GetCounterValue("c:baz"));
365 EXPECT_LE(kRunMs
* 2, table
.GetCounterValue("t:baz"));
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
);
378 StatsCounterTimer
foo("foo");
379 StatsRate
bar("bar");
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"));
386 const int kRunMs
= 100;
390 StatsScope
<StatsCounterTimer
> timer(foo
);
391 StatsScope
<StatsRate
> timer2(bar
);
392 PlatformThread::Sleep(kRunMs
);
394 EXPECT_LE(kRunMs
, table
.GetCounterValue("t:foo"));
395 EXPECT_LE(kRunMs
, table
.GetCounterValue("t:bar"));
396 EXPECT_EQ(1, table
.GetCounterValue("c:bar"));
398 // Try a second scope.
400 StatsScope
<StatsCounterTimer
> timer(foo
);
401 StatsScope
<StatsRate
> timer2(bar
);
402 PlatformThread::Sleep(kRunMs
);
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"));
408 DeleteShmem(kTableName
);