content/media/webrtc/LoadMonitor.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "LoadMonitor.h"
michael@0 7 #include "LoadManager.h"
michael@0 8 #include "nsString.h"
michael@0 9 #include "prlog.h"
michael@0 10 #include "prtime.h"
michael@0 11 #include "prinrval.h"
michael@0 12 #include "prsystem.h"
michael@0 13 #include "prprf.h"
michael@0 14
michael@0 15 #include "nsString.h"
michael@0 16 #include "nsThreadUtils.h"
michael@0 17 #include "nsReadableUtils.h"
michael@0 18 #include "nsNetUtil.h"
michael@0 19 #include "nsILineInputStream.h"
michael@0 20 #include "nsIObserverService.h"
michael@0 21 #include "nsIServiceManager.h"
michael@0 22
michael@0 23 #include "mozilla/TimeStamp.h"
michael@0 24 #include "mozilla/Services.h"
michael@0 25
michael@0 26 #if defined(ANDROID) || defined(LINUX)
michael@0 27 #include <sys/time.h>
michael@0 28 #include <sys/resource.h>
michael@0 29 #include <unistd.h>
michael@0 30 #endif
michael@0 31
michael@0 32 // NSPR_LOG_MODULES=LoadManager:5
michael@0 33 #undef LOG
michael@0 34 #undef LOG_ENABLED
michael@0 35 #if defined(PR_LOGGING)
michael@0 36 #define LOG(args) PR_LOG(gLoadManagerLog, PR_LOG_DEBUG, args)
michael@0 37 #define LOG_ENABLED() PR_LOG_TEST(gLoadManagerLog, 4)
michael@0 38 #define LOG_MANY_ENABLED() PR_LOG_TEST(gLoadManagerLog, 5)
michael@0 39 #else
michael@0 40 #define LOG(args)
michael@0 41 #define LOG_ENABLED() (false)
michael@0 42 #define LOG_MANY_ENABLED() (false)
michael@0 43 #endif
michael@0 44
michael@0 45 namespace mozilla {
michael@0 46
michael@0 47 NS_IMPL_ISUPPORTS(LoadMonitor, nsIObserver)
michael@0 48
michael@0 49 LoadMonitor::LoadMonitor(int aLoadUpdateInterval)
michael@0 50 : mLoadUpdateInterval(aLoadUpdateInterval),
michael@0 51 mLock("LoadMonitor.mLock"),
michael@0 52 mCondVar(mLock, "LoadMonitor.mCondVar"),
michael@0 53 mShutdownPending(false),
michael@0 54 mLoadInfoThread(nullptr),
michael@0 55 mSystemLoad(0.0f),
michael@0 56 mProcessLoad(0.0f),
michael@0 57 mLoadNotificationCallback(nullptr)
michael@0 58 {
michael@0 59 }
michael@0 60
michael@0 61 LoadMonitor::~LoadMonitor()
michael@0 62 {
michael@0 63 Shutdown();
michael@0 64 }
michael@0 65
michael@0 66 NS_IMETHODIMP
michael@0 67 LoadMonitor::Observe(nsISupports* /* aSubject */,
michael@0 68 const char* aTopic,
michael@0 69 const char16_t* /* aData */)
michael@0 70 {
michael@0 71 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
michael@0 72 MOZ_ASSERT(!strcmp("xpcom-shutdown-threads", aTopic), "Bad topic!");
michael@0 73 Shutdown();
michael@0 74 return NS_OK;
michael@0 75 }
michael@0 76
michael@0 77 class LoadMonitorAddObserver : public nsRunnable
michael@0 78 {
michael@0 79 public:
michael@0 80 LoadMonitorAddObserver(nsRefPtr<LoadMonitor> loadMonitor)
michael@0 81 {
michael@0 82 mLoadMonitor = loadMonitor;
michael@0 83 }
michael@0 84
michael@0 85 NS_IMETHOD Run()
michael@0 86 {
michael@0 87 nsCOMPtr<nsIObserverService> observerService =
michael@0 88 mozilla::services::GetObserverService();
michael@0 89 if (!observerService)
michael@0 90 return NS_ERROR_FAILURE;
michael@0 91
michael@0 92 nsresult rv = observerService->AddObserver(mLoadMonitor, "xpcom-shutdown-threads", false);
michael@0 93 NS_ENSURE_SUCCESS(rv, rv);
michael@0 94
michael@0 95 return NS_OK;
michael@0 96 }
michael@0 97
michael@0 98 private:
michael@0 99 nsRefPtr<LoadMonitor> mLoadMonitor;
michael@0 100 };
michael@0 101
michael@0 102 class LoadMonitorRemoveObserver : public nsRunnable
michael@0 103 {
michael@0 104 public:
michael@0 105 LoadMonitorRemoveObserver(nsRefPtr<LoadMonitor> loadMonitor)
michael@0 106 {
michael@0 107 mLoadMonitor = loadMonitor;
michael@0 108 }
michael@0 109
michael@0 110 NS_IMETHOD Run()
michael@0 111 {
michael@0 112 // remove xpcom shutdown observer
michael@0 113 nsCOMPtr<nsIObserverService> observerService =
michael@0 114 mozilla::services::GetObserverService();
michael@0 115
michael@0 116 if (observerService)
michael@0 117 observerService->RemoveObserver(mLoadMonitor, "xpcom-shutdown-threads");
michael@0 118
michael@0 119 return NS_OK;
michael@0 120 }
michael@0 121
michael@0 122 private:
michael@0 123 nsRefPtr<LoadMonitor> mLoadMonitor;
michael@0 124 };
michael@0 125
michael@0 126 void LoadMonitor::Shutdown()
michael@0 127 {
michael@0 128 MutexAutoLock lock(mLock);
michael@0 129 if (mLoadInfoThread) {
michael@0 130 mShutdownPending = true;
michael@0 131 mCondVar.Notify();
michael@0 132
michael@0 133 mLoadInfoThread = nullptr;
michael@0 134
michael@0 135 nsRefPtr<LoadMonitorRemoveObserver> remObsRunner = new LoadMonitorRemoveObserver(this);
michael@0 136 if (!NS_IsMainThread()) {
michael@0 137 NS_DispatchToMainThread(remObsRunner, NS_DISPATCH_NORMAL);
michael@0 138 } else {
michael@0 139 remObsRunner->Run();
michael@0 140 }
michael@0 141 }
michael@0 142 }
michael@0 143
michael@0 144 class LoadStats
michael@0 145 {
michael@0 146 public:
michael@0 147 LoadStats() :
michael@0 148 mPrevTotalTimes(0),
michael@0 149 mPrevCpuTimes(0),
michael@0 150 mPrevLoad(0) {};
michael@0 151
michael@0 152 double GetLoad() { return (double)mPrevLoad; };
michael@0 153
michael@0 154 uint64_t mPrevTotalTimes;
michael@0 155 uint64_t mPrevCpuTimes;
michael@0 156 float mPrevLoad; // Previous load value.
michael@0 157 };
michael@0 158
michael@0 159 class LoadInfo : public mozilla::RefCounted<LoadInfo>
michael@0 160 {
michael@0 161 public:
michael@0 162 MOZ_DECLARE_REFCOUNTED_TYPENAME(LoadInfo)
michael@0 163 LoadInfo(int aLoadUpdateInterval);
michael@0 164 double GetSystemLoad() { return mSystemLoad.GetLoad(); };
michael@0 165 double GetProcessLoad() { return mProcessLoad.GetLoad(); };
michael@0 166 nsresult UpdateSystemLoad();
michael@0 167 nsresult UpdateProcessLoad();
michael@0 168
michael@0 169 private:
michael@0 170 void UpdateCpuLoad(uint64_t ticks_per_interval,
michael@0 171 uint64_t current_total_times,
michael@0 172 uint64_t current_cpu_times,
michael@0 173 LoadStats* loadStat);
michael@0 174 LoadStats mSystemLoad;
michael@0 175 LoadStats mProcessLoad;
michael@0 176 uint64_t mTicksPerInterval;
michael@0 177 int mLoadUpdateInterval;
michael@0 178 };
michael@0 179
michael@0 180 LoadInfo::LoadInfo(int aLoadUpdateInterval)
michael@0 181 : mLoadUpdateInterval(aLoadUpdateInterval)
michael@0 182 {
michael@0 183 #if defined(ANDROID) || defined(LINUX)
michael@0 184 mTicksPerInterval = (sysconf(_SC_CLK_TCK) * mLoadUpdateInterval) / 1000;
michael@0 185 #endif
michael@0 186 }
michael@0 187
michael@0 188 void LoadInfo::UpdateCpuLoad(uint64_t ticks_per_interval,
michael@0 189 uint64_t current_total_times,
michael@0 190 uint64_t current_cpu_times,
michael@0 191 LoadStats *loadStat) {
michael@0 192
michael@0 193 // Check if we get an inconsistent number of ticks.
michael@0 194 if (((current_total_times - loadStat->mPrevTotalTimes)
michael@0 195 > (ticks_per_interval * 10))
michael@0 196 || current_total_times < loadStat->mPrevTotalTimes
michael@0 197 || current_cpu_times < loadStat->mPrevCpuTimes) {
michael@0 198 // Bug at least on the Nexus 4 and Galaxy S4
michael@0 199 // https://code.google.com/p/android/issues/detail?id=41630
michael@0 200 // We do need to update our previous times, or we can get stuck
michael@0 201 // when there is a blip upwards and then we get a bunch of consecutive
michael@0 202 // lower times. Just skip the load calculation.
michael@0 203 LOG(("Inconsistent time values are passed. ignored"));
michael@0 204 // Try to recover next tick
michael@0 205 loadStat->mPrevTotalTimes = current_total_times;
michael@0 206 loadStat->mPrevCpuTimes = current_cpu_times;
michael@0 207 return;
michael@0 208 }
michael@0 209
michael@0 210 const uint64_t cpu_diff = current_cpu_times - loadStat->mPrevCpuTimes;
michael@0 211 const uint64_t total_diff = current_total_times - loadStat->mPrevTotalTimes;
michael@0 212 if (total_diff > 0) {
michael@0 213 float result = (float)cpu_diff / (float)total_diff;
michael@0 214 loadStat->mPrevLoad = result;
michael@0 215 }
michael@0 216 loadStat->mPrevTotalTimes = current_total_times;
michael@0 217 loadStat->mPrevCpuTimes = current_cpu_times;
michael@0 218 }
michael@0 219
michael@0 220 nsresult LoadInfo::UpdateSystemLoad()
michael@0 221 {
michael@0 222 #if defined(LINUX) || defined(ANDROID)
michael@0 223 nsCOMPtr<nsIFile> procStatFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
michael@0 224 procStatFile->InitWithPath(NS_LITERAL_STRING("/proc/stat"));
michael@0 225
michael@0 226 nsCOMPtr<nsIInputStream> fileInputStream;
michael@0 227 nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
michael@0 228 procStatFile);
michael@0 229 NS_ENSURE_SUCCESS(rv, rv);
michael@0 230
michael@0 231 nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
michael@0 232 NS_ENSURE_SUCCESS(rv, rv);
michael@0 233
michael@0 234 nsAutoCString buffer;
michael@0 235 bool isMore = true;
michael@0 236 lineInputStream->ReadLine(buffer, &isMore);
michael@0 237
michael@0 238 uint64_t user;
michael@0 239 uint64_t nice;
michael@0 240 uint64_t system;
michael@0 241 uint64_t idle;
michael@0 242 if (PR_sscanf(buffer.get(), "cpu %llu %llu %llu %llu",
michael@0 243 &user, &nice,
michael@0 244 &system, &idle) != 4) {
michael@0 245 LOG(("Error parsing /proc/stat"));
michael@0 246 return NS_ERROR_FAILURE;
michael@0 247 }
michael@0 248
michael@0 249 const uint64_t cpu_times = nice + system + user;
michael@0 250 const uint64_t total_times = cpu_times + idle;
michael@0 251
michael@0 252 UpdateCpuLoad(mTicksPerInterval,
michael@0 253 total_times,
michael@0 254 cpu_times,
michael@0 255 &mSystemLoad);
michael@0 256 return NS_OK;
michael@0 257 #else
michael@0 258 // Not implemented
michael@0 259 return NS_OK;
michael@0 260 #endif
michael@0 261 }
michael@0 262
michael@0 263 nsresult LoadInfo::UpdateProcessLoad() {
michael@0 264 #if defined(LINUX) || defined(ANDROID)
michael@0 265 struct timeval tv;
michael@0 266 gettimeofday(&tv, nullptr);
michael@0 267 const uint64_t total_times = tv.tv_sec * PR_USEC_PER_SEC + tv.tv_usec;
michael@0 268
michael@0 269 rusage usage;
michael@0 270 if (getrusage(RUSAGE_SELF, &usage) < 0) {
michael@0 271 LOG(("getrusage failed"));
michael@0 272 return NS_ERROR_FAILURE;
michael@0 273 }
michael@0 274
michael@0 275 const uint64_t cpu_times =
michael@0 276 (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) * PR_USEC_PER_SEC +
michael@0 277 usage.ru_utime.tv_usec + usage.ru_stime.tv_usec;
michael@0 278
michael@0 279 UpdateCpuLoad(PR_USEC_PER_MSEC * mLoadUpdateInterval,
michael@0 280 total_times,
michael@0 281 cpu_times,
michael@0 282 &mProcessLoad);
michael@0 283 #endif // defined(LINUX) || defined(ANDROID)
michael@0 284 return NS_OK;
michael@0 285 }
michael@0 286
michael@0 287 class LoadInfoCollectRunner : public nsRunnable
michael@0 288 {
michael@0 289 public:
michael@0 290 LoadInfoCollectRunner(nsRefPtr<LoadMonitor> loadMonitor,
michael@0 291 int aLoadUpdateInterval)
michael@0 292 : mLoadUpdateInterval(aLoadUpdateInterval),
michael@0 293 mLoadNoiseCounter(0)
michael@0 294 {
michael@0 295 mLoadMonitor = loadMonitor;
michael@0 296 mLoadInfo = new LoadInfo(mLoadUpdateInterval);
michael@0 297 }
michael@0 298
michael@0 299 NS_IMETHOD Run()
michael@0 300 {
michael@0 301 MutexAutoLock lock(mLoadMonitor->mLock);
michael@0 302 while (!mLoadMonitor->mShutdownPending) {
michael@0 303 mLoadInfo->UpdateSystemLoad();
michael@0 304 mLoadInfo->UpdateProcessLoad();
michael@0 305 float sysLoad = mLoadInfo->GetSystemLoad();
michael@0 306 float procLoad = mLoadInfo->GetProcessLoad();
michael@0 307 if ((++mLoadNoiseCounter % (LOG_MANY_ENABLED() ? 1 : 10)) == 0) {
michael@0 308 LOG(("System Load: %f Process Load: %f", sysLoad, procLoad));
michael@0 309 mLoadNoiseCounter = 0;
michael@0 310 }
michael@0 311 mLoadMonitor->SetSystemLoad(sysLoad);
michael@0 312 mLoadMonitor->SetProcessLoad(procLoad);
michael@0 313 mLoadMonitor->FireCallbacks();
michael@0 314
michael@0 315 mLoadMonitor->mCondVar.Wait(PR_MillisecondsToInterval(mLoadUpdateInterval));
michael@0 316 }
michael@0 317 return NS_OK;
michael@0 318 }
michael@0 319
michael@0 320 private:
michael@0 321 RefPtr<LoadInfo> mLoadInfo;
michael@0 322 nsRefPtr<LoadMonitor> mLoadMonitor;
michael@0 323 int mLoadUpdateInterval;
michael@0 324 int mLoadNoiseCounter;
michael@0 325 };
michael@0 326
michael@0 327 void
michael@0 328 LoadMonitor::SetProcessLoad(float load) {
michael@0 329 mLock.AssertCurrentThreadOwns();
michael@0 330 mProcessLoad = load;
michael@0 331 }
michael@0 332
michael@0 333 void
michael@0 334 LoadMonitor::SetSystemLoad(float load) {
michael@0 335 mLock.AssertCurrentThreadOwns();
michael@0 336 mSystemLoad = load;
michael@0 337 }
michael@0 338
michael@0 339 float
michael@0 340 LoadMonitor::GetProcessLoad() {
michael@0 341 MutexAutoLock lock(mLock);
michael@0 342 float load = mProcessLoad;
michael@0 343 return load;
michael@0 344 }
michael@0 345
michael@0 346 void
michael@0 347 LoadMonitor::FireCallbacks() {
michael@0 348 if (mLoadNotificationCallback) {
michael@0 349 mLoadNotificationCallback->LoadChanged(mSystemLoad, mProcessLoad);
michael@0 350 }
michael@0 351 }
michael@0 352
michael@0 353 float
michael@0 354 LoadMonitor::GetSystemLoad() {
michael@0 355 MutexAutoLock lock(mLock);
michael@0 356 float load = mSystemLoad;
michael@0 357 return load;
michael@0 358 }
michael@0 359
michael@0 360 nsresult
michael@0 361 LoadMonitor::Init(nsRefPtr<LoadMonitor> &self)
michael@0 362 {
michael@0 363 LOG(("Initializing LoadMonitor"));
michael@0 364
michael@0 365 #if defined(ANDROID) || defined(LINUX)
michael@0 366 nsRefPtr<LoadMonitorAddObserver> addObsRunner = new LoadMonitorAddObserver(self);
michael@0 367 NS_DispatchToMainThread(addObsRunner, NS_DISPATCH_NORMAL);
michael@0 368
michael@0 369 NS_NewNamedThread("Sys Load Info", getter_AddRefs(mLoadInfoThread));
michael@0 370
michael@0 371 nsRefPtr<LoadInfoCollectRunner> runner =
michael@0 372 new LoadInfoCollectRunner(self, mLoadUpdateInterval);
michael@0 373 mLoadInfoThread->Dispatch(runner, NS_DISPATCH_NORMAL);
michael@0 374 #endif
michael@0 375
michael@0 376 return NS_OK;
michael@0 377 }
michael@0 378
michael@0 379 void
michael@0 380 LoadMonitor::SetLoadChangeCallback(LoadNotificationCallback* aCallback)
michael@0 381 {
michael@0 382 mLoadNotificationCallback = aCallback;
michael@0 383 }
michael@0 384
michael@0 385 }

mercurial