Home | History | Annotate | Download | only in base
      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:
     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 
     80 class 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.
    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 
    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 
    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
    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
    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.
    178 static void CpuHalfBusyLoop(int busytime) {
    179   CpuLoad(50.0, busytime / 1000.0, 1, 1, 0.050, -1);
    180 }
    181 
    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 
    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.
    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.
    357 TEST(CpuMonitorTest, TestGetBothLoad) {
    358   TestCpuSampler(true, true, false);
    359 }
    360 
    361 // Tests a query less than the interval produces the same value.
    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 
    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   // 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