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
{
22 void DeleteShmem(const std::string
& name
) {
28 // Open a StatsTable and verify that we can write to each of the
29 // locations in the table.
30 TEST_F(StatsTableTest
, VerifySlots
) {
31 const std::string kTableName
= "VerifySlotsStatTable";
32 const int kMaxThreads
= 1;
33 const int kMaxCounter
= 5;
34 DeleteShmem(kTableName
);
35 StatsTable
table(kTableName
, kMaxThreads
, kMaxCounter
);
37 // Register a single thread.
38 std::string thread_name
= "mainThread";
39 int slot_id
= table
.RegisterThread(thread_name
);
40 EXPECT_NE(slot_id
, 0);
42 // Fill up the table with counters.
43 std::string counter_base_name
= "counter";
44 for (int index
= 0; index
< kMaxCounter
; index
++) {
45 std::string counter_name
= counter_base_name
;
46 base::StringAppendF(&counter_name
, "counter.ctr%d", index
);
47 int counter_id
= table
.FindCounter(counter_name
);
48 EXPECT_GT(counter_id
, 0);
51 // Try to allocate an additional thread. Verify it fails.
52 slot_id
= table
.RegisterThread("too many threads");
53 EXPECT_EQ(slot_id
, 0);
55 // Try to allocate an additional counter. Verify it fails.
56 int counter_id
= table
.FindCounter(counter_base_name
);
57 EXPECT_EQ(counter_id
, 0);
59 DeleteShmem(kTableName
);
62 // CounterZero will continually be set to 0.
63 const std::string kCounterZero
= "CounterZero";
64 // Counter1313 will continually be set to 1313.
65 const std::string kCounter1313
= "Counter1313";
66 // CounterIncrement will be incremented each time.
67 const std::string kCounterIncrement
= "CounterIncrement";
68 // CounterDecrement will be decremented each time.
69 const std::string kCounterDecrement
= "CounterDecrement";
70 // CounterMixed will be incremented by odd numbered threads and
71 // decremented by even threads.
72 const std::string kCounterMixed
= "CounterMixed";
73 // The number of thread loops that we will do.
74 const int kThreadLoops
= 100;
76 class StatsTableThread
: public SimpleThread
{
78 StatsTableThread(std::string name
, int id
)
82 virtual void Run() OVERRIDE
;
88 void StatsTableThread::Run() {
89 // Each thread will open the shared memory and set counters
90 // concurrently in a loop. We'll use some pauses to
91 // mixup the thread scheduling.
93 StatsCounter
zero_counter(kCounterZero
);
94 StatsCounter
lucky13_counter(kCounter1313
);
95 StatsCounter
increment_counter(kCounterIncrement
);
96 StatsCounter
decrement_counter(kCounterDecrement
);
97 for (int index
= 0; index
< kThreadLoops
; index
++) {
98 StatsCounter
mixed_counter(kCounterMixed
); // create this one in the loop
100 lucky13_counter
.Set(1313);
101 increment_counter
.Increment();
102 decrement_counter
.Decrement();
104 mixed_counter
.Decrement();
106 mixed_counter
.Increment();
107 PlatformThread::Sleep(TimeDelta::FromMilliseconds(index
% 10));
111 // Create a few threads and have them poke on their counters.
112 // See http://crbug.com/10611 for more information.
113 #if defined(OS_MACOSX) || defined(THREAD_SANITIZER)
114 #define MAYBE_MultipleThreads DISABLED_MultipleThreads
116 #define MAYBE_MultipleThreads MultipleThreads
118 TEST_F(StatsTableTest
, MAYBE_MultipleThreads
) {
119 // Create a stats table.
120 const std::string kTableName
= "MultipleThreadStatTable";
121 const int kMaxThreads
= 20;
122 const int kMaxCounter
= 5;
123 DeleteShmem(kTableName
);
124 StatsTable
table(kTableName
, kMaxThreads
, kMaxCounter
);
125 StatsTable::set_current(&table
);
127 EXPECT_EQ(0, table
.CountThreadsRegistered());
129 // Spin up a set of threads to go bang on the various counters.
130 // After we join the threads, we'll make sure the counters
131 // contain the values we expected.
132 StatsTableThread
* threads
[kMaxThreads
];
134 // Spawn the threads.
135 for (int index
= 0; index
< kMaxThreads
; index
++) {
136 threads
[index
] = new StatsTableThread("MultipleThreadsTest", index
);
137 threads
[index
]->Start();
140 // Wait for the threads to finish.
141 for (int index
= 0; index
< kMaxThreads
; index
++) {
142 threads
[index
]->Join();
143 delete threads
[index
];
146 StatsCounter
zero_counter(kCounterZero
);
147 StatsCounter
lucky13_counter(kCounter1313
);
148 StatsCounter
increment_counter(kCounterIncrement
);
149 StatsCounter
decrement_counter(kCounterDecrement
);
150 StatsCounter
mixed_counter(kCounterMixed
);
152 // Verify the various counters are correct.
154 name
= "c:" + kCounterZero
;
155 EXPECT_EQ(0, table
.GetCounterValue(name
));
156 name
= "c:" + kCounter1313
;
157 EXPECT_EQ(1313 * kMaxThreads
,
158 table
.GetCounterValue(name
));
159 name
= "c:" + kCounterIncrement
;
160 EXPECT_EQ(kMaxThreads
* kThreadLoops
,
161 table
.GetCounterValue(name
));
162 name
= "c:" + kCounterDecrement
;
163 EXPECT_EQ(-kMaxThreads
* kThreadLoops
,
164 table
.GetCounterValue(name
));
165 name
= "c:" + kCounterMixed
;
166 EXPECT_EQ((kMaxThreads
% 2) * kThreadLoops
,
167 table
.GetCounterValue(name
));
168 EXPECT_EQ(0, table
.CountThreadsRegistered());
170 DeleteShmem(kTableName
);
173 const std::string kMPTableName
= "MultipleProcessStatTable";
175 MULTIPROCESS_TEST_MAIN(StatsTableMultipleProcessMain
) {
176 // Each process will open the shared memory and set counters
177 // concurrently in a loop. We'll use some pauses to
178 // mixup the scheduling.
180 StatsTable
table(kMPTableName
, 0, 0);
181 StatsTable::set_current(&table
);
182 StatsCounter
zero_counter(kCounterZero
);
183 StatsCounter
lucky13_counter(kCounter1313
);
184 StatsCounter
increment_counter(kCounterIncrement
);
185 StatsCounter
decrement_counter(kCounterDecrement
);
186 for (int index
= 0; index
< kThreadLoops
; index
++) {
188 lucky13_counter
.Set(1313);
189 increment_counter
.Increment();
190 decrement_counter
.Decrement();
191 PlatformThread::Sleep(TimeDelta::FromMilliseconds(index
% 10));
196 // Create a few processes and have them poke on their counters.
197 // This test is slow and flaky http://crbug.com/10611
198 TEST_F(StatsTableTest
, DISABLED_MultipleProcesses
) {
199 // Create a stats table.
200 const int kMaxProcs
= 20;
201 const int kMaxCounter
= 5;
202 DeleteShmem(kMPTableName
);
203 StatsTable
table(kMPTableName
, kMaxProcs
, kMaxCounter
);
204 StatsTable::set_current(&table
);
205 EXPECT_EQ(0, table
.CountThreadsRegistered());
207 // Spin up a set of processes to go bang on the various counters.
208 // After we join the processes, we'll make sure the counters
209 // contain the values we expected.
210 ProcessHandle procs
[kMaxProcs
];
212 // Spawn the processes.
213 for (int16 index
= 0; index
< kMaxProcs
; index
++) {
214 procs
[index
] = this->SpawnChild("StatsTableMultipleProcessMain", false);
215 EXPECT_NE(kNullProcessHandle
, procs
[index
]);
218 // Wait for the processes to finish.
219 for (int index
= 0; index
< kMaxProcs
; index
++) {
220 EXPECT_TRUE(WaitForSingleProcess(
221 procs
[index
], base::TimeDelta::FromMinutes(1)));
222 CloseProcessHandle(procs
[index
]);
225 StatsCounter
zero_counter(kCounterZero
);
226 StatsCounter
lucky13_counter(kCounter1313
);
227 StatsCounter
increment_counter(kCounterIncrement
);
228 StatsCounter
decrement_counter(kCounterDecrement
);
230 // Verify the various counters are correct.
232 name
= "c:" + kCounterZero
;
233 EXPECT_EQ(0, table
.GetCounterValue(name
));
234 name
= "c:" + kCounter1313
;
235 EXPECT_EQ(1313 * kMaxProcs
,
236 table
.GetCounterValue(name
));
237 name
= "c:" + kCounterIncrement
;
238 EXPECT_EQ(kMaxProcs
* kThreadLoops
,
239 table
.GetCounterValue(name
));
240 name
= "c:" + kCounterDecrement
;
241 EXPECT_EQ(-kMaxProcs
* kThreadLoops
,
242 table
.GetCounterValue(name
));
243 EXPECT_EQ(0, table
.CountThreadsRegistered());
245 DeleteShmem(kMPTableName
);
248 class MockStatsCounter
: public StatsCounter
{
250 explicit MockStatsCounter(const std::string
& name
)
251 : StatsCounter(name
) {}
252 int* Pointer() { return GetPtr(); }
255 // Test some basic StatsCounter operations
256 TEST_F(StatsTableTest
, StatsCounter
) {
257 // Create a stats table.
258 const std::string kTableName
= "StatTable";
259 const int kMaxThreads
= 20;
260 const int kMaxCounter
= 5;
261 DeleteShmem(kTableName
);
262 StatsTable
table(kTableName
, kMaxThreads
, kMaxCounter
);
263 StatsTable::set_current(&table
);
265 MockStatsCounter
foo("foo");
267 // Test initial state.
268 EXPECT_TRUE(foo
.Enabled());
269 ASSERT_NE(foo
.Pointer(), static_cast<int*>(0));
270 EXPECT_EQ(0, *(foo
.Pointer()));
271 EXPECT_EQ(0, table
.GetCounterValue("c:foo"));
274 while (*(foo
.Pointer()) < 123) foo
.Increment();
275 EXPECT_EQ(123, table
.GetCounterValue("c:foo"));
277 EXPECT_EQ(123, table
.GetCounterValue("c:foo"));
279 EXPECT_EQ(122, table
.GetCounterValue("c:foo"));
283 EXPECT_EQ(0, table
.GetCounterValue("c:foo"));
285 EXPECT_EQ(100, table
.GetCounterValue("c:foo"));
287 EXPECT_EQ(-1, table
.GetCounterValue("c:foo"));
289 EXPECT_EQ(0, table
.GetCounterValue("c:foo"));
293 EXPECT_EQ(-1, table
.GetCounterValue("c:foo"));
295 EXPECT_EQ(-1, table
.GetCounterValue("c:foo"));
297 EXPECT_EQ(0, table
.GetCounterValue("c:foo"));
299 DeleteShmem(kTableName
);
302 class MockStatsCounterTimer
: public StatsCounterTimer
{
304 explicit MockStatsCounterTimer(const std::string
& name
)
305 : StatsCounterTimer(name
) {}
307 TimeTicks
start_time() { return start_time_
; }
308 TimeTicks
stop_time() { return stop_time_
; }
311 // Test some basic StatsCounterTimer operations
312 TEST_F(StatsTableTest
, StatsCounterTimer
) {
313 // Create a stats table.
314 const std::string kTableName
= "StatTable";
315 const int kMaxThreads
= 20;
316 const int kMaxCounter
= 5;
317 DeleteShmem(kTableName
);
318 StatsTable
table(kTableName
, kMaxThreads
, kMaxCounter
);
319 StatsTable::set_current(&table
);
321 MockStatsCounterTimer
bar("bar");
323 // Test initial state.
324 EXPECT_FALSE(bar
.Running());
325 EXPECT_TRUE(bar
.start_time().is_null());
326 EXPECT_TRUE(bar
.stop_time().is_null());
328 const TimeDelta kDuration
= TimeDelta::FromMilliseconds(100);
332 PlatformThread::Sleep(kDuration
);
334 EXPECT_GT(table
.GetCounterValue("t:bar"), 0);
335 EXPECT_LE(kDuration
.InMilliseconds(), table
.GetCounterValue("t:bar"));
337 // Verify that timing again is additive.
339 PlatformThread::Sleep(kDuration
);
341 EXPECT_GT(table
.GetCounterValue("t:bar"), 0);
342 EXPECT_LE(kDuration
.InMilliseconds() * 2, table
.GetCounterValue("t:bar"));
343 DeleteShmem(kTableName
);
346 // Test some basic StatsRate operations
347 TEST_F(StatsTableTest
, StatsRate
) {
348 // Create a stats table.
349 const std::string kTableName
= "StatTable";
350 const int kMaxThreads
= 20;
351 const int kMaxCounter
= 5;
352 DeleteShmem(kTableName
);
353 StatsTable
table(kTableName
, kMaxThreads
, kMaxCounter
);
354 StatsTable::set_current(&table
);
356 StatsRate
baz("baz");
358 // Test initial state.
359 EXPECT_FALSE(baz
.Running());
360 EXPECT_EQ(0, table
.GetCounterValue("c:baz"));
361 EXPECT_EQ(0, table
.GetCounterValue("t:baz"));
363 const TimeDelta kDuration
= TimeDelta::FromMilliseconds(100);
367 PlatformThread::Sleep(kDuration
);
369 EXPECT_EQ(1, table
.GetCounterValue("c:baz"));
370 EXPECT_LE(kDuration
.InMilliseconds(), table
.GetCounterValue("t:baz"));
372 // Verify that timing again is additive.
374 PlatformThread::Sleep(kDuration
);
376 EXPECT_EQ(2, table
.GetCounterValue("c:baz"));
377 EXPECT_LE(kDuration
.InMilliseconds() * 2, table
.GetCounterValue("t:baz"));
378 DeleteShmem(kTableName
);
381 // Test some basic StatsScope operations
382 TEST_F(StatsTableTest
, StatsScope
) {
383 // Create a stats table.
384 const std::string kTableName
= "StatTable";
385 const int kMaxThreads
= 20;
386 const int kMaxCounter
= 5;
387 DeleteShmem(kTableName
);
388 StatsTable
table(kTableName
, kMaxThreads
, kMaxCounter
);
389 StatsTable::set_current(&table
);
391 StatsCounterTimer
foo("foo");
392 StatsRate
bar("bar");
394 // Test initial state.
395 EXPECT_EQ(0, table
.GetCounterValue("t:foo"));
396 EXPECT_EQ(0, table
.GetCounterValue("t:bar"));
397 EXPECT_EQ(0, table
.GetCounterValue("c:bar"));
399 const TimeDelta kDuration
= TimeDelta::FromMilliseconds(100);
403 StatsScope
<StatsCounterTimer
> timer(foo
);
404 StatsScope
<StatsRate
> timer2(bar
);
405 PlatformThread::Sleep(kDuration
);
407 EXPECT_LE(kDuration
.InMilliseconds(), table
.GetCounterValue("t:foo"));
408 EXPECT_LE(kDuration
.InMilliseconds(), table
.GetCounterValue("t:bar"));
409 EXPECT_EQ(1, table
.GetCounterValue("c:bar"));
411 // Try a second scope.
413 StatsScope
<StatsCounterTimer
> timer(foo
);
414 StatsScope
<StatsRate
> timer2(bar
);
415 PlatformThread::Sleep(kDuration
);
417 EXPECT_LE(kDuration
.InMilliseconds() * 2, table
.GetCounterValue("t:foo"));
418 EXPECT_LE(kDuration
.InMilliseconds() * 2, table
.GetCounterValue("t:bar"));
419 EXPECT_EQ(2, table
.GetCounterValue("c:bar"));
421 DeleteShmem(kTableName
);