From 649145059788b22a3c4727419f0558eb6967c7ad Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Tue, 29 Oct 2013 15:00:20 +0100 Subject: [PATCH] Bug 902000 - Add a service that monitors the system load. r=jesup --- content/media/webrtc/LoadMonitor.cpp | 342 +++++++++++++++++++++ content/media/webrtc/LoadMonitor.h | 50 +++ content/media/webrtc/MediaEngineWebRTC.cpp | 4 + content/media/webrtc/MediaEngineWebRTC.h | 5 + content/media/webrtc/moz.build | 3 +- 5 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 content/media/webrtc/LoadMonitor.cpp create mode 100644 content/media/webrtc/LoadMonitor.h diff --git a/content/media/webrtc/LoadMonitor.cpp b/content/media/webrtc/LoadMonitor.cpp new file mode 100644 index 00000000000..6cf86de4087 --- /dev/null +++ b/content/media/webrtc/LoadMonitor.cpp @@ -0,0 +1,342 @@ +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "LoadMonitor.h" +#include "nsString.h" +#include "prlog.h" +#include "prtime.h" +#include "prinrval.h" +#include "prsystem.h" + +#include "nsString.h" +#include "nsThreadUtils.h" +#include "nsReadableUtils.h" +#include "nsNetUtil.h" +#include "nsILineInputStream.h" +#include "nsIObserverService.h" +#include "nsIServiceManager.h" + +#include "mozilla/TimeStamp.h" +#include "mozilla/Util.h" +#include "mozilla/Services.h" + +#if defined(ANDROID) || defined(LINUX) || defined(XP_MACOSX) +#include +#include +#endif + +// NSPR_LOG_MODULES=LoadMonitor:5 +PRLogModuleInfo *gLoadMonitorLog = nullptr; +#if defined(PR_LOGGING) +#define LOG(args) PR_LOG(gLoadMonitorLog, PR_LOG_DEBUG, args) +#define LOG_ENABLED() PR_LOG_TEST(gLoadMonitorLog, 4) +#define LOG_MANY_ENABLED() PR_LOG_TEST(gLoadMonitorLog, 5) +#else +#define LOG(args) +#define LOG_ENABLED() (false) +#define LOG_MANY_ENABLED() (false) +#endif + +using namespace mozilla; + +// Update the system load every x milliseconds +static const int kLoadUpdateInterval = 1000; + +NS_IMPL_ISUPPORTS1(LoadMonitor, nsIObserver) + +LoadMonitor::LoadMonitor() + : mLock("LoadMonitor.mLock"), + mCondVar(mLock, "LoadMonitor.mCondVar"), + mShutdownPending(false), + mLoadInfoThread(nullptr), + mSystemLoad(0.0f), + mProcessLoad(0.0f) +{ +} + +LoadMonitor::~LoadMonitor() +{ + Shutdown(); +} + +NS_IMETHODIMP +LoadMonitor::Observe(nsISupports* /* aSubject */, + const char* aTopic, + const PRUnichar* /* aData */) +{ + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(!strcmp("xpcom-shutdown-threads", aTopic), "Bad topic!"); + Shutdown(); + return NS_OK; +} + +class LoadMonitorAddObserver : public nsRunnable +{ +public: + LoadMonitorAddObserver(nsRefPtr loadMonitor) + { + mLoadMonitor = loadMonitor; + } + + NS_IMETHOD Run() + { + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (!observerService) + return NS_ERROR_FAILURE; + + nsresult rv = observerService->AddObserver(mLoadMonitor, "xpcom-shutdown-threads", false); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + +private: + nsRefPtr mLoadMonitor; +}; + +class LoadMonitorRemoveObserver : public nsRunnable +{ +public: + LoadMonitorRemoveObserver(nsRefPtr loadMonitor) + { + mLoadMonitor = loadMonitor; + } + + NS_IMETHOD Run() + { + // remove xpcom shutdown observer + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + + if (observerService) + observerService->RemoveObserver(mLoadMonitor, "xpcom-shutdown-threads"); + + return NS_OK; + } + +private: + nsRefPtr mLoadMonitor; +}; + +void LoadMonitor::Shutdown() +{ + MutexAutoLock lock(mLock); + if (mLoadInfoThread) { + mShutdownPending = true; + mCondVar.Notify(); + + mLoadInfoThread = nullptr; + + nsRefPtr remObsRunner = new LoadMonitorRemoveObserver(this); + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(remObsRunner, NS_DISPATCH_NORMAL); + } else { + remObsRunner->Run(); + } + } +} + +class LoadStats +{ +public: + LoadStats() : + mPrevTotalTimes(0), + mPrevCpuTimes(0), + mPrevLoad(0) {}; + + double GetLoad() { return (double)mPrevLoad; }; + + uint64_t mPrevTotalTimes; + uint64_t mPrevCpuTimes; + float mPrevLoad; // Previous load value. +}; + +class LoadInfo : public mozilla::RefCounted +{ +public: + double GetSystemLoad() { return mSystemLoad.GetLoad(); }; + double GetProcessLoad() { return mProcessLoad.GetLoad(); }; + nsresult UpdateSystemLoad(); + nsresult UpdateProcessLoad(); + +private: + void UpdateCpuLoad(uint64_t current_total_times, + uint64_t current_cpu_times, + LoadStats* loadStat); + LoadStats mSystemLoad; + LoadStats mProcessLoad; +}; + +void LoadInfo::UpdateCpuLoad(uint64_t current_total_times, + uint64_t current_cpu_times, + LoadStats *loadStat) { + float result = 0.0f; + + if (current_total_times < loadStat->mPrevTotalTimes || + current_cpu_times < loadStat->mPrevCpuTimes) { + //LOG(("Current total: %lld old total: %lld", current_total_times, loadStat->mPrevTotalTimes)); + //LOG(("Current cpu: %lld old cpu: %lld", current_cpu_times, loadStat->mPrevCpuTimes)); + LOG(("Inconsistent time values are passed. ignored")); + } else { + const uint64_t cpu_diff = current_cpu_times - loadStat->mPrevCpuTimes; + const uint64_t total_diff = current_total_times - loadStat->mPrevTotalTimes; + if (total_diff > 0) { + result = (float)cpu_diff / (float)total_diff; + loadStat->mPrevLoad = result; + } + } + loadStat->mPrevTotalTimes = current_total_times; + loadStat->mPrevCpuTimes = current_cpu_times; +} + +nsresult LoadInfo::UpdateSystemLoad() +{ +#if defined(LINUX) || defined(ANDROID) + nsCOMPtr procStatFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); + procStatFile->InitWithPath(NS_LITERAL_STRING("/proc/stat")); + + nsCOMPtr fileInputStream; + nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), + procStatFile); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr lineInputStream = do_QueryInterface(fileInputStream, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString buffer; + bool isMore = true; + lineInputStream->ReadLine(buffer, &isMore); + + uint64_t user; + uint64_t nice; + uint64_t system; + uint64_t idle; + if (sscanf(buffer.get(), "cpu %Lu %Lu %Lu %Lu", + &user, &nice, + &system, &idle) != 4) { + LOG(("Error parsing /proc/stat")); + return NS_ERROR_FAILURE; + } + + const uint64_t cpu_times = nice + system + user; + const uint64_t total_times = cpu_times + idle; + + UpdateCpuLoad(total_times, + cpu_times * PR_GetNumberOfProcessors(), + &mSystemLoad); + return NS_OK; +#else + // Not implemented + return NS_OK; +#endif +} + +nsresult LoadInfo::UpdateProcessLoad() { +#if defined(LINUX) || defined(ANDROID) + struct timeval tv; + gettimeofday(&tv, NULL); + const uint64_t total_times = tv.tv_sec * PR_USEC_PER_SEC + tv.tv_usec; + + rusage usage; + if (getrusage(RUSAGE_SELF, &usage) < 0) { + LOG(("getrusage failed")); + return NS_ERROR_FAILURE; + } + + const uint64_t cpu_times = + (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) * PR_USEC_PER_SEC + + usage.ru_utime.tv_usec + usage.ru_stime.tv_usec; + + UpdateCpuLoad(total_times, + cpu_times, + &mProcessLoad); +#endif // defined(LINUX) || defined(ANDROID) + return NS_OK; +} + +class LoadInfoCollectRunner : public nsRunnable +{ +public: + LoadInfoCollectRunner(nsRefPtr loadMonitor) + { + mLoadMonitor = loadMonitor; + mLoadInfo = new LoadInfo(); + mLoadNoiseCounter = 0; + } + + NS_IMETHOD Run() + { + MutexAutoLock lock(mLoadMonitor->mLock); + while (!mLoadMonitor->mShutdownPending) { + mLoadInfo->UpdateSystemLoad(); + mLoadInfo->UpdateProcessLoad(); + float sysLoad = mLoadInfo->GetSystemLoad(); + float procLoad = mLoadInfo->GetProcessLoad(); + if ((++mLoadNoiseCounter % (LOG_MANY_ENABLED() ? 1 : 10)) == 0) { + LOG(("System Load: %f Process Load: %f", sysLoad, procLoad)); + mLoadNoiseCounter = 0; + } + mLoadMonitor->SetSystemLoad(sysLoad); + mLoadMonitor->SetProcessLoad(procLoad); + + mLoadMonitor->mCondVar.Wait(PR_MillisecondsToInterval(kLoadUpdateInterval)); + } + return NS_OK; + } + +private: + RefPtr mLoadInfo; + nsRefPtr mLoadMonitor; + int mLoadNoiseCounter; +}; + +void +LoadMonitor::SetProcessLoad(float load) { + mLock.AssertCurrentThreadOwns(); + mProcessLoad = load; +} + +void +LoadMonitor::SetSystemLoad(float load) { + mLock.AssertCurrentThreadOwns(); + mSystemLoad = load; +} + +float +LoadMonitor::GetProcessLoad() { + MutexAutoLock lock(mLock); + float load = mProcessLoad; + return load; +} + +float +LoadMonitor::GetSystemLoad() { + MutexAutoLock lock(mLock); + float load = mSystemLoad; + return load; +} + +nsresult +LoadMonitor::Init(nsRefPtr &self) +{ +#if defined(PR_LOGGING) + if (!gLoadMonitorLog) + gLoadMonitorLog = PR_NewLogModule("LoadMonitor"); + LOG(("Initializing LoadMonitor")); +#endif + +#if defined(ANDROID) || defined(LINUX) + nsRefPtr addObsRunner = new LoadMonitorAddObserver(self); + NS_DispatchToMainThread(addObsRunner, NS_DISPATCH_NORMAL); + + NS_NewNamedThread("Sys Load Info", getter_AddRefs(mLoadInfoThread)); + + nsRefPtr runner = new LoadInfoCollectRunner(self); + mLoadInfoThread->Dispatch(runner, NS_DISPATCH_NORMAL); +#endif + + return NS_OK; +} diff --git a/content/media/webrtc/LoadMonitor.h b/content/media/webrtc/LoadMonitor.h new file mode 100644 index 00000000000..eb166b4d76c --- /dev/null +++ b/content/media/webrtc/LoadMonitor.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _LOADMONITOR_H_ +#define _LOADMONITOR_H_ + +#include "mozilla/Mutex.h" +#include "mozilla/CondVar.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Atomics.h" +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsIThread.h" +#include "nsIObserver.h" + +class LoadInfoUpdateRunner; +class LoadInfoCollectRunner; + +class LoadMonitor MOZ_FINAL : public nsIObserver +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIOBSERVER + + LoadMonitor(); + ~LoadMonitor(); + + nsresult Init(nsRefPtr &self); + void Shutdown(); + float GetSystemLoad(); + float GetProcessLoad(); + + friend class LoadInfoCollectRunner; + +private: + + void SetProcessLoad(float load); + void SetSystemLoad(float load); + + mozilla::Mutex mLock; + mozilla::CondVar mCondVar; + bool mShutdownPending; + nsCOMPtr mLoadInfoThread; + float mSystemLoad; + float mProcessLoad; +}; + +#endif /* _LOADMONITOR_H_ */ diff --git a/content/media/webrtc/MediaEngineWebRTC.cpp b/content/media/webrtc/MediaEngineWebRTC.cpp index 4d91f431d62..b537f5856ae 100644 --- a/content/media/webrtc/MediaEngineWebRTC.cpp +++ b/content/media/webrtc/MediaEngineWebRTC.cpp @@ -55,6 +55,8 @@ MediaEngineWebRTC::MediaEngineWebRTC() if (compMgr) { compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource); } + mLoadMonitor = new LoadMonitor(); + mLoadMonitor->Init(mLoadMonitor); } #endif @@ -359,6 +361,8 @@ MediaEngineWebRTC::Shutdown() mVideoEngine = nullptr; mVoiceEngine = nullptr; + + mLoadMonitor->Shutdown(); } } diff --git a/content/media/webrtc/MediaEngineWebRTC.h b/content/media/webrtc/MediaEngineWebRTC.h index b43f328008b..15d9dc451f5 100644 --- a/content/media/webrtc/MediaEngineWebRTC.h +++ b/content/media/webrtc/MediaEngineWebRTC.h @@ -26,6 +26,7 @@ #include "AudioSegment.h" #include "StreamBuffer.h" #include "MediaStreamGraph.h" +#include "LoadMonitor.h" // WebRTC library includes follow @@ -356,6 +357,8 @@ public: , mHasTabVideoSource(false) { AsyncLatencyLogger::Get(true)->AddRef(); + mLoadMonitor = new LoadMonitor(); + mLoadMonitor->Init(mLoadMonitor); } #else MediaEngineWebRTC(); @@ -402,6 +405,8 @@ private: nsDOMCameraManager* mCameraManager; uint64_t mWindowId; #endif + + nsRefPtr mLoadMonitor; }; } diff --git a/content/media/webrtc/moz.build b/content/media/webrtc/moz.build index 4b2ae8a3aee..a2e00e09600 100644 --- a/content/media/webrtc/moz.build +++ b/content/media/webrtc/moz.build @@ -14,8 +14,9 @@ EXPORTS += [ ] if CONFIG['MOZ_WEBRTC']: - EXPORTS += ['MediaEngineWebRTC.h'] + EXPORTS += ['LoadMonitor.h', 'MediaEngineWebRTC.h'] SOURCES += [ + 'LoadMonitor.cpp', 'MediaEngineTabVideoSource.cpp', 'MediaEngineWebRTC.cpp', 'MediaEngineWebRTCAudio.cpp',