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

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

mercurial