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 #include "webrtc/test/testsupport/gtest_disable.h"
     27 
     28 namespace rtc {
     29 
     30 static const int kMaxCpus = 1024;
     31 static const int kSettleTime = 100;  // Amount of time to between tests.
     32 static const int kIdleTime = 500;  // Amount of time to be idle in ms.
     33 static const int kBusyTime = 1000;  // Amount of time to be busy in ms.
     34 static const int kLongInterval = 2000;  // Interval longer than busy times
     35 
     36 class 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 
     64 class 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.
    103 bool 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 
    113 bool 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 
    124 int 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
    152 static void CpuTwoBusyLoop(int busytime) {
    153   CpuLoad(100.0, busytime / 1000.0, 2, 1, 0.050, -1);
    154 }
    155 
    156 // Make 1 CPUs busy
    157 static 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.
    162 static void CpuHalfBusyLoop(int busytime) {
    163   CpuLoad(50.0, busytime / 1000.0, 1, 1, 0.050, -1);
    164 }
    165 
    166 void 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 
    320 TEST(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.
    335 TEST(CpuMonitorTest, TestGetSystemLoadForceFallback) {
    336   TestCpuSampler(false, true, true);
    337 }
    338 #endif
    339 
    340 // Tests both process and system functions in use at same time.
    341 TEST(CpuMonitorTest, DISABLED_ON_MAC(TestGetBothLoad)) {
    342   TestCpuSampler(true, true, false);
    343 }
    344 
    345 // Tests a query less than the interval produces the same value.
    346 TEST(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 
    368 TEST(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