blob: 649fd0707088f15e75ab9bf11995db9cd27d28ba [file] [log] [blame]
license.botf003cfe2008-08-24 09:55:55 +09001// Copyright (c) 2006-2008 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.
initial.commit3f4a7322008-07-27 06:49:38 +09004
5#include <process.h>
6#include <windows.h>
7
8#include "base/multiprocess_test.h"
9#include "base/stats_table.h"
10#include "base/stats_counters.h"
11#include "base/string_util.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14namespace {
15 class StatsTableTest : public MultiProcessTest {
16 };
17}
18
19// Open a StatsTable and verify that we can write to each of the
20// locations in the table.
21TEST_F(StatsTableTest, VerifySlots) {
22 const std::wstring kTableName = L"VerifySlotsStatTable";
23 const int kMaxThreads = 1;
24 const int kMaxCounter = 5;
25 StatsTable table(kTableName, kMaxThreads, kMaxCounter);
26
27 // Register a single thread.
28 std::wstring thread_name = L"mainThread";
29 int slot_id = table.RegisterThread(thread_name);
30 EXPECT_TRUE(slot_id);
31
32 // Fill up the table with counters.
33 std::wstring counter_base_name = L"counter";
34 for (int index=0; index < kMaxCounter; index++) {
35 std::wstring counter_name = counter_base_name;
36 StringAppendF(&counter_name, L"counter.ctr%d", index);
37 int counter_id = table.FindCounter(counter_name);
38 EXPECT_GT(counter_id, 0);
39 }
40
41 // Try to allocate an additional thread. Verify it fails.
42 slot_id = table.RegisterThread(L"too many threads");
43 EXPECT_EQ(slot_id, 0);
44
45 // Try to allocate an additional counter. Verify it fails.
46 int counter_id = table.FindCounter(counter_base_name);
47 EXPECT_EQ(counter_id, 0);
48}
49
50// CounterZero will continually be set to 0.
51const std::wstring kCounterZero = L"CounterZero";
52// Counter1313 will continually be set to 1313.
53const std::wstring kCounter1313 = L"Counter1313";
54// CounterIncrement will be incremented each time.
55const std::wstring kCounterIncrement = L"CounterIncrement";
56// CounterDecrement will be decremented each time.
57const std::wstring kCounterDecrement = L"CounterDecrement";
58// CounterMixed will be incremented by odd numbered threads and
59// decremented by even threads.
60const std::wstring kCounterMixed = L"CounterMixed";
61// The number of thread loops that we will do.
62const int kThreadLoops = 1000;
63
64unsigned __stdcall StatsTableMultipleThreadMain(void* param) {
65 // Each thread will open the shared memory and set counters
66 // concurrently in a loop. We'll use some pauses to
67 // mixup the thread scheduling.
68 int16 id = reinterpret_cast<int16>(param);
69
70 StatsCounter zero_counter(kCounterZero);
71 StatsCounter lucky13_counter(kCounter1313);
72 StatsCounter increment_counter(kCounterIncrement);
73 StatsCounter decrement_counter(kCounterDecrement);
74 for (int index = 0; index < kThreadLoops; index++) {
75 StatsCounter mixed_counter(kCounterMixed); // create this one in the loop
76 zero_counter.Set(0);
77 lucky13_counter.Set(1313);
78 increment_counter.Increment();
79 decrement_counter.Decrement();
80 if (id % 2)
81 mixed_counter.Decrement();
82 else
83 mixed_counter.Increment();
84 Sleep(index % 10); // short wait
85 }
86 return 0;
87}
88// Create a few threads and have them poke on their counters.
89TEST_F(StatsTableTest, MultipleThreads) {
90 // Create a stats table.
91 const std::wstring kTableName = L"MultipleThreadStatTable";
92 const int kMaxThreads = 20;
93 const int kMaxCounter = 5;
94 StatsTable table(kTableName, kMaxThreads, kMaxCounter);
95 StatsTable::set_current(&table);
96
97 EXPECT_EQ(0, table.CountThreadsRegistered());
98
99 // Spin up a set of threads to go bang on the various counters.
100 // After we join the threads, we'll make sure the counters
101 // contain the values we expected.
102 HANDLE threads[kMaxThreads];
103
104 // Spawn the threads.
105 for (int16 index = 0; index < kMaxThreads; index++) {
106 void* argument = reinterpret_cast<void*>(index);
107 unsigned thread_id;
108 threads[index] = reinterpret_cast<HANDLE>(
109 _beginthreadex(NULL, 0, StatsTableMultipleThreadMain, argument, 0,
110 &thread_id));
111 EXPECT_NE((HANDLE)NULL, threads[index]);
112 }
113
114 // Wait for the threads to finish.
115 for (int index = 0; index < kMaxThreads; index++) {
116 DWORD rv = WaitForSingleObject(threads[index], 60 * 1000);
117 EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished
118 }
119 StatsCounter zero_counter(kCounterZero);
120 StatsCounter lucky13_counter(kCounter1313);
121 StatsCounter increment_counter(kCounterIncrement);
122 StatsCounter decrement_counter(kCounterDecrement);
123 StatsCounter mixed_counter(kCounterMixed);
124
125 // Verify the various counters are correct.
126 std::wstring name;
127 name = L"c:" + kCounterZero;
128 EXPECT_EQ(0, table.GetCounterValue(name));
129 name = L"c:" + kCounter1313;
130 EXPECT_EQ(1313 * kMaxThreads,
131 table.GetCounterValue(name));
132 name = L"c:" + kCounterIncrement;
133 EXPECT_EQ(kMaxThreads * kThreadLoops,
134 table.GetCounterValue(name));
135 name = L"c:" + kCounterDecrement;
136 EXPECT_EQ(-kMaxThreads * kThreadLoops,
137 table.GetCounterValue(name));
138 name = L"c:" + kCounterMixed;
139 EXPECT_EQ((kMaxThreads % 2) * kThreadLoops,
140 table.GetCounterValue(name));
141 EXPECT_EQ(0, table.CountThreadsRegistered());
142}
143
144const std::wstring kTableName = L"MultipleProcessStatTable";
145
146extern "C" int __declspec(dllexport) ChildProcessMain() {
147 // Each process will open the shared memory and set counters
148 // concurrently in a loop. We'll use some pauses to
149 // mixup the scheduling.
150
151 StatsTable table(kTableName, 0, 0);
152 StatsTable::set_current(&table);
153 StatsCounter zero_counter(kCounterZero);
154 StatsCounter lucky13_counter(kCounter1313);
155 StatsCounter increment_counter(kCounterIncrement);
156 StatsCounter decrement_counter(kCounterDecrement);
157 for (int index = 0; index < kThreadLoops; index++) {
158 zero_counter.Set(0);
159 lucky13_counter.Set(1313);
160 increment_counter.Increment();
161 decrement_counter.Decrement();
162 Sleep(index % 10); // short wait
163 }
164 return 0;
165}
166
167// Create a few threads and have them poke on their counters.
168TEST_F(StatsTableTest, MultipleProcesses) {
169 // Create a stats table.
170 const std::wstring kTableName = L"MultipleProcessStatTable";
171 const int kMaxThreads = 20;
172 const int kMaxCounter = 5;
173 StatsTable table(kTableName, kMaxThreads, kMaxCounter);
174 StatsTable::set_current(&table);
175
176 EXPECT_EQ(0, table.CountThreadsRegistered());
177
178 // Spin up a set of threads to go bang on the various counters.
179 // After we join the threads, we'll make sure the counters
180 // contain the values we expected.
181 HANDLE threads[kMaxThreads];
182
183 // Spawn the processes.
184 for (int16 index = 0; index < kMaxThreads; index++) {
185 threads[index] = this->SpawnChild(L"ChildProcessMain");
186 EXPECT_NE((HANDLE)NULL, threads[index]);
187 }
188
189 // Wait for the threads to finish.
190 for (int index = 0; index < kMaxThreads; index++) {
191 DWORD rv = WaitForSingleObject(threads[index], 60 * 1000);
192 EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished
193 }
194 StatsCounter zero_counter(kCounterZero);
195 StatsCounter lucky13_counter(kCounter1313);
196 StatsCounter increment_counter(kCounterIncrement);
197 StatsCounter decrement_counter(kCounterDecrement);
198
199 // Verify the various counters are correct.
200 std::wstring name;
201 name = L"c:" + kCounterZero;
202 EXPECT_EQ(0, table.GetCounterValue(name));
203 name = L"c:" + kCounter1313;
204 EXPECT_EQ(1313 * kMaxThreads,
205 table.GetCounterValue(name));
206 name = L"c:" + kCounterIncrement;
207 EXPECT_EQ(kMaxThreads * kThreadLoops,
208 table.GetCounterValue(name));
209 name = L"c:" + kCounterDecrement;
210 EXPECT_EQ(-kMaxThreads * kThreadLoops,
211 table.GetCounterValue(name));
212 EXPECT_EQ(0, table.CountThreadsRegistered());
213}
214
215class MockStatsCounter : public StatsCounter {
216 public:
217 MockStatsCounter(const std::wstring& name)
218 : StatsCounter(name) {}
219 int* Pointer() { return GetPtr(); }
220};
221
222// Test some basic StatsCounter operations
223TEST_F(StatsTableTest, StatsCounter) {
224 // Create a stats table.
225 const std::wstring kTableName = L"StatTable";
226 const int kMaxThreads = 20;
227 const int kMaxCounter = 5;
228 StatsTable table(kTableName, kMaxThreads, kMaxCounter);
229 StatsTable::set_current(&table);
230
231 MockStatsCounter foo(L"foo");
232
233 // Test initial state.
234 EXPECT_TRUE(foo.Enabled());
235 EXPECT_NE(foo.Pointer(), static_cast<int*>(0));
236 EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
237 EXPECT_EQ(0, *(foo.Pointer()));
238
239 // Test Increment.
240 while(*(foo.Pointer()) < 123) foo.Increment();
241 EXPECT_EQ(123, table.GetCounterValue(L"c:foo"));
242 foo.Add(0);
243 EXPECT_EQ(123, table.GetCounterValue(L"c:foo"));
244 foo.Add(-1);
245 EXPECT_EQ(122, table.GetCounterValue(L"c:foo"));
246
247 // Test Set.
248 foo.Set(0);
249 EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
250 foo.Set(100);
251 EXPECT_EQ(100, table.GetCounterValue(L"c:foo"));
252 foo.Set(-1);
253 EXPECT_EQ(-1, table.GetCounterValue(L"c:foo"));
254 foo.Set(0);
255 EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
256
257 // Test Decrement.
258 foo.Decrement(1);
259 EXPECT_EQ(-1, table.GetCounterValue(L"c:foo"));
260 foo.Decrement(0);
261 EXPECT_EQ(-1, table.GetCounterValue(L"c:foo"));
262 foo.Decrement(-1);
263 EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
264}
265
266class MockStatsCounterTimer : public StatsCounterTimer {
267 public:
268 MockStatsCounterTimer(const std::wstring& name)
269 : StatsCounterTimer(name) {}
270
271 TimeTicks start_time() { return start_time_; }
272 TimeTicks stop_time() { return stop_time_; }
273};
274
275// Test some basic StatsCounterTimer operations
276TEST_F(StatsTableTest, StatsCounterTimer) {
277 // Create a stats table.
278 const std::wstring kTableName = L"StatTable";
279 const int kMaxThreads = 20;
280 const int kMaxCounter = 5;
281 StatsTable table(kTableName, kMaxThreads, kMaxCounter);
282 StatsTable::set_current(&table);
283
284 MockStatsCounterTimer bar(L"bar");
285
286 // Test initial state.
287 EXPECT_FALSE(bar.Running());
288 EXPECT_TRUE(bar.start_time().is_null());
289 EXPECT_TRUE(bar.stop_time().is_null());
290
291 // Do some timing.
292 bar.Start();
293 Sleep(500);
294 bar.Stop();
295 EXPECT_LE(500, table.GetCounterValue(L"t:bar"));
296
297 // Verify that timing again is additive.
298 bar.Start();
299 Sleep(500);
300 bar.Stop();
301 EXPECT_LE(1000, table.GetCounterValue(L"t:bar"));
302}
303
304// Test some basic StatsRate operations
305TEST_F(StatsTableTest, StatsRate) {
306 // Create a stats table.
307 const std::wstring kTableName = L"StatTable";
308 const int kMaxThreads = 20;
309 const int kMaxCounter = 5;
310 StatsTable table(kTableName, kMaxThreads, kMaxCounter);
311 StatsTable::set_current(&table);
312
313 StatsRate baz(L"baz");
314
315 // Test initial state.
316 EXPECT_FALSE(baz.Running());
317 EXPECT_EQ(0, table.GetCounterValue(L"c:baz"));
318 EXPECT_EQ(0, table.GetCounterValue(L"t:baz"));
319
320 // Do some timing.
321 baz.Start();
322 Sleep(500);
323 baz.Stop();
324 EXPECT_EQ(1, table.GetCounterValue(L"c:baz"));
325 EXPECT_LE(500, table.GetCounterValue(L"t:baz"));
326
327 // Verify that timing again is additive.
328 baz.Start();
329 Sleep(500);
330 baz.Stop();
331 EXPECT_EQ(2, table.GetCounterValue(L"c:baz"));
332 EXPECT_LE(1000, table.GetCounterValue(L"t:baz"));
333}
334
335// Test some basic StatsScope operations
336TEST_F(StatsTableTest, StatsScope) {
337 // Create a stats table.
338 const std::wstring kTableName = L"StatTable";
339 const int kMaxThreads = 20;
340 const int kMaxCounter = 5;
341 StatsTable table(kTableName, kMaxThreads, kMaxCounter);
342 StatsTable::set_current(&table);
343
344 StatsCounterTimer foo(L"foo");
345 StatsRate bar(L"bar");
346
347 // Test initial state.
348 EXPECT_EQ(0, table.GetCounterValue(L"t:foo"));
349 EXPECT_EQ(0, table.GetCounterValue(L"t:bar"));
350 EXPECT_EQ(0, table.GetCounterValue(L"c:bar"));
351
352 // Try a scope.
353 {
354 StatsScope<StatsCounterTimer> timer(foo);
355 StatsScope<StatsRate> timer2(bar);
356 Sleep(500);
357 }
358 EXPECT_LE(500, table.GetCounterValue(L"t:foo"));
359 EXPECT_LE(500, table.GetCounterValue(L"t:bar"));
360 EXPECT_EQ(1, table.GetCounterValue(L"c:bar"));
361
362 // Try a second scope.
363 {
364 StatsScope<StatsCounterTimer> timer(foo);
365 StatsScope<StatsRate> timer2(bar);
366 Sleep(500);
367 }
368 EXPECT_LE(1000, table.GetCounterValue(L"t:foo"));
369 EXPECT_LE(1000, table.GetCounterValue(L"t:bar"));
370 EXPECT_EQ(2, table.GetCounterValue(L"c:bar"));
deanm@google.com97137862008-08-14 20:44:17 +0900371}
license.botf003cfe2008-08-24 09:55:55 +0900372