Home | History | Annotate | Download | only in base
      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 
     27 namespace rtc {
     28 
     29 static const int kMaxCpus = 1024;
     30 static const int kSettleTime = 100;  // Amount of time to between tests.
     31 static const int kIdleTime = 500;  // Amount of time to be idle in ms.
     32 static const int kBusyTime = 1000;  // Amount of time to be busy in ms.
     33 static const int kLongInterval = 2000;  // Interval longer than busy times
     34 
     35 class 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 
     63 class 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.
    102 bool 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 
    112 bool 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 
    123 int 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
    151 static void CpuTwoBusyLoop(int busytime) {
    152   CpuLoad(100.0, busytime / 1000.0, 2, 1, 0.050, -1);
    153 }
    154 
    155 // Make 1 CPUs busy
    156 static 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.
    161 static void CpuHalfBusyLoop(int busytime) {
    162   CpuLoad(50.0, busytime / 1000.0, 1, 1, 0.050, -1);
    163 }
    164 
    165 void 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 
    319 TEST(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.
    334 TEST(CpuMonitorTest, TestGetSystemLoadForceFallback) {
    335   TestCpuSampler(false, true, true);
    336 }
    337 #endif
    338 
    339 // Tests both process and system functions in use at same time.
    340 TEST(CpuMonitorTest, TestGetBothLoad) {
    341   TestCpuSampler(true, true, false);
    342 }
    343 
    344 // Tests a query less than the interval produces the same value.
    345 TEST(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 
    367 TEST(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
    389