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