blob: cdc3e6b79b2df0696f881f4a45c6db84fb6d71c1 [file] [log] [blame]
henrike@webrtc.org0e118e72013-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 }
wu@webrtc.orge5b49102013-10-18 16:27:26 +000057 virtual ~BusyThread() {
58 Stop();
59 }
henrike@webrtc.org0e118e72013-07-10 00:45:36 +000060 void Run() {
61 Timing time;
62 double busy_time = interval_ * load_ / 100.0;
63 for (;;) {
64 time.BusyWait(busy_time);
65 time.IdleWait(interval_ - busy_time);
66 if (duration_) {
67 duration_ -= interval_;
68 if (duration_ <= 0) {
69 break;
70 }
71 }
72 }
73 }
74 private:
75 double load_;
76 double duration_;
77 double interval_;
78};
79
80class CpuLoadListener : public sigslot::has_slots<> {
81 public:
82 CpuLoadListener()
83 : current_cpus_(0),
84 cpus_(0),
85 process_load_(.0f),
86 system_load_(.0f),
87 count_(0) {
88 }
89
90 void OnCpuLoad(int current_cpus, int cpus, float proc_load, float sys_load) {
91 current_cpus_ = current_cpus;
92 cpus_ = cpus;
93 process_load_ = proc_load;
94 system_load_ = sys_load;
95 ++count_;
96 }
97
98 int current_cpus() const { return current_cpus_; }
99 int cpus() const { return cpus_; }
100 float process_load() const { return process_load_; }
101 float system_load() const { return system_load_; }
102 int count() const { return count_; }
103
104 private:
105 int current_cpus_;
106 int cpus_;
107 float process_load_;
108 float system_load_;
109 int count_;
110};
111
112// Set affinity (which cpu to run on), but respecting FLAG_affinity:
113// -1 means no affinity - run on whatever cpu is available.
114// 0 .. N means run on specific cpu. The tool will create N threads and call
115// SetThreadAffinity on 0 to N - 1 as cpu. FLAG_affinity sets the first cpu
116// so the range becomes affinity to affinity + N - 1
117// Note that this function affects Windows scheduling, effectively giving
118// the thread with affinity for a specified CPU more priority on that CPU.
119bool SetThreadAffinity(BusyThread* t, int cpu, int affinity) {
120#ifdef WIN32
121 if (affinity >= 0) {
122 return ::SetThreadAffinityMask(t->GetHandle(),
123 1 << (cpu + affinity)) != FALSE;
124 }
125#endif
126 return true;
127}
128
129bool SetThreadPriority(BusyThread* t, int prio) {
130 if (!prio) {
131 return true;
132 }
133 bool ok = t->SetPriority(static_cast<talk_base::ThreadPriority>(prio));
134 if (!ok) {
135 std::cout << "Error setting thread priority." << std::endl;
136 }
137 return ok;
138}
139
140int CpuLoad(double cpuload, double duration, int numthreads,
141 int priority, double interval, int affinity) {
142 int ret = 0;
143 std::vector<BusyThread*> threads;
144 for (int i = 0; i < numthreads; ++i) {
145 threads.push_back(new BusyThread(cpuload, duration, interval));
146 // NOTE(fbarchard): Priority must be done before Start.
147 if (!SetThreadPriority(threads[i], priority) ||
148 !threads[i]->Start() ||
149 !SetThreadAffinity(threads[i], i, affinity)) {
150 ret = 1;
151 break;
152 }
153 }
154 // Wait on each thread
155 if (ret == 0) {
156 for (int i = 0; i < numthreads; ++i) {
157 threads[i]->Stop();
158 }
159 }
160
161 for (int i = 0; i < numthreads; ++i) {
162 delete threads[i];
163 }
164 return ret;
165}
166
167// Make 2 CPUs busy
168static void CpuTwoBusyLoop(int busytime) {
169 CpuLoad(100.0, busytime / 1000.0, 2, 1, 0.050, -1);
170}
171
172// Make 1 CPUs busy
173static void CpuBusyLoop(int busytime) {
174 CpuLoad(100.0, busytime / 1000.0, 1, 1, 0.050, -1);
175}
176
177// Make 1 use half CPU time.
178static void CpuHalfBusyLoop(int busytime) {
179 CpuLoad(50.0, busytime / 1000.0, 1, 1, 0.050, -1);
180}
181
182void TestCpuSampler(bool test_proc, bool test_sys, bool force_fallback) {
183 CpuSampler sampler;
184 sampler.set_force_fallback(force_fallback);
185 EXPECT_TRUE(sampler.Init());
186 sampler.set_load_interval(100);
187 int cpus = sampler.GetMaxCpus();
188
189 // Test1: CpuSampler under idle situation.
190 Thread::SleepMs(kSettleTime);
191 sampler.GetProcessLoad();
192 sampler.GetSystemLoad();
193
194 Thread::SleepMs(kIdleTime);
195
196 float proc_idle = 0.f, sys_idle = 0.f;
197 if (test_proc) {
198 proc_idle = sampler.GetProcessLoad();
199 }
200 if (test_sys) {
201 sys_idle = sampler.GetSystemLoad();
202 }
203 if (test_proc) {
204 LOG(LS_INFO) << "ProcessLoad Idle: "
205 << std::setiosflags(std::ios_base::fixed)
206 << std::setprecision(2) << std::setw(6) << proc_idle;
207 EXPECT_GE(proc_idle, 0.f);
208 EXPECT_LE(proc_idle, static_cast<float>(cpus));
209 }
210 if (test_sys) {
211 LOG(LS_INFO) << "SystemLoad Idle: "
212 << std::setiosflags(std::ios_base::fixed)
213 << std::setprecision(2) << std::setw(6) << sys_idle;
214 EXPECT_GE(sys_idle, 0.f);
215 EXPECT_LE(sys_idle, static_cast<float>(cpus));
216 }
217
218 // Test2: CpuSampler with main process at 50% busy.
219 Thread::SleepMs(kSettleTime);
220 sampler.GetProcessLoad();
221 sampler.GetSystemLoad();
222
223 CpuHalfBusyLoop(kBusyTime);
224
225 float proc_halfbusy = 0.f, sys_halfbusy = 0.f;
226 if (test_proc) {
227 proc_halfbusy = sampler.GetProcessLoad();
228 }
229 if (test_sys) {
230 sys_halfbusy = sampler.GetSystemLoad();
231 }
232 if (test_proc) {
233 LOG(LS_INFO) << "ProcessLoad Halfbusy: "
234 << std::setiosflags(std::ios_base::fixed)
235 << std::setprecision(2) << std::setw(6) << proc_halfbusy;
236 EXPECT_GE(proc_halfbusy, 0.f);
237 EXPECT_LE(proc_halfbusy, static_cast<float>(cpus));
238 }
239 if (test_sys) {
240 LOG(LS_INFO) << "SystemLoad Halfbusy: "
241 << std::setiosflags(std::ios_base::fixed)
242 << std::setprecision(2) << std::setw(6) << sys_halfbusy;
243 EXPECT_GE(sys_halfbusy, 0.f);
244 EXPECT_LE(sys_halfbusy, static_cast<float>(cpus));
245 }
246
247 // Test3: CpuSampler with main process busy.
248 Thread::SleepMs(kSettleTime);
249 sampler.GetProcessLoad();
250 sampler.GetSystemLoad();
251
252 CpuBusyLoop(kBusyTime);
253
254 float proc_busy = 0.f, sys_busy = 0.f;
255 if (test_proc) {
256 proc_busy = sampler.GetProcessLoad();
257 }
258 if (test_sys) {
259 sys_busy = sampler.GetSystemLoad();
260 }
261 if (test_proc) {
262 LOG(LS_INFO) << "ProcessLoad Busy: "
263 << std::setiosflags(std::ios_base::fixed)
264 << std::setprecision(2) << std::setw(6) << proc_busy;
265 EXPECT_GE(proc_busy, 0.f);
266 EXPECT_LE(proc_busy, static_cast<float>(cpus));
267 }
268 if (test_sys) {
269 LOG(LS_INFO) << "SystemLoad Busy: "
270 << std::setiosflags(std::ios_base::fixed)
271 << std::setprecision(2) << std::setw(6) << sys_busy;
272 EXPECT_GE(sys_busy, 0.f);
273 EXPECT_LE(sys_busy, static_cast<float>(cpus));
274 }
275
276 // Test4: CpuSampler with 2 cpus process busy.
277 if (cpus >= 2) {
278 Thread::SleepMs(kSettleTime);
279 sampler.GetProcessLoad();
280 sampler.GetSystemLoad();
281
282 CpuTwoBusyLoop(kBusyTime);
283
284 float proc_twobusy = 0.f, sys_twobusy = 0.f;
285 if (test_proc) {
286 proc_twobusy = sampler.GetProcessLoad();
287 }
288 if (test_sys) {
289 sys_twobusy = sampler.GetSystemLoad();
290 }
291 if (test_proc) {
292 LOG(LS_INFO) << "ProcessLoad 2 CPU Busy:"
293 << std::setiosflags(std::ios_base::fixed)
294 << std::setprecision(2) << std::setw(6) << proc_twobusy;
295 EXPECT_GE(proc_twobusy, 0.f);
296 EXPECT_LE(proc_twobusy, static_cast<float>(cpus));
297 }
298 if (test_sys) {
299 LOG(LS_INFO) << "SystemLoad 2 CPU Busy: "
300 << std::setiosflags(std::ios_base::fixed)
301 << std::setprecision(2) << std::setw(6) << sys_twobusy;
302 EXPECT_GE(sys_twobusy, 0.f);
303 EXPECT_LE(sys_twobusy, static_cast<float>(cpus));
304 }
305 }
306
307 // Test5: CpuSampler with idle process after being busy.
308 Thread::SleepMs(kSettleTime);
309 sampler.GetProcessLoad();
310 sampler.GetSystemLoad();
311
312 Thread::SleepMs(kIdleTime);
313
314 if (test_proc) {
315 proc_idle = sampler.GetProcessLoad();
316 }
317 if (test_sys) {
318 sys_idle = sampler.GetSystemLoad();
319 }
320 if (test_proc) {
321 LOG(LS_INFO) << "ProcessLoad Idle: "
322 << std::setiosflags(std::ios_base::fixed)
323 << std::setprecision(2) << std::setw(6) << proc_idle;
324 EXPECT_GE(proc_idle, 0.f);
325 EXPECT_LE(proc_idle, proc_busy);
326 }
327 if (test_sys) {
328 LOG(LS_INFO) << "SystemLoad Idle: "
329 << std::setiosflags(std::ios_base::fixed)
330 << std::setprecision(2) << std::setw(6) << sys_idle;
331 EXPECT_GE(sys_idle, 0.f);
332 EXPECT_LE(sys_idle, static_cast<float>(cpus));
333 }
334}
335
336TEST(CpuMonitorTest, TestCpus) {
337 CpuSampler sampler;
338 EXPECT_TRUE(sampler.Init());
339 int current_cpus = sampler.GetCurrentCpus();
340 int cpus = sampler.GetMaxCpus();
341 LOG(LS_INFO) << "Current Cpus: " << std::setw(9) << current_cpus;
342 LOG(LS_INFO) << "Maximum Cpus: " << std::setw(9) << cpus;
343 EXPECT_GT(cpus, 0);
344 EXPECT_LE(cpus, kMaxCpus);
345 EXPECT_GT(current_cpus, 0);
346 EXPECT_LE(current_cpus, cpus);
347}
348
349#ifdef WIN32
350// Tests overall system CpuSampler using legacy OS fallback code if applicable.
351TEST(CpuMonitorTest, TestGetSystemLoadForceFallback) {
352 TestCpuSampler(false, true, true);
353}
354#endif
355
356// Tests both process and system functions in use at same time.
357TEST(CpuMonitorTest, TestGetBothLoad) {
358 TestCpuSampler(true, true, false);
359}
360
361// Tests a query less than the interval produces the same value.
362TEST(CpuMonitorTest, TestInterval) {
363 CpuSampler sampler;
364 EXPECT_TRUE(sampler.Init());
365
366 // Test1: Set interval to large value so sampler will not update.
367 sampler.set_load_interval(kLongInterval);
368
369 sampler.GetProcessLoad();
370 sampler.GetSystemLoad();
371
372 float proc_orig = sampler.GetProcessLoad();
373 float sys_orig = sampler.GetSystemLoad();
374
375 Thread::SleepMs(kIdleTime);
376
377 float proc_halftime = sampler.GetProcessLoad();
378 float sys_halftime = sampler.GetSystemLoad();
379
380 EXPECT_EQ(proc_orig, proc_halftime);
381 EXPECT_EQ(sys_orig, sys_halftime);
382}
383
384TEST(CpuMonitorTest, TestCpuMonitor) {
385 CpuMonitor monitor(Thread::Current());
386 CpuLoadListener listener;
387 monitor.SignalUpdate.connect(&listener, &CpuLoadListener::OnCpuLoad);
388 EXPECT_TRUE(monitor.Start(10));
henrike@webrtc.org22a95152014-03-12 19:53:43 +0000389 // We have checked cpu load more than twice.
390 EXPECT_TRUE_WAIT(listener.count() > 2, 1000);
henrike@webrtc.org0e118e72013-07-10 00:45:36 +0000391 EXPECT_GT(listener.current_cpus(), 0);
392 EXPECT_GT(listener.cpus(), 0);
393 EXPECT_GE(listener.process_load(), .0f);
394 EXPECT_GE(listener.system_load(), .0f);
395
396 monitor.Stop();
397 // Wait 20 ms to ake sure all signals are delivered.
398 Thread::Current()->ProcessMessages(20);
399 int old_count = listener.count();
400 Thread::Current()->ProcessMessages(20);
401 // Verfy no more siganls.
402 EXPECT_EQ(old_count, listener.count());
403}
404
405} // namespace talk_base