1/*
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  virtual ~BusyThread() {
58    Stop();
59  }
60  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));
389  // We have checked cpu load more than twice.
390  EXPECT_TRUE_WAIT(listener.count() > 2, 1000);
391  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
406