• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
44 namespace talk_base {
45 
46 static const int kMaxCpus = 1024;
47 static const int kSettleTime = 100;  // Amount of time to between tests.
48 static const int kIdleTime = 500;  // Amount of time to be idle in ms.
49 static const int kBusyTime = 1000;  // Amount of time to be busy in ms.
50 static const int kLongInterval = 2000;  // Interval longer than busy times
51 
52 class BusyThread : public talk_base::Thread {
53  public:
BusyThread(double load,double duration,double interval)54   BusyThread(double load, double duration, double interval) :
55     load_(load), duration_(duration), interval_(interval) {
56   }
~BusyThread()57   virtual ~BusyThread() {
58     Stop();
59   }
Run()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 
80 class CpuLoadListener : public sigslot::has_slots<> {
81  public:
CpuLoadListener()82   CpuLoadListener()
83       : current_cpus_(0),
84         cpus_(0),
85         process_load_(.0f),
86         system_load_(.0f),
87         count_(0) {
88   }
89 
OnCpuLoad(int current_cpus,int cpus,float proc_load,float sys_load)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 
current_cpus() const98   int current_cpus() const { return current_cpus_; }
cpus() const99   int cpus() const { return cpus_; }
process_load() const100   float process_load() const { return process_load_; }
system_load() const101   float system_load() const { return system_load_; }
count() const102   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.
SetThreadAffinity(BusyThread * t,int cpu,int affinity)119 bool 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 
SetThreadPriority(BusyThread * t,int prio)129 bool 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 
CpuLoad(double cpuload,double duration,int numthreads,int priority,double interval,int affinity)140 int 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
CpuTwoBusyLoop(int busytime)168 static void CpuTwoBusyLoop(int busytime) {
169   CpuLoad(100.0, busytime / 1000.0, 2, 1, 0.050, -1);
170 }
171 
172 // Make 1 CPUs busy
CpuBusyLoop(int busytime)173 static 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.
CpuHalfBusyLoop(int busytime)178 static void CpuHalfBusyLoop(int busytime) {
179   CpuLoad(50.0, busytime / 1000.0, 1, 1, 0.050, -1);
180 }
181 
TestCpuSampler(bool test_proc,bool test_sys,bool force_fallback)182 void 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 
TEST(CpuMonitorTest,TestCpus)336 TEST(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.
TEST(CpuMonitorTest,TestGetSystemLoadForceFallback)351 TEST(CpuMonitorTest, TestGetSystemLoadForceFallback) {
352   TestCpuSampler(false, true, true);
353 }
354 #endif
355 
356 // Tests both process and system functions in use at same time.
TEST(CpuMonitorTest,TestGetBothLoad)357 TEST(CpuMonitorTest, TestGetBothLoad) {
358   TestCpuSampler(true, true, false);
359 }
360 
361 // Tests a query less than the interval produces the same value.
TEST(CpuMonitorTest,TestInterval)362 TEST(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 
TEST(CpuMonitorTest,TestCpuMonitor)384 TEST(CpuMonitorTest, TestCpuMonitor) {
385   CpuMonitor monitor(Thread::Current());
386   CpuLoadListener listener;
387   monitor.SignalUpdate.connect(&listener, &CpuLoadListener::OnCpuLoad);
388   EXPECT_TRUE(monitor.Start(10));
389   Thread::Current()->ProcessMessages(50);
390   EXPECT_GT(listener.count(), 2);  // We have checked cpu load more than twice.
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