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
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 | } |