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