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