1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webrtc/LoadMonitor.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,385 @@ 1.4 +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "LoadMonitor.h" 1.10 +#include "LoadManager.h" 1.11 +#include "nsString.h" 1.12 +#include "prlog.h" 1.13 +#include "prtime.h" 1.14 +#include "prinrval.h" 1.15 +#include "prsystem.h" 1.16 +#include "prprf.h" 1.17 + 1.18 +#include "nsString.h" 1.19 +#include "nsThreadUtils.h" 1.20 +#include "nsReadableUtils.h" 1.21 +#include "nsNetUtil.h" 1.22 +#include "nsILineInputStream.h" 1.23 +#include "nsIObserverService.h" 1.24 +#include "nsIServiceManager.h" 1.25 + 1.26 +#include "mozilla/TimeStamp.h" 1.27 +#include "mozilla/Services.h" 1.28 + 1.29 +#if defined(ANDROID) || defined(LINUX) 1.30 +#include <sys/time.h> 1.31 +#include <sys/resource.h> 1.32 +#include <unistd.h> 1.33 +#endif 1.34 + 1.35 +// NSPR_LOG_MODULES=LoadManager:5 1.36 +#undef LOG 1.37 +#undef LOG_ENABLED 1.38 +#if defined(PR_LOGGING) 1.39 +#define LOG(args) PR_LOG(gLoadManagerLog, PR_LOG_DEBUG, args) 1.40 +#define LOG_ENABLED() PR_LOG_TEST(gLoadManagerLog, 4) 1.41 +#define LOG_MANY_ENABLED() PR_LOG_TEST(gLoadManagerLog, 5) 1.42 +#else 1.43 +#define LOG(args) 1.44 +#define LOG_ENABLED() (false) 1.45 +#define LOG_MANY_ENABLED() (false) 1.46 +#endif 1.47 + 1.48 +namespace mozilla { 1.49 + 1.50 +NS_IMPL_ISUPPORTS(LoadMonitor, nsIObserver) 1.51 + 1.52 +LoadMonitor::LoadMonitor(int aLoadUpdateInterval) 1.53 + : mLoadUpdateInterval(aLoadUpdateInterval), 1.54 + mLock("LoadMonitor.mLock"), 1.55 + mCondVar(mLock, "LoadMonitor.mCondVar"), 1.56 + mShutdownPending(false), 1.57 + mLoadInfoThread(nullptr), 1.58 + mSystemLoad(0.0f), 1.59 + mProcessLoad(0.0f), 1.60 + mLoadNotificationCallback(nullptr) 1.61 +{ 1.62 +} 1.63 + 1.64 +LoadMonitor::~LoadMonitor() 1.65 +{ 1.66 + Shutdown(); 1.67 +} 1.68 + 1.69 +NS_IMETHODIMP 1.70 +LoadMonitor::Observe(nsISupports* /* aSubject */, 1.71 + const char* aTopic, 1.72 + const char16_t* /* aData */) 1.73 +{ 1.74 + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); 1.75 + MOZ_ASSERT(!strcmp("xpcom-shutdown-threads", aTopic), "Bad topic!"); 1.76 + Shutdown(); 1.77 + return NS_OK; 1.78 +} 1.79 + 1.80 +class LoadMonitorAddObserver : public nsRunnable 1.81 +{ 1.82 +public: 1.83 + LoadMonitorAddObserver(nsRefPtr<LoadMonitor> loadMonitor) 1.84 + { 1.85 + mLoadMonitor = loadMonitor; 1.86 + } 1.87 + 1.88 + NS_IMETHOD Run() 1.89 + { 1.90 + nsCOMPtr<nsIObserverService> observerService = 1.91 + mozilla::services::GetObserverService(); 1.92 + if (!observerService) 1.93 + return NS_ERROR_FAILURE; 1.94 + 1.95 + nsresult rv = observerService->AddObserver(mLoadMonitor, "xpcom-shutdown-threads", false); 1.96 + NS_ENSURE_SUCCESS(rv, rv); 1.97 + 1.98 + return NS_OK; 1.99 + } 1.100 + 1.101 +private: 1.102 + nsRefPtr<LoadMonitor> mLoadMonitor; 1.103 +}; 1.104 + 1.105 +class LoadMonitorRemoveObserver : public nsRunnable 1.106 +{ 1.107 +public: 1.108 + LoadMonitorRemoveObserver(nsRefPtr<LoadMonitor> loadMonitor) 1.109 + { 1.110 + mLoadMonitor = loadMonitor; 1.111 + } 1.112 + 1.113 + NS_IMETHOD Run() 1.114 + { 1.115 + // remove xpcom shutdown observer 1.116 + nsCOMPtr<nsIObserverService> observerService = 1.117 + mozilla::services::GetObserverService(); 1.118 + 1.119 + if (observerService) 1.120 + observerService->RemoveObserver(mLoadMonitor, "xpcom-shutdown-threads"); 1.121 + 1.122 + return NS_OK; 1.123 + } 1.124 + 1.125 +private: 1.126 + nsRefPtr<LoadMonitor> mLoadMonitor; 1.127 +}; 1.128 + 1.129 +void LoadMonitor::Shutdown() 1.130 +{ 1.131 + MutexAutoLock lock(mLock); 1.132 + if (mLoadInfoThread) { 1.133 + mShutdownPending = true; 1.134 + mCondVar.Notify(); 1.135 + 1.136 + mLoadInfoThread = nullptr; 1.137 + 1.138 + nsRefPtr<LoadMonitorRemoveObserver> remObsRunner = new LoadMonitorRemoveObserver(this); 1.139 + if (!NS_IsMainThread()) { 1.140 + NS_DispatchToMainThread(remObsRunner, NS_DISPATCH_NORMAL); 1.141 + } else { 1.142 + remObsRunner->Run(); 1.143 + } 1.144 + } 1.145 +} 1.146 + 1.147 +class LoadStats 1.148 +{ 1.149 +public: 1.150 + LoadStats() : 1.151 + mPrevTotalTimes(0), 1.152 + mPrevCpuTimes(0), 1.153 + mPrevLoad(0) {}; 1.154 + 1.155 + double GetLoad() { return (double)mPrevLoad; }; 1.156 + 1.157 + uint64_t mPrevTotalTimes; 1.158 + uint64_t mPrevCpuTimes; 1.159 + float mPrevLoad; // Previous load value. 1.160 +}; 1.161 + 1.162 +class LoadInfo : public mozilla::RefCounted<LoadInfo> 1.163 +{ 1.164 +public: 1.165 + MOZ_DECLARE_REFCOUNTED_TYPENAME(LoadInfo) 1.166 + LoadInfo(int aLoadUpdateInterval); 1.167 + double GetSystemLoad() { return mSystemLoad.GetLoad(); }; 1.168 + double GetProcessLoad() { return mProcessLoad.GetLoad(); }; 1.169 + nsresult UpdateSystemLoad(); 1.170 + nsresult UpdateProcessLoad(); 1.171 + 1.172 +private: 1.173 + void UpdateCpuLoad(uint64_t ticks_per_interval, 1.174 + uint64_t current_total_times, 1.175 + uint64_t current_cpu_times, 1.176 + LoadStats* loadStat); 1.177 + LoadStats mSystemLoad; 1.178 + LoadStats mProcessLoad; 1.179 + uint64_t mTicksPerInterval; 1.180 + int mLoadUpdateInterval; 1.181 +}; 1.182 + 1.183 +LoadInfo::LoadInfo(int aLoadUpdateInterval) 1.184 + : mLoadUpdateInterval(aLoadUpdateInterval) 1.185 +{ 1.186 +#if defined(ANDROID) || defined(LINUX) 1.187 + mTicksPerInterval = (sysconf(_SC_CLK_TCK) * mLoadUpdateInterval) / 1000; 1.188 +#endif 1.189 +} 1.190 + 1.191 +void LoadInfo::UpdateCpuLoad(uint64_t ticks_per_interval, 1.192 + uint64_t current_total_times, 1.193 + uint64_t current_cpu_times, 1.194 + LoadStats *loadStat) { 1.195 + 1.196 + // Check if we get an inconsistent number of ticks. 1.197 + if (((current_total_times - loadStat->mPrevTotalTimes) 1.198 + > (ticks_per_interval * 10)) 1.199 + || current_total_times < loadStat->mPrevTotalTimes 1.200 + || current_cpu_times < loadStat->mPrevCpuTimes) { 1.201 + // Bug at least on the Nexus 4 and Galaxy S4 1.202 + // https://code.google.com/p/android/issues/detail?id=41630 1.203 + // We do need to update our previous times, or we can get stuck 1.204 + // when there is a blip upwards and then we get a bunch of consecutive 1.205 + // lower times. Just skip the load calculation. 1.206 + LOG(("Inconsistent time values are passed. ignored")); 1.207 + // Try to recover next tick 1.208 + loadStat->mPrevTotalTimes = current_total_times; 1.209 + loadStat->mPrevCpuTimes = current_cpu_times; 1.210 + return; 1.211 + } 1.212 + 1.213 + const uint64_t cpu_diff = current_cpu_times - loadStat->mPrevCpuTimes; 1.214 + const uint64_t total_diff = current_total_times - loadStat->mPrevTotalTimes; 1.215 + if (total_diff > 0) { 1.216 + float result = (float)cpu_diff / (float)total_diff; 1.217 + loadStat->mPrevLoad = result; 1.218 + } 1.219 + loadStat->mPrevTotalTimes = current_total_times; 1.220 + loadStat->mPrevCpuTimes = current_cpu_times; 1.221 +} 1.222 + 1.223 +nsresult LoadInfo::UpdateSystemLoad() 1.224 +{ 1.225 +#if defined(LINUX) || defined(ANDROID) 1.226 + nsCOMPtr<nsIFile> procStatFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); 1.227 + procStatFile->InitWithPath(NS_LITERAL_STRING("/proc/stat")); 1.228 + 1.229 + nsCOMPtr<nsIInputStream> fileInputStream; 1.230 + nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), 1.231 + procStatFile); 1.232 + NS_ENSURE_SUCCESS(rv, rv); 1.233 + 1.234 + nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv); 1.235 + NS_ENSURE_SUCCESS(rv, rv); 1.236 + 1.237 + nsAutoCString buffer; 1.238 + bool isMore = true; 1.239 + lineInputStream->ReadLine(buffer, &isMore); 1.240 + 1.241 + uint64_t user; 1.242 + uint64_t nice; 1.243 + uint64_t system; 1.244 + uint64_t idle; 1.245 + if (PR_sscanf(buffer.get(), "cpu %llu %llu %llu %llu", 1.246 + &user, &nice, 1.247 + &system, &idle) != 4) { 1.248 + LOG(("Error parsing /proc/stat")); 1.249 + return NS_ERROR_FAILURE; 1.250 + } 1.251 + 1.252 + const uint64_t cpu_times = nice + system + user; 1.253 + const uint64_t total_times = cpu_times + idle; 1.254 + 1.255 + UpdateCpuLoad(mTicksPerInterval, 1.256 + total_times, 1.257 + cpu_times, 1.258 + &mSystemLoad); 1.259 + return NS_OK; 1.260 +#else 1.261 + // Not implemented 1.262 + return NS_OK; 1.263 +#endif 1.264 +} 1.265 + 1.266 +nsresult LoadInfo::UpdateProcessLoad() { 1.267 +#if defined(LINUX) || defined(ANDROID) 1.268 + struct timeval tv; 1.269 + gettimeofday(&tv, nullptr); 1.270 + const uint64_t total_times = tv.tv_sec * PR_USEC_PER_SEC + tv.tv_usec; 1.271 + 1.272 + rusage usage; 1.273 + if (getrusage(RUSAGE_SELF, &usage) < 0) { 1.274 + LOG(("getrusage failed")); 1.275 + return NS_ERROR_FAILURE; 1.276 + } 1.277 + 1.278 + const uint64_t cpu_times = 1.279 + (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) * PR_USEC_PER_SEC + 1.280 + usage.ru_utime.tv_usec + usage.ru_stime.tv_usec; 1.281 + 1.282 + UpdateCpuLoad(PR_USEC_PER_MSEC * mLoadUpdateInterval, 1.283 + total_times, 1.284 + cpu_times, 1.285 + &mProcessLoad); 1.286 +#endif // defined(LINUX) || defined(ANDROID) 1.287 + return NS_OK; 1.288 +} 1.289 + 1.290 +class LoadInfoCollectRunner : public nsRunnable 1.291 +{ 1.292 +public: 1.293 + LoadInfoCollectRunner(nsRefPtr<LoadMonitor> loadMonitor, 1.294 + int aLoadUpdateInterval) 1.295 + : mLoadUpdateInterval(aLoadUpdateInterval), 1.296 + mLoadNoiseCounter(0) 1.297 + { 1.298 + mLoadMonitor = loadMonitor; 1.299 + mLoadInfo = new LoadInfo(mLoadUpdateInterval); 1.300 + } 1.301 + 1.302 + NS_IMETHOD Run() 1.303 + { 1.304 + MutexAutoLock lock(mLoadMonitor->mLock); 1.305 + while (!mLoadMonitor->mShutdownPending) { 1.306 + mLoadInfo->UpdateSystemLoad(); 1.307 + mLoadInfo->UpdateProcessLoad(); 1.308 + float sysLoad = mLoadInfo->GetSystemLoad(); 1.309 + float procLoad = mLoadInfo->GetProcessLoad(); 1.310 + if ((++mLoadNoiseCounter % (LOG_MANY_ENABLED() ? 1 : 10)) == 0) { 1.311 + LOG(("System Load: %f Process Load: %f", sysLoad, procLoad)); 1.312 + mLoadNoiseCounter = 0; 1.313 + } 1.314 + mLoadMonitor->SetSystemLoad(sysLoad); 1.315 + mLoadMonitor->SetProcessLoad(procLoad); 1.316 + mLoadMonitor->FireCallbacks(); 1.317 + 1.318 + mLoadMonitor->mCondVar.Wait(PR_MillisecondsToInterval(mLoadUpdateInterval)); 1.319 + } 1.320 + return NS_OK; 1.321 + } 1.322 + 1.323 +private: 1.324 + RefPtr<LoadInfo> mLoadInfo; 1.325 + nsRefPtr<LoadMonitor> mLoadMonitor; 1.326 + int mLoadUpdateInterval; 1.327 + int mLoadNoiseCounter; 1.328 +}; 1.329 + 1.330 +void 1.331 +LoadMonitor::SetProcessLoad(float load) { 1.332 + mLock.AssertCurrentThreadOwns(); 1.333 + mProcessLoad = load; 1.334 +} 1.335 + 1.336 +void 1.337 +LoadMonitor::SetSystemLoad(float load) { 1.338 + mLock.AssertCurrentThreadOwns(); 1.339 + mSystemLoad = load; 1.340 +} 1.341 + 1.342 +float 1.343 +LoadMonitor::GetProcessLoad() { 1.344 + MutexAutoLock lock(mLock); 1.345 + float load = mProcessLoad; 1.346 + return load; 1.347 +} 1.348 + 1.349 +void 1.350 +LoadMonitor::FireCallbacks() { 1.351 + if (mLoadNotificationCallback) { 1.352 + mLoadNotificationCallback->LoadChanged(mSystemLoad, mProcessLoad); 1.353 + } 1.354 +} 1.355 + 1.356 +float 1.357 +LoadMonitor::GetSystemLoad() { 1.358 + MutexAutoLock lock(mLock); 1.359 + float load = mSystemLoad; 1.360 + return load; 1.361 +} 1.362 + 1.363 +nsresult 1.364 +LoadMonitor::Init(nsRefPtr<LoadMonitor> &self) 1.365 +{ 1.366 + LOG(("Initializing LoadMonitor")); 1.367 + 1.368 +#if defined(ANDROID) || defined(LINUX) 1.369 + nsRefPtr<LoadMonitorAddObserver> addObsRunner = new LoadMonitorAddObserver(self); 1.370 + NS_DispatchToMainThread(addObsRunner, NS_DISPATCH_NORMAL); 1.371 + 1.372 + NS_NewNamedThread("Sys Load Info", getter_AddRefs(mLoadInfoThread)); 1.373 + 1.374 + nsRefPtr<LoadInfoCollectRunner> runner = 1.375 + new LoadInfoCollectRunner(self, mLoadUpdateInterval); 1.376 + mLoadInfoThread->Dispatch(runner, NS_DISPATCH_NORMAL); 1.377 +#endif 1.378 + 1.379 + return NS_OK; 1.380 +} 1.381 + 1.382 +void 1.383 +LoadMonitor::SetLoadChangeCallback(LoadNotificationCallback* aCallback) 1.384 +{ 1.385 + mLoadNotificationCallback = aCallback; 1.386 +} 1.387 + 1.388 +}