Bug 1193838 - Allow ProfileGatherer to gather profiles from exiting processes. r=BenWa
authorMike Conley <mconley@mozilla.com>
Tue, 18 Aug 2015 14:57:35 -0400
changeset 309605 479e586e1a7fb017fcc2522a8b33ad386582762c
parent 309604 76da1f09f4ee8597d96565270c0e4cf6bcdd48d2
child 309606 9b6075887a725f632300c1e1c1186ad0325b42fd
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBenWa
bugs1193838
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1193838 - Allow ProfileGatherer to gather profiles from exiting processes. r=BenWa
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/plugins/ipc/PluginModuleParent.cpp
tools/profiler/gecko/ProfileGatherer.cpp
tools/profiler/public/ProfileGatherer.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2960,16 +2960,25 @@ ContentChild::RecvShutdown()
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     if (os) {
         os->NotifyObservers(static_cast<nsIContentChild*>(this),
                             "content-child-shutdown", nullptr);
     }
 
     GetIPCChannel()->SetAbortOnError(false);
 
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    if (profiler_is_active()) {
+        // We're shutting down while we were profiling. Send the
+        // profile up to the parent so that we don't lose this
+        // information.
+        Unused << RecvGatherProfile();
+    }
+#endif
+
     // Ignore errors here. If this fails, the parent will kill us after a
     // timeout.
     Unused << SendFinishShutdown();
     return true;
 }
 
 PBrowserOrId
 ContentChild::GetBrowserOrId(TabChild* aTabChild)
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2089,16 +2089,22 @@ ContentParent::ActorDestroy(ActorDestroy
         sNuwaPrefUpdates = nullptr;
     }
 #endif
 
     RecvRemoveGeolocationListener();
 
     mConsoleService = nullptr;
 
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    if (mGatherer && !mProfile.IsEmpty()) {
+        mGatherer->OOPExitProfile(mProfile);
+    }
+#endif
+
     if (obs) {
         RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
 
         props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);
 
         if (AbnormalShutdown == why) {
             Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
                                   NS_LITERAL_CSTRING("content"), 1);
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -3180,18 +3180,17 @@ PluginProfilerObserver::Observe(nsISuppo
                                 const char16_t *aData)
 {
     if (!strcmp(aTopic, "profiler-started")) {
         nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
         mPmp->StartProfiler(params);
     } else if (!strcmp(aTopic, "profiler-stopped")) {
         mPmp->StopProfiler();
     } else if (!strcmp(aTopic, "profiler-subprocess-gather")) {
-        RefPtr<ProfileGatherer> gatherer = static_cast<ProfileGatherer*>(aSubject);
-        mPmp->GatherAsyncProfile(gatherer);
+        mPmp->GatherAsyncProfile();
     } else if (!strcmp(aTopic, "profiler-subprocess")) {
         nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
         mPmp->GatheredAsyncProfile(pse);
     }
     return NS_OK;
 }
 
 void
--- a/tools/profiler/gecko/ProfileGatherer.cpp
+++ b/tools/profiler/gecko/ProfileGatherer.cpp
@@ -7,30 +7,46 @@
 #include "nsIObserverService.h"
 #include "GeckoSampler.h"
 
 using mozilla::dom::AutoJSAPI;
 using mozilla::dom::Promise;
 
 namespace mozilla {
 
-NS_IMPL_ISUPPORTS0(ProfileGatherer)
+/**
+ * When a subprocess exits before we've gathered profiles, we'll
+ * store profiles for those processes until gathering starts. We'll
+ * only store up to MAX_SUBPROCESS_EXIT_PROFILES. The buffer is
+ * circular, so as soon as we receive another exit profile, we'll
+ * bump the oldest one out of the buffer.
+ */
+static const uint32_t MAX_SUBPROCESS_EXIT_PROFILES = 5;
+
+NS_IMPL_ISUPPORTS(ProfileGatherer, nsIObserver)
 
 ProfileGatherer::ProfileGatherer(GeckoSampler* aTicker)
   : mTicker(aTicker)
   , mSinceTime(0)
   , mPendingProfiles(0)
   , mGathering(false)
 {
 }
 
 void
 ProfileGatherer::GatheredOOPProfile()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  if (!mGathering) {
+    // If we're not actively gathering, then we don't actually
+    // care that we gathered a profile here. This can happen for
+    // processes that exit while profiling.
+    return;
+  }
+
   if (NS_WARN_IF(!mPromise)) {
     // If we're not holding on to a Promise, then someone is
     // calling us erroneously.
     return;
   }
 
   mPendingProfiles--;
 
@@ -63,17 +79,19 @@ ProfileGatherer::Start(double aSinceTime
 
   mSinceTime = aSinceTime;
   mPromise = aPromise;
   mGathering = true;
   mPendingProfiles = 0;
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
-    nsresult rv = os->NotifyObservers(this, "profiler-subprocess-gather", nullptr);
+    nsresult rv = os->AddObserver(this, "profiler-subprocess", false);
+    NS_WARN_IF(NS_FAILED(rv));
+    rv = os->NotifyObservers(this, "profiler-subprocess-gather", nullptr);
     NS_WARN_IF(NS_FAILED(rv));
   }
 
   if (!mPendingProfiles) {
     Finish();
   }
 }
 
