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.
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"
20 class StatsTableTest
: public MultiProcessTest
{
23 // Open a StatsTable and verify that we can write to each of the
24 // locations in the table.
25 TEST_F(StatsTableTest
, VerifySlots
) {
26 const int kMaxThreads
= 1;
27 const int kMaxCounter
= 5;
28 StatsTable
table(StatsTable::TableIdentifier(), kMaxThreads
, kMaxCounter
);
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);
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);
44 // Try to allocate an additional thread. Verify it fails.
45 slot_id
= table
.RegisterThread("too many threads");
46 EXPECT_EQ(slot_id
, 0);
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);
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;
67 class StatsTableThread
: public SimpleThread
{
69 StatsTableThread(std::string name
, int id
)
73 virtual void Run() OVERRIDE
;
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.
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
91 lucky13_counter
.Set(1313);
92 increment_counter
.Increment();
93 decrement_counter
.Decrement();
95 mixed_counter
.Decrement();
97 mixed_counter
.Increment();
98 PlatformThread::Sleep(TimeDelta::FromMilliseconds(index
% 10));
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
107 #define MAYBE_MultipleThreads 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
);
116 EXPECT_EQ(0, table
.CountThreadsRegistered());
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
];
123 // Spawn the threads.
124 for (int index
= 0; index
< kMaxThreads
; index
++) {
125 threads
[index
] = new StatsTableThread("MultipleThreadsTest", index
);
126 threads
[index
]->Start();
129 // Wait for the threads to finish.
130 for (int index
= 0; index
< kMaxThreads
; index
++) {
131 threads
[index
]->Join();
132 delete threads
[index
];
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
);
141 // Verify the various counters are correct.
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());
160 // This multiprocess test only runs on Windows. On Posix, the shared memory
161 // handle is not sent between the processes properly.
163 const std::string kMPTableName
= "MultipleProcessStatTable";
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.
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
++) {
178 lucky13_counter
.Set(1313);
179 increment_counter
.Increment();
180 decrement_counter
.Decrement();
181 PlatformThread::Sleep(TimeDelta::FromMilliseconds(index
% 10));
186 // Create a few processes and have them poke on their counters.
187 // This test is slow and flaky http://crbug.com/10611
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());
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
];
201 // Spawn the processes.
202 for (int16 index
= 0; index
< kMaxProcs
; index
++) {
203 procs
[index
] = SpawnChild("StatsTableMultipleProcessMain");
204 EXPECT_NE(kNullProcessHandle
, procs
[index
]);
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
]);
214 StatsCounter
zero_counter(kCounterZero
);
215 StatsCounter
lucky13_counter(kCounter1313
);
216 StatsCounter
increment_counter(kCounterIncrement
);
217 StatsCounter
decrement_counter(kCounterDecrement
);
219 // Verify the various counters are correct.
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());
236 class MockStatsCounter
: public StatsCounter
{
238 explicit MockStatsCounter(const std::string
& name
)
239 : StatsCounter(name
) {}
240 int* Pointer() { return GetPtr(); }
243 // Test some basic StatsCounter operations
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
);
251 MockStatsCounter
foo("foo");
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"));
260 while (*(foo
.Pointer()) < 123) foo
.Increment();
261 EXPECT_EQ(123, table
.GetCounterValue("c:foo"));
263 EXPECT_EQ(123, table
.GetCounterValue("c:foo"));
265 EXPECT_EQ(122, table
.GetCounterValue("c:foo"));
269 EXPECT_EQ(0, table
.GetCounterValue("c:foo"));
271 EXPECT_EQ(100, table
.GetCounterValue("c:foo"));
273 EXPECT_EQ(-1, table
.GetCounterValue("c:foo"));
275 EXPECT_EQ(0, table
.GetCounterValue("c:foo"));
279 EXPECT_EQ(-1, table
.GetCounterValue("c:foo"));
281 EXPECT_EQ(-1, table
.GetCounterValue("c:foo"));
283 EXPECT_EQ(0, table
.GetCounterValue("c:foo"));
286 class MockStatsCounterTimer
: public StatsCounterTimer
{
288 explicit MockStatsCounterTimer(const std::string
& name
)
289 : StatsCounterTimer(name
) {}
291 TimeTicks
start_time() { return start_time_
; }
292 TimeTicks
stop_time() { return stop_time_
; }
295 // Test some basic StatsCounterTimer operations
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
);
303 MockStatsCounterTimer
bar("bar");
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());
310 const TimeDelta kDuration
= TimeDelta::FromMilliseconds(100);
314 PlatformThread::Sleep(kDuration
);
316 EXPECT_GT(table
.GetCounterValue("t:bar"), 0);
317 EXPECT_LE(kDuration
.InMilliseconds(), table
.GetCounterValue("t:bar"));
319 // Verify that timing again is additive.
321 PlatformThread::Sleep(kDuration
);
323 EXPECT_GT(table
.GetCounterValue("t:bar"), 0);
324 EXPECT_LE(kDuration
.InMilliseconds() * 2, table
.GetCounterValue("t:bar"));
327 // Test some basic StatsRate operations
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
);
335 StatsRate
baz("baz");
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"));
342 const TimeDelta kDuration
= TimeDelta::FromMilliseconds(100);
346 PlatformThread::Sleep(kDuration
);
348 EXPECT_EQ(1, table
.GetCounterValue("c:baz"));
349 EXPECT_LE(kDuration
.InMilliseconds(), table
.GetCounterValue("t:baz"));
351 // Verify that timing again is additive.
353 PlatformThread::Sleep(kDuration
);
355 EXPECT_EQ(2, table
.GetCounterValue("c:baz"));
356 EXPECT_LE(kDuration
.InMilliseconds() * 2, table
.GetCounterValue("t:baz"));
359 // Test some basic StatsScope operations
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
);
367 StatsCounterTimer
foo("foo");
368 StatsRate
bar("bar");
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"));
375 const TimeDelta kDuration
= TimeDelta::FromMilliseconds(100);
379 StatsScope
<StatsCounterTimer
> timer(foo
);
380 StatsScope
<StatsRate
> timer2(bar
);
381 PlatformThread::Sleep(kDuration
);
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"));
387 // Try a second scope.
389 StatsScope
<StatsCounterTimer
> timer(foo
);
390 StatsScope
<StatsRate
> timer2(bar
);
391 PlatformThread::Sleep(kDuration
);
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"));