michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsMemoryReporterManager_h__ michael@0: #define nsMemoryReporterManager_h__ michael@0: michael@0: #include "nsIMemoryReporter.h" michael@0: #include "nsITimer.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "nsTHashtable.h" michael@0: #include "nsHashKeys.h" michael@0: michael@0: class nsITimer; michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: class MemoryReport; michael@0: } michael@0: } michael@0: michael@0: class nsMemoryReporterManager : public nsIMemoryReporterManager michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIMEMORYREPORTERMANAGER michael@0: michael@0: nsMemoryReporterManager(); michael@0: virtual ~nsMemoryReporterManager(); michael@0: michael@0: // Gets the memory reporter manager service. michael@0: static nsMemoryReporterManager* GetOrCreate() michael@0: { michael@0: nsCOMPtr imgr = michael@0: do_GetService("@mozilla.org/memory-reporter-manager;1"); michael@0: return static_cast(imgr.get()); michael@0: } michael@0: michael@0: typedef nsTHashtable > StrongReportersTable; michael@0: typedef nsTHashtable > WeakReportersTable; michael@0: michael@0: void IncrementNumChildProcesses(); michael@0: void DecrementNumChildProcesses(); michael@0: michael@0: // Inter-process memory reporting proceeds as follows. michael@0: // michael@0: // - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER) michael@0: // synchronously gets memory reports for the current process, tells all michael@0: // child processes to get memory reports, and sets up some state michael@0: // (mGetReportsState) for when the child processes report back, including a michael@0: // timer. Control then returns to the main event loop. michael@0: // michael@0: // - HandleChildReports() is called (asynchronously) once per child process michael@0: // that reports back. If all child processes report back before time-out, michael@0: // the timer is cancelled. (The number of child processes is part of the michael@0: // saved request state.) michael@0: // michael@0: // - TimeoutCallback() is called (asynchronously) if all the child processes michael@0: // don't respond within the time threshold. michael@0: // michael@0: // - FinishReporting() finishes things off. It is *always* called -- either michael@0: // from HandleChildReports() (if all child processes have reported back) or michael@0: // from TimeoutCallback() (if time-out occurs). michael@0: // michael@0: // All operations occur on the main thread. michael@0: // michael@0: // The above sequence of steps is a "request". A partially-completed request michael@0: // is described as "in flight". michael@0: // michael@0: // Each request has a "generation", a unique number that identifies it. This michael@0: // is used to ensure that each reports from a child process corresponds to michael@0: // the appropriate request from the parent process. (It's easier to michael@0: // implement a generation system than to implement a child report request michael@0: // cancellation mechanism.) michael@0: // michael@0: // Failures are mostly ignored, because it's (a) typically the most sensible michael@0: // thing to do, and (b) often hard to do anything else. The following are michael@0: // the failure cases of note. michael@0: // michael@0: // - If a request is made while the previous request is in flight, the new michael@0: // request is ignored, as per getReports()'s specification. No error is michael@0: // reported, because the previous request will complete soon enough. michael@0: // michael@0: // - If one or more child processes fail to respond within the time limit, michael@0: // things will proceed as if they don't exist. No error is reported, michael@0: // because partial information is better than nothing. michael@0: // michael@0: // - If a child process reports after the time-out occurs, it is ignored. michael@0: // (Generation checking will ensure it is ignored even if a subsequent michael@0: // request is in flight; this is the main use of generations.) No error michael@0: // is reported, because there's nothing sensible to be done about it at michael@0: // this late stage. michael@0: // michael@0: // Now, what what happens if a child process is created/destroyed in the michael@0: // middle of a request? Well, GetReportsState contains a copy of michael@0: // mNumChildProcesses which it uses to determine finished-ness. So... michael@0: // michael@0: // - If a process is created, it won't have received the request for reports, michael@0: // and the GetReportsState's mNumChildProcesses won't account for it. So michael@0: // the reported data will reflect how things were when the request began. michael@0: // michael@0: // - If a process is destroyed before reporting back, we'll just hit the michael@0: // time-out, because we'll have received reports (barring other errors) michael@0: // from N-1 child process. So the reported data will reflect how things michael@0: // are when the request ends. michael@0: // michael@0: // - If a process is destroyed after reporting back, but before all other michael@0: // child processes have reported back, it will be included in the reported michael@0: // data. So the reported data will reflect how things were when the michael@0: // request began. michael@0: // michael@0: // The inconsistencies between these three cases are unfortunate but michael@0: // difficult to avoid. It's enough of an edge case to not be worth doing michael@0: // more. michael@0: // michael@0: void HandleChildReports( michael@0: const uint32_t& generation, michael@0: const InfallibleTArray& aChildReports); michael@0: nsresult FinishReporting(); michael@0: michael@0: // Functions that (a) implement distinguished amounts, and (b) are outside of michael@0: // this module. michael@0: struct AmountFns michael@0: { michael@0: mozilla::InfallibleAmountFn mJSMainRuntimeGCHeap; michael@0: mozilla::InfallibleAmountFn mJSMainRuntimeTemporaryPeak; michael@0: mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem; michael@0: mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsUser; michael@0: michael@0: mozilla::InfallibleAmountFn mImagesContentUsedUncompressed; michael@0: michael@0: mozilla::InfallibleAmountFn mStorageSQLite; michael@0: michael@0: mozilla::InfallibleAmountFn mLowMemoryEventsVirtual; michael@0: mozilla::InfallibleAmountFn mLowMemoryEventsPhysical; michael@0: michael@0: mozilla::InfallibleAmountFn mGhostWindows; michael@0: michael@0: AmountFns() { mozilla::PodZero(this); } michael@0: }; michael@0: AmountFns mAmountFns; michael@0: michael@0: // Convenience function to get RSS easily from other code. This is useful michael@0: // when debugging transient memory spikes with printf instrumentation. michael@0: static int64_t ResidentFast(); michael@0: michael@0: // Functions that measure per-tab memory consumption. michael@0: struct SizeOfTabFns michael@0: { michael@0: mozilla::JSSizeOfTabFn mJS; michael@0: mozilla::NonJSSizeOfTabFn mNonJS; michael@0: michael@0: SizeOfTabFns() michael@0: { michael@0: mozilla::PodZero(this); michael@0: } michael@0: }; michael@0: SizeOfTabFns mSizeOfTabFns; michael@0: michael@0: private: michael@0: nsresult RegisterReporterHelper(nsIMemoryReporter* aReporter, michael@0: bool aForce, bool aStrongRef); michael@0: nsresult StartGettingReports(); michael@0: michael@0: static void TimeoutCallback(nsITimer* aTimer, void* aData); michael@0: // Note: this timeout needs to be long enough to allow for the michael@0: // possibility of DMD reports and/or running on a low-end phone. michael@0: static const uint32_t kTimeoutLengthMS = 50000; michael@0: michael@0: mozilla::Mutex mMutex; michael@0: bool mIsRegistrationBlocked; michael@0: michael@0: StrongReportersTable* mStrongReporters; michael@0: WeakReportersTable* mWeakReporters; michael@0: michael@0: // These two are only used for testing purposes. michael@0: StrongReportersTable* mSavedStrongReporters; michael@0: WeakReportersTable* mSavedWeakReporters; michael@0: michael@0: uint32_t mNumChildProcesses; michael@0: uint32_t mNextGeneration; michael@0: michael@0: struct GetReportsState michael@0: { michael@0: uint32_t mGeneration; michael@0: nsCOMPtr mTimer; michael@0: uint32_t mNumChildProcesses; michael@0: uint32_t mNumChildProcessesCompleted; michael@0: bool mParentDone; michael@0: nsCOMPtr mHandleReport; michael@0: nsCOMPtr mHandleReportData; michael@0: nsCOMPtr mFinishReporting; michael@0: nsCOMPtr mFinishReportingData; michael@0: nsString mDMDDumpIdent; michael@0: michael@0: GetReportsState(uint32_t aGeneration, nsITimer* aTimer, michael@0: uint32_t aNumChildProcesses, michael@0: nsIHandleReportCallback* aHandleReport, michael@0: nsISupports* aHandleReportData, michael@0: nsIFinishReportingCallback* aFinishReporting, michael@0: nsISupports* aFinishReportingData, michael@0: const nsAString &aDMDDumpIdent) michael@0: : mGeneration(aGeneration) michael@0: , mTimer(aTimer) michael@0: , mNumChildProcesses(aNumChildProcesses) michael@0: , mNumChildProcessesCompleted(0) michael@0: , mParentDone(false) michael@0: , mHandleReport(aHandleReport) michael@0: , mHandleReportData(aHandleReportData) michael@0: , mFinishReporting(aFinishReporting) michael@0: , mFinishReportingData(aFinishReportingData) michael@0: , mDMDDumpIdent(aDMDDumpIdent) michael@0: { michael@0: } michael@0: }; michael@0: michael@0: // When this is non-null, a request is in flight. Note: We use manual michael@0: // new/delete for this because its lifetime doesn't match block scope or michael@0: // anything like that. michael@0: GetReportsState* mGetReportsState; michael@0: }; michael@0: michael@0: #define NS_MEMORY_REPORTER_MANAGER_CID \ michael@0: { 0xfb97e4f5, 0x32dd, 0x497a, \ michael@0: { 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } } michael@0: michael@0: #endif // nsMemoryReporterManager_h__