blob: 952b89e73ab2ef8bebd3ed3e3a9c0d5e515e758f [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2010 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <iomanip>
29#include <iostream>
30#include <vector>
31
32#ifdef WIN32
33#include "talk/base/win32.h"
34#endif
35
36#include "talk/base/cpumonitor.h"
37#include "talk/base/flags.h"
38#include "talk/base/gunit.h"
39#include "talk/base/scoped_ptr.h"
40#include "talk/base/thread.h"
41#include "talk/base/timeutils.h"
42#include "talk/base/timing.h"
43
44namespace talk_base {
45
46static const int kMaxCpus = 1024;
47static const int kSettleTime = 100; // Amount of time to between tests.
48static const int kIdleTime = 500; // Amount of time to be idle in ms.
49static const int kBusyTime = 1000; // Amount of time to be busy in ms.
50static const int kLongInterval = 2000; // Interval longer than busy times
51
52class BusyThread : public talk_base::Thread {
53 public:
54 BusyThread(double load, double duration, double interval) :
55 load_(load), duration_(duration), interval_(interval) {
56 }
57 void Run() {
58 Timing time;
59 double busy_time = interval_ * load_ / 100.0;
60 for (;;) {
61 time.BusyWait(busy_time);
62 time.IdleWait(interval_ - busy_time);
63 if (duration_) {
64 duration_ -= interval_;
65 if (duration_ <= 0) {
66 break;
67 }
68 }
69 }
70 }
71 private:
72 double load_;
73 double duration_;
74 double interval_;
75};
76
77class CpuLoadListener : public sigslot::has_slots<> {
78 public:
79 CpuLoadListener()
80 : current_cpus_(0),
81 cpus_(0),
82 process_load_(.0f),
83 system_load_(.0f),
84 count_(0) {
85 }
86
87 void OnCpuLoad(int current_cpus, int cpus, float proc_load, float sys_load) {
88 current_cpus_ = current_cpus;
89 cpus_ = cpus;
90 process_load_ = proc_load;
91 system_load_ = sys_load;
92 ++count_;
93 }
94
95 int current_cpus() const { return current_cpus_; }
96 int cpus() const { return cpus_; }
97 float process_load() const { return process_load_; }
98 float system_load() const { return system_load_; }
99 int count() const { return count_; }
100
101 private:
102 int current_cpus_;
103 int cpus_;
104 float process_load_;
105 float system_load_;
106 int count_;
107};
108
109// Set affinity (which cpu to run on), but respecting FLAG_affinity:
110// -1 means no affinity - run on whatever cpu is available.
111// 0 .. N means run on specific cpu. The tool will create N threads and call
112// SetThreadAffinity on 0 to N - 1 as cpu. FLAG_affinity sets the first cpu
113// so the range becomes affinity to affinity + N - 1
114// Note that this function affects Windows scheduling, effectively giving
115// the thread with affinity for a specified CPU more priority on that CPU.
116bool SetThreadAffinity(BusyThread* t, int cpu, int affinity) {
117#ifdef WIN32
118 if (affinity >= 0) {
119 return ::SetThreadAffinityMask(t->GetHandle(),
120 1 << (cpu + affinity)) != FALSE;
121 }
122#endif
123 return true;
124}
125
126bool SetThreadPriority(BusyThread* t, int prio) {
127 if (!prio) {
128 return true;
129 }
130 bool ok = t->SetPriority(static_cast<talk_base::ThreadPriority>(prio));
131 if (!ok) {
132 std::cout << "Error setting thread priority." << std::endl;
133 }
134 return ok;
135}
136
137int CpuLoad(double cpuload, double duration, int numthreads,
138 int priority, double interval, int affinity) {
139 int ret = 0;
140 std::vector<BusyThread*> threads;
141 for (int i = 0; i < numthreads; ++i) {
142 threads.push_back(new BusyThread(cpuload, duration, interval));
143 // NOTE(fbarchard): Priority must be done before Start.
144 if (!SetThreadPriority(threads[i], priority) ||
145 !threads[i]->Start() ||
146 !SetThreadAffinity(threads[i], i, affinity)) {
147 ret = 1;
148 break;
149 }
150 }
151 // Wait on each thread
152 if (ret == 0) {
153 for (int i = 0; i < numthreads; ++i) {
154 threads[i]->Stop();
155 }
156 }
157
158 for (int i = 0; i < numthreads; ++i) {
159 delete threads[i];
160 }
161 return ret;
162}
163
164// Make 2 CPUs busy
165static void CpuTwoBusyLoop(int busytime) {
166 CpuLoad(100.0, busytime / 1000.0, 2, 1, 0.050, -1);
167}
168
169// Make 1 CPUs busy
170static void CpuBusyLoop(int busytime) {
171 CpuLoad(100.0, busytime / 1000.0, 1, 1, 0.050, -1);
172}
173
174// Make 1 use half CPU time.
175static void CpuHalfBusyLoop(int busytime) {
176 CpuLoad(50.0, busytime / 1000.0, 1, 1, 0.050, -1);
177}
178
179void TestCpuSampler(bool test_proc, bool test_sys, bool force_fallback) {
180 CpuSampler sampler;
181 sampler.set_force_fallback(force_fallback);
182 EXPECT_TRUE(sampler.Init());
183 sampler.set_load_interval(100);
184 int cpus = sampler.GetMaxCpus();
185
186 // Test1: CpuSampler under idle situation.
187 Thread::SleepMs(kSettleTime);
188 sampler.GetProcessLoad();
189 sampler.GetSystemLoad();
190
191 Thread::SleepMs(kIdleTime);
192
193 float proc_idle = 0.f, sys_idle = 0.f;
194 if (test_proc) {
195 proc_idle = sampler.GetProcessLoad();
196 }
197 if (test_sys) {
198 sys_idle = sampler.GetSystemLoad();
199 }
200 if (test_proc) {
201 LOG(LS_INFO) << "ProcessLoad Idle: "
202 << std::setiosflags(std::ios_base::fixed)
203 << std::setprecision(2) << std::setw(6) << proc_idle;
204 EXPECT_GE(proc_idle, 0.f);
205 EXPECT_LE(proc_idle, static_cast<float>(cpus));
206 }
207 if (test_sys) {
208 LOG(LS_INFO) << "SystemLoad Idle: "
209 << std::setiosflags(std::ios_base::fixed)
210 << std::setprecision(2) << std::setw(6) << sys_idle;
211 EXPECT_GE(sys_idle, 0.f);
212 EXPECT_LE(sys_idle, static_cast<float>(cpus));
213 }
214
215 // Test2: CpuSampler with main process at 50% busy.
216 Thread::SleepMs(kSettleTime);
217 sampler.GetProcessLoad();
218 sampler.GetSystemLoad();
219
220 CpuHalfBusyLoop(kBusyTime);
221
222 float proc_halfbusy = 0.f, sys_halfbusy = 0.f;
223 if (test_proc) {
224 proc_halfbusy = sampler.GetProcessLoad();
225 }
226 if (test_sys) {
227 sys_halfbusy = sampler.GetSystemLoad();
228 }
229 if (test_proc) {
230 LOG(LS_INFO) << "ProcessLoad Halfbusy: "
231 << std::setiosflags(std::ios_base::fixed)
232 << std::setprecision(2) << std::setw(6) << proc_halfbusy;
233 EXPECT_GE(proc_halfbusy, 0.f);
234 EXPECT_LE(proc_halfbusy, static_cast<float>(cpus));
235 }
236 if (test_sys) {
237 LOG(LS_INFO) << "SystemLoad Halfbusy: "
238 << std::setiosflags(std::ios_base::fixed)
239 << std::setprecision(2) << std::setw(6) << sys_halfbusy;
240 EXPECT_GE(sys_halfbusy, 0.f);
241 EXPECT_LE(sys_halfbusy, static_cast<float>(cpus));
242 }
243
244 // Test3: CpuSampler with main process busy.
245 Thread::SleepMs(kSettleTime);
246 sampler.GetProcessLoad();
247 sampler.GetSystemLoad();
248
249 CpuBusyLoop(kBusyTime);
250
251 float proc_busy = 0.f, sys_busy = 0.f;
252 if (test_proc) {
253 proc_busy = sampler.GetProcessLoad();
254 }
255 if (test_sys) {
256 sys_busy = sampler.GetSystemLoad();
257 }
258 if (test_proc) {
259 LOG(LS_INFO) << "ProcessLoad Busy: "
260 << std::setiosflags(std::ios_base::fixed)
261 << std::setprecision(2) << std::setw(6) << proc_busy;
262 EXPECT_GE(proc_busy, 0.f);
263 EXPECT_LE(proc_busy, static_cast<float>(cpus));
264 }
265 if (test_sys) {
266 LOG(LS_INFO) << "SystemLoad Busy: "
267 << std::setiosflags(std::ios_base::fixed)
268 << std::setprecision(2) << std::setw(6) << sys_busy;
269 EXPECT_GE(sys_busy, 0.f);
270 EXPECT_LE(sys_busy, static_cast<float>(cpus));
271 }
272
273 // Test4: CpuSampler with 2 cpus process busy.
274 if (cpus >= 2) {
275 Thread::SleepMs(kSettleTime);
276 sampler.GetProcessLoad();
277 sampler.GetSystemLoad();
278
279 CpuTwoBusyLoop(kBusyTime);
280
281 float proc_twobusy = 0.f, sys_twobusy = 0.f;
282 if (test_proc) {
283 proc_twobusy = sampler.GetProcessLoad();
284 }
285 if (test_sys) {
286 sys_twobusy = sampler.GetSystemLoad();
287 }
288 if (test_proc) {
289 LOG(LS_INFO) << "ProcessLoad 2 CPU Busy:"
290 << std::setiosflags(std::ios_base::fixed)
291 << std::setprecision(2) << std::setw(6) << proc_twobusy;
292 EXPECT_GE(proc_twobusy, 0.f);
293 EXPECT_LE(proc_twobusy, static_cast<float>(cpus));
294 }
295 if (test_sys) {
296 LOG(LS_INFO) << "SystemLoad 2 CPU Busy: "
297 << std::setiosflags(std::ios_base::fixed)
298 << std::setprecision(2) << std::setw(6) << sys_twobusy;
299 EXPECT_GE(sys_twobusy, 0.f);
300 EXPECT_LE(sys_twobusy, static_cast<float>(cpus));
301 }
302 }
303
304 // Test5: CpuSampler with idle process after being busy.
305 Thread::SleepMs(kSettleTime);
306 sampler.GetProcessLoad();
307 sampler.GetSystemLoad();
308
309 Thread::SleepMs(kIdleTime);
310
311 if (test_proc) {
312 proc_idle = sampler.GetProcessLoad();
313 }
314 if (test_sys) {
315 sys_idle = sampler.GetSystemLoad();
316 }
317 if (test_proc) {
318 LOG(LS_INFO) << "ProcessLoad Idle: "
319 << std::setiosflags(std::ios_base::fixed)
320 << std::setprecision(2) << std::setw(6) << proc_idle;
321 EXPECT_GE(proc_idle, 0.f);
322 EXPECT_LE(proc_idle, proc_busy);
323 }
324 if (test_sys) {
325 LOG(LS_INFO) << "SystemLoad Idle: "
326 << std::setiosflags(std::ios_base::fixed)
327 << std::setprecision(2) << std::setw(6) << sys_idle;
328 EXPECT_GE(sys_idle, 0.f);
329 EXPECT_LE(sys_idle, static_cast<float>(cpus));
330 }
331}
332
333TEST(CpuMonitorTest, TestCpus) {
334 CpuSampler sampler;
335 EXPECT_TRUE(sampler.Init());
336 int current_cpus = sampler.GetCurrentCpus();
337 int cpus = sampler.GetMaxCpus();
338 LOG(LS_INFO) << "Current Cpus: " << std::setw(9) << current_cpus;
339 LOG(LS_INFO) << "Maximum Cpus: " << std::setw(9) << cpus;
340 EXPECT_GT(cpus, 0);
341 EXPECT_LE(cpus, kMaxCpus);
342 EXPECT_GT(current_cpus, 0);
343 EXPECT_LE(current_cpus, cpus);
344}
345
346#ifdef WIN32
347// Tests overall system CpuSampler using legacy OS fallback code if applicable.
348TEST(CpuMonitorTest, TestGetSystemLoadForceFallback) {
349 TestCpuSampler(false, true, true);
350}
351#endif
352
353// Tests both process and system functions in use at same time.
354TEST(CpuMonitorTest, TestGetBothLoad) {
355 TestCpuSampler(true, true, false);
356}
357
358// Tests a query less than the interval produces the same value.
359TEST(CpuMonitorTest, TestInterval) {
360 CpuSampler sampler;
361 EXPECT_TRUE(sampler.Init());
362
363 // Test1: Set interval to large value so sampler will not update.
364 sampler.set_load_interval(kLongInterval);
365
366 sampler.GetProcessLoad();
367 sampler.GetSystemLoad();
368
369 float proc_orig = sampler.GetProcessLoad();
370 float sys_orig = sampler.GetSystemLoad();
371
372 Thread::SleepMs(kIdleTime);
373
374 float proc_halftime = sampler.GetProcessLoad();
375 float sys_halftime = sampler.GetSystemLoad();
376
377 EXPECT_EQ(proc_orig, proc_halftime);
378 EXPECT_EQ(sys_orig, sys_halftime);
379}
380
381TEST(CpuMonitorTest, TestCpuMonitor) {
382 CpuMonitor monitor(Thread::Current());
383 CpuLoadListener listener;
384 monitor.SignalUpdate.connect(&listener, &CpuLoadListener::OnCpuLoad);
385 EXPECT_TRUE(monitor.Start(10));
386 Thread::Current()->ProcessMessages(50);
387 EXPECT_GT(listener.count(), 2); // We have checked cpu load more than twice.
388 EXPECT_GT(listener.current_cpus(), 0);
389 EXPECT_GT(listener.cpus(), 0);
390 EXPECT_GE(listener.process_load(), .0f);
391 EXPECT_GE(listener.system_load(), .0f);
392
393 monitor.Stop();
394 // Wait 20 ms to ake sure all signals are delivered.
395 Thread::Current()->ProcessMessages(20);
396 int old_count = listener.count();
397 Thread::Current()->ProcessMessages(20);
398 // Verfy no more siganls.
399 EXPECT_EQ(old_count, listener.count());
400}
401
402} // namespace talk_base