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