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