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 void Run() { 58 Timing time; 59 double busy_time = interval_ * load_ / 100.0; 60 for (;;) { 61 time.BusyWait(busy_time); 62 time.IdleWait(interval_ - busy_time); 63 if (duration_) { 64 duration_ -= interval_; 65 if (duration_ <= 0) { 66 break; 67 } 68 } 69 } 70 } 71 private: 72 double load_; 73 double duration_; 74 double interval_; 75}; 76 77class CpuLoadListener : public sigslot::has_slots<> { 78 public: 79 CpuLoadListener() 80 : current_cpus_(0), 81 cpus_(0), 82 process_load_(.0f), 83 system_load_(.0f), 84 count_(0) { 85 } 86 87 void OnCpuLoad(int current_cpus, int cpus, float proc_load, float sys_load) { 88 current_cpus_ = current_cpus; 89 cpus_ = cpus; 90 process_load_ = proc_load; 91 system_load_ = sys_load; 92 ++count_; 93 } 94 95 int current_cpus() const { return current_cpus_; } 96 int cpus() const { return cpus_; } 97 float process_load() const { return process_load_; } 98 float system_load() const { return system_load_; } 99 int count() const { return count_; } 100 101 private: 102 int current_cpus_; 103 int cpus_; 104 float process_load_; 105 float system_load_; 106 int count_; 107}; 108 109// Set affinity (which cpu to run on), but respecting FLAG_affinity: 110// -1 means no affinity - run on whatever cpu is available. 111// 0 .. N means run on specific cpu. The tool will create N threads and call 112// SetThreadAffinity on 0 to N - 1 as cpu. FLAG_affinity sets the first cpu 113// so the range becomes affinity to affinity + N - 1 114// Note that this function affects Windows scheduling, effectively giving 115// the thread with affinity for a specified CPU more priority on that CPU. 116bool SetThreadAffinity(BusyThread* t, int cpu, int affinity) { 117#ifdef WIN32 118 if (affinity >= 0) { 119 return ::SetThreadAffinityMask(t->GetHandle(), 120 1 << (cpu + affinity)) != FALSE; 121 } 122#endif 123 return true; 124} 125 126bool SetThreadPriority(BusyThread* t, int prio) { 127 if (!prio) { 128 return true; 129 } 130 bool ok = t->SetPriority(static_cast<talk_base::ThreadPriority>(prio)); 131 if (!ok) { 132 std::cout << "Error setting thread priority." << std::endl; 133 } 134 return ok; 135} 136 137int CpuLoad(double cpuload, double duration, int numthreads, 138 int priority, double interval, int affinity) { 139 int ret = 0; 140 std::vector<BusyThread*> threads; 141 for (int i = 0; i < numthreads; ++i) { 142 threads.push_back(new BusyThread(cpuload, duration, interval)); 143 // NOTE(fbarchard): Priority must be done before Start. 144 if (!SetThreadPriority(threads[i], priority) || 145 !threads[i]->Start() || 146 !SetThreadAffinity(threads[i], i, affinity)) { 147 ret = 1; 148 break; 149 } 150 } 151 // Wait on each thread 152 if (ret == 0) { 153 for (int i = 0; i < numthreads; ++i) { 154 threads[i]->Stop(); 155 } 156 } 157 158 for (int i = 0; i < numthreads; ++i) { 159 delete threads[i]; 160 } 161 return ret; 162} 163 164// Make 2 CPUs busy 165static void CpuTwoBusyLoop(int busytime) { 166 CpuLoad(100.0, busytime / 1000.0, 2, 1, 0.050, -1); 167} 168 169// Make 1 CPUs busy 170static void CpuBusyLoop(int busytime) { 171 CpuLoad(100.0, busytime / 1000.0, 1, 1, 0.050, -1); 172} 173 174// Make 1 use half CPU time. 175static void CpuHalfBusyLoop(int busytime) { 176 CpuLoad(50.0, busytime / 1000.0, 1, 1, 0.050, -1); 177} 178 179void TestCpuSampler(bool test_proc, bool test_sys, bool force_fallback) { 180 CpuSampler sampler; 181 sampler.set_force_fallback(force_fallback); 182 EXPECT_TRUE(sampler.Init()); 183 sampler.set_load_interval(100); 184 int cpus = sampler.GetMaxCpus(); 185 186 // Test1: CpuSampler under idle situation. 187 Thread::SleepMs(kSettleTime); 188 sampler.GetProcessLoad(); 189 sampler.GetSystemLoad(); 190 191 Thread::SleepMs(kIdleTime); 192 193 float proc_idle = 0.f, sys_idle = 0.f; 194 if (test_proc) { 195 proc_idle = sampler.GetProcessLoad(); 196 } 197 if (test_sys) { 198 sys_idle = sampler.GetSystemLoad(); 199 } 200 if (test_proc) { 201 LOG(LS_INFO) << "ProcessLoad Idle: " 202 << std::setiosflags(std::ios_base::fixed) 203 << std::setprecision(2) << std::setw(6) << proc_idle; 204 EXPECT_GE(proc_idle, 0.f); 205 EXPECT_LE(proc_idle, static_cast<float>(cpus)); 206 } 207 if (test_sys) { 208 LOG(LS_INFO) << "SystemLoad Idle: " 209 << std::setiosflags(std::ios_base::fixed) 210 << std::setprecision(2) << std::setw(6) << sys_idle; 211 EXPECT_GE(sys_idle, 0.f); 212 EXPECT_LE(sys_idle, static_cast<float>(cpus)); 213 } 214 215 // Test2: CpuSampler with main process at 50% busy. 216 Thread::SleepMs(kSettleTime); 217 sampler.GetProcessLoad(); 218 sampler.GetSystemLoad(); 219 220 CpuHalfBusyLoop(kBusyTime); 221 222 float proc_halfbusy = 0.f, sys_halfbusy = 0.f; 223 if (test_proc) { 224 proc_halfbusy = sampler.GetProcessLoad(); 225 } 226 if (test_sys) { 227 sys_halfbusy = sampler.GetSystemLoad(); 228 } 229 if (test_proc) { 230 LOG(LS_INFO) << "ProcessLoad Halfbusy: " 231 << std::setiosflags(std::ios_base::fixed) 232 << std::setprecision(2) << std::setw(6) << proc_halfbusy; 233 EXPECT_GE(proc_halfbusy, 0.f); 234 EXPECT_LE(proc_halfbusy, static_cast<float>(cpus)); 235 } 236 if (test_sys) { 237 LOG(LS_INFO) << "SystemLoad Halfbusy: " 238 << std::setiosflags(std::ios_base::fixed) 239 << std::setprecision(2) << std::setw(6) << sys_halfbusy; 240 EXPECT_GE(sys_halfbusy, 0.f); 241 EXPECT_LE(sys_halfbusy, static_cast<float>(cpus)); 242 } 243 244 // Test3: CpuSampler with main process busy. 245 Thread::SleepMs(kSettleTime); 246 sampler.GetProcessLoad(); 247 sampler.GetSystemLoad(); 248 249 CpuBusyLoop(kBusyTime); 250 251 float proc_busy = 0.f, sys_busy = 0.f; 252 if (test_proc) { 253 proc_busy = sampler.GetProcessLoad(); 254 } 255 if (test_sys) { 256 sys_busy = sampler.GetSystemLoad(); 257 } 258 if (test_proc) { 259 LOG(LS_INFO) << "ProcessLoad Busy: " 260 << std::setiosflags(std::ios_base::fixed) 261 << std::setprecision(2) << std::setw(6) << proc_busy; 262 EXPECT_GE(proc_busy, 0.f); 263 EXPECT_LE(proc_busy, static_cast<float>(cpus)); 264 } 265 if (test_sys) { 266 LOG(LS_INFO) << "SystemLoad Busy: " 267 << std::setiosflags(std::ios_base::fixed) 268 << std::setprecision(2) << std::setw(6) << sys_busy; 269 EXPECT_GE(sys_busy, 0.f); 270 EXPECT_LE(sys_busy, static_cast<float>(cpus)); 271 } 272 273 // Test4: CpuSampler with 2 cpus process busy. 274 if (cpus >= 2) { 275 Thread::SleepMs(kSettleTime); 276 sampler.GetProcessLoad(); 277 sampler.GetSystemLoad(); 278 279 CpuTwoBusyLoop(kBusyTime); 280 281 float proc_twobusy = 0.f, sys_twobusy = 0.f; 282 if (test_proc) { 283 proc_twobusy = sampler.GetProcessLoad(); 284 } 285 if (test_sys) { 286 sys_twobusy = sampler.GetSystemLoad(); 287 } 288 if (test_proc) { 289 LOG(LS_INFO) << "ProcessLoad 2 CPU Busy:" 290 << std::setiosflags(std::ios_base::fixed) 291 << std::setprecision(2) << std::setw(6) << proc_twobusy; 292 EXPECT_GE(proc_twobusy, 0.f); 293 EXPECT_LE(proc_twobusy, static_cast<float>(cpus)); 294 } 295 if (test_sys) { 296 LOG(LS_INFO) << "SystemLoad 2 CPU Busy: " 297 << std::setiosflags(std::ios_base::fixed) 298 << std::setprecision(2) << std::setw(6) << sys_twobusy; 299 EXPECT_GE(sys_twobusy, 0.f); 300 EXPECT_LE(sys_twobusy, static_cast<float>(cpus)); 301 } 302 } 303 304 // Test5: CpuSampler with idle process after being busy. 305 Thread::SleepMs(kSettleTime); 306 sampler.GetProcessLoad(); 307 sampler.GetSystemLoad(); 308 309 Thread::SleepMs(kIdleTime); 310 311 if (test_proc) { 312 proc_idle = sampler.GetProcessLoad(); 313 } 314 if (test_sys) { 315 sys_idle = sampler.GetSystemLoad(); 316 } 317 if (test_proc) { 318 LOG(LS_INFO) << "ProcessLoad Idle: " 319 << std::setiosflags(std::ios_base::fixed) 320 << std::setprecision(2) << std::setw(6) << proc_idle; 321 EXPECT_GE(proc_idle, 0.f); 322 EXPECT_LE(proc_idle, proc_busy); 323 } 324 if (test_sys) { 325 LOG(LS_INFO) << "SystemLoad Idle: " 326 << std::setiosflags(std::ios_base::fixed) 327 << std::setprecision(2) << std::setw(6) << sys_idle; 328 EXPECT_GE(sys_idle, 0.f); 329 EXPECT_LE(sys_idle, static_cast<float>(cpus)); 330 } 331} 332 333TEST(CpuMonitorTest, TestCpus) { 334 CpuSampler sampler; 335 EXPECT_TRUE(sampler.Init()); 336 int current_cpus = sampler.GetCurrentCpus(); 337 int cpus = sampler.GetMaxCpus(); 338 LOG(LS_INFO) << "Current Cpus: " << std::setw(9) << current_cpus; 339 LOG(LS_INFO) << "Maximum Cpus: " << std::setw(9) << cpus; 340 EXPECT_GT(cpus, 0); 341 EXPECT_LE(cpus, kMaxCpus); 342 EXPECT_GT(current_cpus, 0); 343 EXPECT_LE(current_cpus, cpus); 344} 345 346#ifdef WIN32 347// Tests overall system CpuSampler using legacy OS fallback code if applicable. 348TEST(CpuMonitorTest, TestGetSystemLoadForceFallback) { 349 TestCpuSampler(false, true, true); 350} 351#endif 352 353// Tests both process and system functions in use at same time. 354TEST(CpuMonitorTest, TestGetBothLoad) { 355 TestCpuSampler(true, true, false); 356} 357 358// Tests a query less than the interval produces the same value. 359TEST(CpuMonitorTest, TestInterval) { 360 CpuSampler sampler; 361 EXPECT_TRUE(sampler.Init()); 362 363 // Test1: Set interval to large value so sampler will not update. 364 sampler.set_load_interval(kLongInterval); 365 366 sampler.GetProcessLoad(); 367 sampler.GetSystemLoad(); 368 369 float proc_orig = sampler.GetProcessLoad(); 370 float sys_orig = sampler.GetSystemLoad(); 371 372 Thread::SleepMs(kIdleTime); 373 374 float proc_halftime = sampler.GetProcessLoad(); 375 float sys_halftime = sampler.GetSystemLoad(); 376 377 EXPECT_EQ(proc_orig, proc_halftime); 378 EXPECT_EQ(sys_orig, sys_halftime); 379} 380 381TEST(CpuMonitorTest, TestCpuMonitor) { 382 CpuMonitor monitor(Thread::Current()); 383 CpuLoadListener listener; 384 monitor.SignalUpdate.connect(&listener, &CpuLoadListener::OnCpuLoad); 385 EXPECT_TRUE(monitor.Start(10)); 386 Thread::Current()->ProcessMessages(50); 387 EXPECT_GT(listener.count(), 2); // We have checked cpu load more than twice. 388 EXPECT_GT(listener.current_cpus(), 0); 389 EXPECT_GT(listener.cpus(), 0); 390 EXPECT_GE(listener.process_load(), .0f); 391 EXPECT_GE(listener.system_load(), .0f); 392 393 monitor.Stop(); 394 // Wait 20 ms to ake sure all signals are delivered. 395 Thread::Current()->ProcessMessages(20); 396 int old_count = listener.count(); 397 Thread::Current()->ProcessMessages(20); 398 // Verfy no more siganls. 399 EXPECT_EQ(old_count, listener.count()); 400} 401 402} // namespace talk_base 403