Fri, 16 Jan 2015 04:50:19 +0100
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 }