@@ -85,16 +103,22 @@ ProfileGatherer::Finish()
   if (!mTicker) {
     // We somehow got called after we were cancelled! This shouldn't
     // be possible, but doing a belt-and-suspenders check to be sure.
     return;
   }
 
   UniquePtr<char[]> buf = mTicker->ToJSON(mSinceTime);
 
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os) {
+    nsresult rv = os->RemoveObserver(this, "profiler-subprocess");
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+
   AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.Init(mPromise->GlobalJSObject()))) {
     // We're really hosed if we can't get a JS context for some reason.
     Reset();
     return;
   }
 
   JSContext* cx = jsapi.cx();
@@ -140,9 +164,43 @@ ProfileGatherer::Cancel()
   if (mPromise) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
 
   // Clear out the GeckoSampler reference, since it's being destroyed.
   mTicker = nullptr;
 }
 
+void
+ProfileGatherer::OOPExitProfile(const nsCString& aProfile)
+{
+  if (mExitProfiles.Length() >= MAX_SUBPROCESS_EXIT_PROFILES) {
+    mExitProfiles.RemoveElementAt(0);
+  }
+  mExitProfiles.AppendElement(aProfile);
+
+  // If a process exited while gathering, we need to make
+  // sure we decrement the counter.
+  if (mGathering) {
+    GatheredOOPProfile();
+  }
+}
+
+NS_IMETHODIMP
+ProfileGatherer::Observe(nsISupports* aSubject,
+                         const char* aTopic,
+                         const char16_t *someData)
+{
+  if (!strcmp(aTopic, "profiler-subprocess")) {
+    nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
+    if (pse) {
+      for (size_t i = 0; i < mExitProfiles.Length(); ++i) {
+        if (!mExitProfiles[i].IsEmpty()) {
+          pse->AddSubProfile(mExitProfiles[i].get());
+        }
+      }
+      mExitProfiles.Clear();
+    }
+  }
+  return NS_OK;
+}
+
 } // namespace mozilla
--- a/tools/profiler/public/ProfileGatherer.h
+++ b/tools/profiler/public/ProfileGatherer.h
@@ -6,32 +6,35 @@
 #define MOZ_PROFILE_GATHERER_H
 
 #include "mozilla/dom/Promise.h"
 
 class GeckoSampler;
 
 namespace mozilla {
 
-class ProfileGatherer final : public nsISupports
+class ProfileGatherer final : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
 
   explicit ProfileGatherer(GeckoSampler* aTicker);
   void WillGatherOOPProfile();
   void GatheredOOPProfile();
   void Start(double aSinceTime, mozilla::dom::Promise* aPromise);
   void Cancel();
+  void OOPExitProfile(const nsCString& aProfile);
 
 private:
   ~ProfileGatherer() {};
   void Finish();
   void Reset();
 
+  nsTArray<nsCString> mExitProfiles;
   RefPtr<mozilla::dom::Promise> mPromise;
   GeckoSampler* mTicker;
   double mSinceTime;
   uint32_t mPendingProfiles;
   bool mGathering;
 };
 
 } // namespace mozilla