1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "nsMemoryReporterManager.h"
8 #include "MemoryReportRequest.h"
9 #include "mozilla/ipc/FileDescriptor.h"
10 #include "mozilla/ipc/FileDescriptorUtils.h"
11 
12 using namespace mozilla::ipc;
13 
14 namespace mozilla {
15 namespace dom {
16 
MemoryReportRequestHost(uint32_t aGeneration)17 MemoryReportRequestHost::MemoryReportRequestHost(uint32_t aGeneration)
18     : mGeneration(aGeneration), mSuccess(false) {
19   MOZ_COUNT_CTOR(MemoryReportRequestHost);
20   mReporterManager = nsMemoryReporterManager::GetOrCreate();
21   NS_WARNING_ASSERTION(mReporterManager, "GetOrCreate failed");
22 }
23 
RecvReport(const MemoryReport & aReport)24 void MemoryReportRequestHost::RecvReport(const MemoryReport& aReport) {
25   // Skip reports from older generations. We need to do this here since we
26   // could receive older reports from a subprocesses before it acknowledges
27   // a new request, and we only track one active request per process.
28   if (aReport.generation() != mGeneration) {
29     return;
30   }
31 
32   if (mReporterManager) {
33     mReporterManager->HandleChildReport(mGeneration, aReport);
34   }
35 }
36 
Finish(uint32_t aGeneration)37 void MemoryReportRequestHost::Finish(uint32_t aGeneration) {
38   // Skip reports from older generations. See the comment in RecvReport.
39   if (mGeneration != aGeneration) {
40     return;
41   }
42   mSuccess = true;
43 }
44 
~MemoryReportRequestHost()45 MemoryReportRequestHost::~MemoryReportRequestHost() {
46   MOZ_COUNT_DTOR(MemoryReportRequestHost);
47 
48   if (mReporterManager) {
49     mReporterManager->EndProcessReport(mGeneration, mSuccess);
50     mReporterManager = nullptr;
51   }
52 }
53 
NS_IMPL_ISUPPORTS(MemoryReportRequestClient,nsIRunnable)54 NS_IMPL_ISUPPORTS(MemoryReportRequestClient, nsIRunnable)
55 
56 /* static */ void MemoryReportRequestClient::Start(
57     uint32_t aGeneration, bool aAnonymize, bool aMinimizeMemoryUsage,
58     const Maybe<FileDescriptor>& aDMDFile, const nsACString& aProcessString,
59     const ReportCallback& aReportCallback,
60     const FinishCallback& aFinishCallback) {
61   RefPtr<MemoryReportRequestClient> request = new MemoryReportRequestClient(
62       aGeneration, aAnonymize, aDMDFile, aProcessString, aReportCallback,
63       aFinishCallback);
64 
65   DebugOnly<nsresult> rv;
66   if (aMinimizeMemoryUsage) {
67     nsCOMPtr<nsIMemoryReporterManager> mgr =
68         do_GetService("@mozilla.org/memory-reporter-manager;1");
69     rv = mgr->MinimizeMemoryUsage(request);
70     // mgr will eventually call actor->Run()
71   } else {
72     rv = request->Run();
73   }
74 
75   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "actor operation failed");
76 }
77 
MemoryReportRequestClient(uint32_t aGeneration,bool aAnonymize,const Maybe<FileDescriptor> & aDMDFile,const nsACString & aProcessString,const ReportCallback & aReportCallback,const FinishCallback & aFinishCallback)78 MemoryReportRequestClient::MemoryReportRequestClient(
79     uint32_t aGeneration, bool aAnonymize,
80     const Maybe<FileDescriptor>& aDMDFile, const nsACString& aProcessString,
81     const ReportCallback& aReportCallback,
82     const FinishCallback& aFinishCallback)
83     : mGeneration(aGeneration),
84       mAnonymize(aAnonymize),
85       mProcessString(aProcessString),
86       mReportCallback(aReportCallback),
87       mFinishCallback(aFinishCallback) {
88   if (aDMDFile.isSome()) {
89     mDMDFile = aDMDFile.value();
90   }
91 }
92 
93 MemoryReportRequestClient::~MemoryReportRequestClient() = default;
94 
95 class HandleReportCallback final : public nsIHandleReportCallback {
96  public:
97   using ReportCallback = typename MemoryReportRequestClient::ReportCallback;
98 
99   NS_DECL_ISUPPORTS
100 
HandleReportCallback(uint32_t aGeneration,const nsACString & aProcess,const ReportCallback & aReportCallback)101   explicit HandleReportCallback(uint32_t aGeneration,
102                                 const nsACString& aProcess,
103                                 const ReportCallback& aReportCallback)
104       : mGeneration(aGeneration),
105         mProcess(aProcess),
106         mReportCallback(aReportCallback) {}
107 
Callback(const nsACString & aProcess,const nsACString & aPath,int32_t aKind,int32_t aUnits,int64_t aAmount,const nsACString & aDescription,nsISupports * aUnused)108   NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
109                       int32_t aKind, int32_t aUnits, int64_t aAmount,
110                       const nsACString& aDescription,
111                       nsISupports* aUnused) override {
112     MemoryReport memreport(mProcess, nsCString(aPath), aKind, aUnits, aAmount,
113                            mGeneration, nsCString(aDescription));
114     mReportCallback(memreport);
115     return NS_OK;
116   }
117 
118  private:
119   ~HandleReportCallback() = default;
120 
121   uint32_t mGeneration;
122   const nsCString mProcess;
123   ReportCallback mReportCallback;
124 };
125 
126 NS_IMPL_ISUPPORTS(HandleReportCallback, nsIHandleReportCallback)
127 
128 class FinishReportingCallback final : public nsIFinishReportingCallback {
129  public:
130   using FinishCallback = typename MemoryReportRequestClient::FinishCallback;
131 
132   NS_DECL_ISUPPORTS
133 
FinishReportingCallback(uint32_t aGeneration,const FinishCallback & aFinishCallback)134   explicit FinishReportingCallback(uint32_t aGeneration,
135                                    const FinishCallback& aFinishCallback)
136       : mGeneration(aGeneration), mFinishCallback(aFinishCallback) {}
137 
Callback(nsISupports * aUnused)138   NS_IMETHOD Callback(nsISupports* aUnused) override {
139     return mFinishCallback(mGeneration) ? NS_OK : NS_ERROR_FAILURE;
140   }
141 
142  private:
143   ~FinishReportingCallback() = default;
144 
145   uint32_t mGeneration;
146   FinishCallback mFinishCallback;
147 };
148 
NS_IMPL_ISUPPORTS(FinishReportingCallback,nsIFinishReportingCallback)149 NS_IMPL_ISUPPORTS(FinishReportingCallback, nsIFinishReportingCallback)
150 
151 NS_IMETHODIMP MemoryReportRequestClient::Run() {
152   nsCOMPtr<nsIMemoryReporterManager> mgr =
153       do_GetService("@mozilla.org/memory-reporter-manager;1");
154 
155   // Run the reporters.  The callback will turn each measurement into a
156   // MemoryReport.
157   RefPtr<HandleReportCallback> handleReport =
158       new HandleReportCallback(mGeneration, mProcessString, mReportCallback);
159   RefPtr<FinishReportingCallback> finishReporting =
160       new FinishReportingCallback(mGeneration, mFinishCallback);
161 
162   nsresult rv = mgr->GetReportsForThisProcessExtended(
163       handleReport, nullptr, mAnonymize, FileDescriptorToFILE(mDMDFile, "wb"),
164       finishReporting, nullptr);
165   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
166                        "GetReportsForThisProcessExtended failed");
167   return rv;
168 }
169 
170 }  // namespace dom
171 }  // namespace mozilla
172