Bug 1330185 - Use a top-level PProfiler protocol to control the profiler in other processes. r=njn, r=billm draft
authorMarkus Stange <mstange@themasta.com>
Mon, 29 May 2017 18:24:29 -0400
changeset 586184 cfa688b52c7af1c6cafc8c9636d62067f7b98b88
parent 586181 b4ff8e1f6913fc9fdf32a74c8474a84fc1d76c01
child 630914 9112f1212732eb0b674e5dd4ee13d93c1a43bb7e
push id61323
push userbmo:mstange@themasta.com
push dateMon, 29 May 2017 22:24:53 +0000
reviewersnjn, billm
bugs1330185
milestone55.0a1
Bug 1330185 - Use a top-level PProfiler protocol to control the profiler in other processes. r=njn, r=billm MozReview-Commit-ID: EROfGuYQ6a0
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/plugins/ipc/PPluginModule.ipdl
dom/plugins/ipc/PluginModuleChild.cpp
dom/plugins/ipc/PluginModuleChild.h
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginModuleParent.h
gfx/ipc/GPUChild.cpp
gfx/ipc/GPUChild.h
gfx/ipc/GPUParent.cpp
gfx/ipc/GPUParent.h
gfx/ipc/PGPU.ipdl
tools/profiler/core/platform.cpp
tools/profiler/gecko/ChildProfilerController.cpp
tools/profiler/gecko/CrossProcessProfilerController.cpp
tools/profiler/gecko/PProfiler.ipdl
tools/profiler/gecko/ProfileGatherer.cpp
tools/profiler/gecko/ProfileGatherer.h
tools/profiler/gecko/ProfilerChild.cpp
tools/profiler/gecko/ProfilerParent.cpp
tools/profiler/gecko/nsIProfiler.idl
tools/profiler/gecko/nsProfiler.cpp
tools/profiler/gecko/nsProfiler.h
tools/profiler/gecko/nsProfilerStartParams.h
tools/profiler/moz.build
tools/profiler/public/ChildProfilerController.h
tools/profiler/public/CrossProcessProfilerController.h
tools/profiler/public/ProfilerChild.h
tools/profiler/public/ProfilerControllingProcess.h
tools/profiler/public/ProfilerParent.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -68,16 +68,20 @@
 #include "mozilla/widget/WidgetMessageUtils.h"
 #include "nsBaseDragService.h"
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/WebBrowserPersistDocumentChild.h"
 #include "imgLoader.h"
 #include "GMPServiceChild.h"
 
+#ifdef MOZ_GECKO_PROFILER
+#include "ChildProfilerController.h"
+#endif
+
 #if defined(MOZ_CONTENT_SANDBOX)
 #if defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #elif defined(XP_LINUX)
 #include "mozilla/Sandbox.h"
 #include "mozilla/SandboxInfo.h"
 
@@ -1122,16 +1126,25 @@ ContentChild::RecvInitGMPService(Endpoin
 {
   if (!GMPServiceChild::Create(Move(aGMPService))) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+ContentChild::RecvInitProfiler(Endpoint<PProfilerChild>&& aEndpoint)
+{
+#ifdef MOZ_GECKO_PROFILER
+  mProfilerController = new ChildProfilerController(Move(aEndpoint));
+#endif
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ContentChild::RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities)
 {
   GeckoMediaPluginServiceChild::UpdateGMPCapabilities(Move(capabilities));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvInitProcessHangMonitor(Endpoint<PProcessHangMonitorChild>&& aHangMonitor)
@@ -2603,70 +2616,16 @@ ContentChild::DeallocPOfflineCacheUpdate
 {
   OfflineCacheUpdateChild* offlineCacheUpdate =
     static_cast<OfflineCacheUpdateChild*>(actor);
   NS_RELEASE(offlineCacheUpdate);
   return true;
 }
 
 mozilla::ipc::IPCResult
-ContentChild::RecvStartProfiler(const ProfilerInitParams& params)
-{
-  nsTArray<const char*> filterArray;
-  for (size_t i = 0; i < params.filters().Length(); ++i) {
-    filterArray.AppendElement(params.filters()[i].get());
-  }
-
-  profiler_start(params.entries(), params.interval(), params.features(),
-                 filterArray.Elements(), filterArray.Length());
-
- return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-ContentChild::RecvStopProfiler()
-{
-  profiler_stop();
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-ContentChild::RecvPauseProfiler(const bool& aPause)
-{
-  if (aPause) {
-    profiler_pause();
-  } else {
-    profiler_resume();
-  }
-
-  return IPC_OK();
-}
-
-void
-ContentChild::GatherProfile(bool aIsExitProfile)
-{
-  nsCString profileCString;
-  UniquePtr<char[]> profile = profiler_get_profile();
-  if (profile) {
-    profileCString = nsCString(profile.get(), strlen(profile.get()));
-  } else {
-    profileCString = EmptyCString();
-  }
-
-  Unused << SendProfile(profileCString, aIsExitProfile);
-}
-
-mozilla::ipc::IPCResult
-ContentChild::RecvGatherProfile()
-{
-  GatherProfile(false);
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
 ContentChild::RecvLoadPluginResult(const uint32_t& aPluginId,
                                    const bool& aResult)
 {
   nsresult rv;
   Endpoint<PPluginModuleParent> endpoint;
   bool finalResult = aResult &&
                      SendConnectPluginBridge(aPluginId, &rv, &endpoint) &&
                      NS_SUCCEEDED(rv);
@@ -2822,21 +2781,22 @@ ContentChild::RecvShutdown()
 
 #if defined(XP_WIN)
     mozilla::widget::StopAudioSession();
 #endif
 
   GetIPCChannel()->SetAbortOnError(false);
 
 #ifdef MOZ_GECKO_PROFILER
-  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.
-    GatherProfile(true);
+  if (mProfilerController) {
+    nsCString shutdownProfile = mProfilerController->GrabShutdownProfileAndShutdown();
+    mProfilerController = nullptr;
+    // Send the shutdown profile to the parent process through our own
+    // message channel, which we know will survive for long enough.
+    Unused << SendShutdownProfile(shutdownProfile);
   }
 #endif
 
   // Start a timer that will insure we quickly exit after a reasonable
   // period of time. Prevents shutdown hangs after our connection to the
   // parent closes.
   StartForceKillTimer();
 
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -30,16 +30,17 @@ class nsIObserver;
 struct SubstitutionMapping;
 struct OverrideMapping;
 class nsIDomainPolicy;
 class nsIURIClassifierCallback;
 struct LookAndFeelInt;
 
 namespace mozilla {
 class RemoteSpellcheckEngineChild;
+class ChildProfilerController;
 
 using mozilla::loader::PScriptCacheChild;
 
 namespace ipc {
 class OptionalURIParams;
 class URIParams;
 }// namespace ipc
 
@@ -145,16 +146,19 @@ public:
 
   mozilla::ipc::IPCResult
   RecvInitContentBridgeChild(Endpoint<PContentBridgeChild>&& aEndpoint) override;
 
   mozilla::ipc::IPCResult
   RecvInitGMPService(Endpoint<PGMPServiceChild>&& aGMPService) override;
 
   mozilla::ipc::IPCResult
+  RecvInitProfiler(Endpoint<PProfilerChild>&& aEndpoint) override;
+
+  mozilla::ipc::IPCResult
   RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities) override;
 
   mozilla::ipc::IPCResult
   RecvInitProcessHangMonitor(Endpoint<PProcessHangMonitorChild>&& aHangMonitor) override;
 
   mozilla::ipc::IPCResult
   RecvInitRendering(
     Endpoint<PCompositorBridgeChild>&& aCompositor,
@@ -436,24 +440,16 @@ public:
   virtual mozilla::ipc::IPCResult RecvAssociatePluginId(const uint32_t& aPluginId,
                                                         const base::ProcessId& aProcessId) override;
 
   virtual mozilla::ipc::IPCResult RecvLoadPluginResult(const uint32_t& aPluginId,
                                                        const bool& aResult) override;
 
   virtual mozilla::ipc::IPCResult RecvUpdateWindow(const uintptr_t& aChildId) override;
 
-  virtual mozilla::ipc::IPCResult RecvStartProfiler(const ProfilerInitParams& params) override;
-
-  virtual mozilla::ipc::IPCResult RecvPauseProfiler(const bool& aPause) override;
-
-  virtual mozilla::ipc::IPCResult RecvStopProfiler() override;
-
-  virtual mozilla::ipc::IPCResult RecvGatherProfile() override;
-
   virtual mozilla::ipc::IPCResult RecvDomainSetChanged(const uint32_t& aSetType,
                                                        const uint32_t& aChangeType,
                                                        const OptionalURIParams& aDomain) override;
 
   virtual mozilla::ipc::IPCResult RecvShutdown() override;
 
   virtual mozilla::ipc::IPCResult
   RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
@@ -669,18 +665,16 @@ private:
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
   virtual already_AddRefed<nsIEventTarget>
   GetConstructedEventTarget(const Message& aMsg) override;
 
-  void GatherProfile(bool aIsExitProfile);
-
   InfallibleTArray<nsAutoPtr<AlertObserver> > mAlertObservers;
   RefPtr<ConsoleListener> mConsoleListener;
 
   nsTHashtable<nsPtrHashKey<nsIObserver>> mIdleObservers;
 
   InfallibleTArray<nsString> mAvailableDictionaries;
 
   // Temporary storage for a list of available font families, passed from the
@@ -715,16 +709,20 @@ private:
   bool mIsAlive;
   nsString mProcessName;
 
   static ContentChild* sSingleton;
 
   nsCOMPtr<nsIDomainPolicy> mPolicy;
   nsCOMPtr<nsITimer> mForceKillTimer;
 
+#ifdef MOZ_GECKO_PROFILER
+  RefPtr<ChildProfilerController> mProfilerController;
+#endif
+
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
   nsCOMPtr<nsIFile> mProfileDir;
 #endif
 
   // Hashtable to keep track of the pending GetFilesHelper objects.
   // This GetFilesHelperChild objects are removed when RecvGetFilesResponse is
   // received.
   nsRefPtrHashtable<nsIDHashKey, GetFilesHelperChild> mGetFilesPendingRequests;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -19,19 +19,16 @@
 #ifdef MOZ_WIDGET_GONK
 #include <sys/types.h>
 #include <sys/wait.h>
 #endif
 
 #include "chrome/common/process_watcher.h"
 
 #include "mozilla/a11y/PDocAccessible.h"
-#ifdef MOZ_GECKO_PROFILER
-#include "CrossProcessProfilerController.h"
-#endif
 #include "GeckoProfiler.h"
 #include "GMPServiceParent.h"
 #include "HandlerServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
 #include "mozilla/a11y/AccessibleWrap.h"
 #endif
@@ -182,16 +179,17 @@
 #include "nsPluginHost.h"
 #include "nsPluginTags.h"
 #include "nsIBlocklistService.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsICaptivePortalService.h"
 #include "nsIObjectLoadingContent.h"
+#include "ProfilerParent.h"
 
 #include "nsIBidiKeyboard.h"
 
 #include "nsLayoutStylesheetCache.h"
 
 #include "ContentPrefs.h"
 #include "mozilla/Sprintf.h"
 
@@ -249,16 +247,20 @@
 #include "nsThread.h"
 #include "mozilla/ipc/CrashReporterHost.h"
 #endif
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
+#ifdef MOZ_GECKO_PROFILER
+#include "nsIProfiler.h"
+#endif
+
 // For VP9Benchmark::sBenchmarkFpsPref
 #include "Benchmark.h"
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
 #if defined(XP_WIN)
 // e10s forced enable pref, defined in nsAppRunner.cpp
 extern const char* kForceEnableE10sPref;
@@ -1070,48 +1072,16 @@ ContentParent::RecvUngrabPointer(const u
 mozilla::ipc::IPCResult
 ContentParent::RecvRemovePermission(const IPC::Principal& aPrincipal,
                                     const nsCString& aPermissionType,
                                     nsresult* aRv) {
   *aRv = Permissions::RemovePermission(aPrincipal, aPermissionType.get());
   return IPC_OK();
 }
 
-void
-ContentParent::SendStartProfiler(const ProfilerInitParams& aParams)
-{
-  if (mSubprocess && mIsAlive) {
-    Unused << PContentParent::SendStartProfiler(aParams);
-  }
-}
-
-void
-ContentParent::SendStopProfiler()
-{
-  if (mSubprocess && mIsAlive) {
-    Unused << PContentParent::SendStopProfiler();
-  }
-}
-
-void
-ContentParent::SendPauseProfiler(const bool& aPause)
-{
-  if (mSubprocess && mIsAlive) {
-    Unused << PContentParent::SendPauseProfiler(aPause);
-  }
-}
-
-void
-ContentParent::SendGatherProfile()
-{
-  if (mSubprocess && mIsAlive) {
-    Unused << PContentParent::SendGatherProfile();
-  }
-}
-
 mozilla::ipc::IPCResult
 ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId,
                                        nsresult* aRv,
                                        Endpoint<PPluginModuleParent>* aEndpoint)
 {
   *aRv = NS_OK;
   // We don't need to get the run ID for the plugin, since we already got it
   // in the first call to SetupBridge in RecvLoadPlugin, so we pass in a dummy
@@ -1356,17 +1326,17 @@ ContentParent::Init()
       SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
 #else
     Unused << SendActivateA11y(0);
 #endif
   }
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
-  mProfilerController = MakeUnique<CrossProcessProfilerController>(this);
+  Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
 #endif
 
   // Ensure that the default set of permissions are avaliable in the content
   // process before we try to load any URIs in it.
   EnsurePermissionsByKey(EmptyCString());
 
   RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton());
   gmps->UpdateContentProcessGMPCapabilities();
@@ -1753,20 +1723,16 @@ ContentParent::ActorDestroy(ActorDestroy
     // Note: the manager could have shutdown already.
     gpu->RemoveListener(this);
   }
 
   RecvRemoveGeolocationListener();
 
   mConsoleService = nullptr;
 
-#ifdef MOZ_GECKO_PROFILER
-  mProfilerController = nullptr;
-#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);
@@ -4642,22 +4608,21 @@ ContentParent::RecvCreateWindowInDiffere
   if (NS_FAILED(rv)) {
     NS_WARNING("Call to CommonCreateWindow failed.");
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentParent::RecvProfile(const nsCString& aProfile, const bool& aIsExitProfile)
+ContentParent::RecvShutdownProfile(const nsCString& aProfile)
 {
 #ifdef MOZ_GECKO_PROFILER
-  if (mProfilerController) {
-    mProfilerController->RecvProfile(aProfile, aIsExitProfile);
-  }
+  nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
+  profiler->ReceiveShutdownProfile(aProfile);
 #endif
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut)
 {
   gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -25,17 +25,16 @@
 #include "nsFrameMessageManager.h"
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
 #include "nsIThreadInternal.h"
 #include "nsIDOMGeoPositionCallback.h"
 #include "nsIDOMGeoPositionErrorCallback.h"
 #include "nsRefPtrHashtable.h"
 #include "PermissionMessageUtils.h"
-#include "ProfilerControllingProcess.h"
 #include "DriverCrashGuard.h"
 
 #define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown")
 
 #define NO_REMOTE_TYPE ""
 
 // These must match the similar ones in E10SUtils.jsm.
 #define DEFAULT_REMOTE_TYPE "web"
@@ -51,19 +50,16 @@ class nsICycleCollectorLogSink;
 class nsIDumpGCAndCCLogsCallback;
 class nsITabParent;
 class nsITimer;
 class ParentIdleListener;
 class nsIWidget;
 
 namespace mozilla {
 class PRemoteSpellcheckEngineParent;
-#ifdef MOZ_GECKO_PROFILER
-class CrossProcessProfilerController;
-#endif
 
 #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
 class SandboxBroker;
 class SandboxBrokerPolicyFactory;
 #endif
 
 class PreallocatedProcessManagerImpl;
 
@@ -109,17 +105,16 @@ class ContentParent final : public PCont
                           , public nsIContentParent
                           , public nsIObserver
                           , public nsIDOMGeoPositionCallback
                           , public nsIDOMGeoPositionErrorCallback
                           , public gfx::gfxVarReceiver
                           , public mozilla::LinkedListElement<ContentParent>
                           , public gfx::GPUProcessListener
                           , public mozilla::MemoryReportingProcess
-                          , public mozilla::ProfilerControllingProcess
 {
   typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
   typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
   typedef mozilla::ipc::PFileDescriptorSetParent PFileDescriptorSetParent;
   typedef mozilla::ipc::TestShellParent TestShellParent;
   typedef mozilla::ipc::URIParams URIParams;
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
@@ -307,21 +302,16 @@ public:
                                                   uint32_t* aNewPluginEpoch) override;
 
   virtual mozilla::ipc::IPCResult RecvUngrabPointer(const uint32_t& aTime) override;
 
   virtual mozilla::ipc::IPCResult RecvRemovePermission(const IPC::Principal& aPrincipal,
                                                        const nsCString& aPermissionType,
                                                        nsresult* aRv) override;
 
-  void SendStartProfiler(const ProfilerInitParams& aParams) override;
-  void SendStopProfiler() override;
-  void SendPauseProfiler(const bool& aPause) override;
-  void SendGatherProfile() override;
-
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIDOMGEOPOSITIONCALLBACK
   NS_DECL_NSIDOMGEOPOSITIONERRORCALLBACK
 
   /**
@@ -1107,18 +1097,17 @@ private:
 
   virtual PWebrtcGlobalParent* AllocPWebrtcGlobalParent() override;
   virtual bool DeallocPWebrtcGlobalParent(PWebrtcGlobalParent *aActor) override;
 
 
   virtual mozilla::ipc::IPCResult RecvUpdateDropEffect(const uint32_t& aDragAction,
                                                        const uint32_t& aDropEffect) override;
 
-  virtual mozilla::ipc::IPCResult RecvProfile(const nsCString& aProfile,
-                                              const bool& aIsExitProfile) override;
+  virtual mozilla::ipc::IPCResult RecvShutdownProfile(const nsCString& aProfile) override;
 
   virtual mozilla::ipc::IPCResult RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut) override;
 
   virtual mozilla::ipc::IPCResult RecvGetAndroidSystemInfo(AndroidSystemInfo* aInfo) override;
 
   virtual mozilla::ipc::IPCResult RecvNotifyBenchmarkResult(const nsString& aCodecName,
                                                             const uint32_t& aDecodeFPS) override;
 
@@ -1225,20 +1214,16 @@ private:
 #ifdef MOZ_X11
   // Dup of child's X socket, used to scope its resources to this
   // object instead of the child process's lifetime.
   ScopedClose mChildXSocketFdDup;
 #endif
 
   PProcessHangMonitorParent* mHangMonitorActor;
 
-#ifdef MOZ_GECKO_PROFILER
-  UniquePtr<mozilla::CrossProcessProfilerController> mProfilerController;
-#endif
-
   UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard;
   UniquePtr<MemoryReportRequestHost> mMemoryReportRequest;
 
 #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
   mozilla::UniquePtr<SandboxBroker> mSandboxBroker;
   static mozilla::UniquePtr<SandboxBrokerPolicyFactory>
       sSandboxBrokerPolicyFactory;
 #endif
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -38,30 +38,30 @@ include protocol PRemoteSpellcheckEngine
 include protocol PWebBrowserPersistDocument;
 include protocol PWebrtcGlobal;
 include protocol PPresentation;
 include protocol PURLClassifier;
 include protocol PURLClassifierLocal;
 include protocol PVRManager;
 include protocol PVideoDecoderManager;
 include protocol PFlyWebPublishedServer;
+include protocol PProfiler;
 include protocol PScriptCache;
 include DOMTypes;
 include JavaScriptTypes;
 include IPCBlob;
 include IPCStream;
 include PTabContext;
 include URIParams;
 include PluginTypes;
 include ProtocolTypes;
 include PBackgroundSharedTypes;
 include PContentPermission;
 include ServiceWorkerConfiguration;
 include GraphicsMessages;
-include ProfilerTypes;
 include MemoryReportTypes;
 
 // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
 // are put into different UnifiedProtocolsXX.cpp files.
 // XXX Remove this once bug 1069073 is fixed
 include "mozilla/dom/PContentBridgeParent.h";
 include "mozilla/layers/WebRenderMessageUtils.h";
 
@@ -345,16 +345,17 @@ both:
     // ignored and should be null/zero.
     async PWebBrowserPersistDocument(nullable PBrowser aBrowser,
                                      uint64_t aOuterWindowID);
 
 child:
     async InitGMPService(Endpoint<PGMPServiceChild> service);
     async InitProcessHangMonitor(Endpoint<PProcessHangMonitorChild> hangMonitor);
     async InitContentBridgeChild(Endpoint<PContentBridgeChild> endpoint);
+    async InitProfiler(Endpoint<PProfilerChild> aEndpoint);
 
     // Give the content process its endpoints to the compositor.
     async InitRendering(
       Endpoint<PCompositorBridgeChild> compositor,
       Endpoint<PImageBridgeChild> imageBridge,
       Endpoint<PVRManagerChild> vr,
       Endpoint<PVideoDecoderManagerChild> video,
       uint32_t[] namespaces);
@@ -500,25 +501,16 @@ child:
 
     /**
      * This call is used by async plugin initialization to notify the
      * PluginModuleContentParent that the PluginModuleChromeParent's async
      * init has completed.
      */
     async LoadPluginResult(uint32_t aPluginId, bool aResult);
 
-    /**
-     * Control the Gecko Profiler in the child process.
-     */
-    async StartProfiler(ProfilerInitParams params);
-    async StopProfiler();
-    async PauseProfiler(bool aPause);
-
-    async GatherProfile();
-
     async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action);
 
     async EndDragSession(bool aDoneDrag, bool aUserCancelled,
                          LayoutDeviceIntPoint aDragEndPoint,
                          uint32_t aKeyModifiers);
 
     async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, OptionalURIParams aDomain);
 
@@ -979,17 +971,17 @@ parent:
      *
      * NOTE: The principal is untrusted in the parent process. Only
      *       principals that can live in the content process should
      *       provided.
      */
     async PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal,
                                     TabId tabId);
 
-    async Profile(nsCString aProfile, bool aIsExitProfile);
+    async ShutdownProfile(nsCString aProfile);
 
     /**
      * Request graphics initialization information from the parent.
      */
     sync GetGraphicsDeviceInitData()
         returns (ContentDeviceData aData);
 
     sync CreateWindow(nullable PBrowser aThisTab,
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PPluginInstance;
 include protocol PPluginScriptableObject;
 include protocol PContent;
-include ProfilerTypes;
+include protocol PProfiler;
 
 using NPError from "npapi.h";
 using NPNVariable from "npapi.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h";
 using struct nsID from "nsID.h";
 using struct mozilla::plugins::NPAudioDeviceChangeDetailsIPC from "mozilla/plugins/PluginMessageUtils.h";
 using mozilla::plugins::GetFileNameFunc from "mozilla/plugins/PluginMessageUtils.h";
@@ -41,16 +41,18 @@ intr protocol PPluginModule
   manages PPluginInstance;
 
 both:
   // Window-specific message which instructs the interrupt mechanism to enter
   // a nested event loop for the current interrupt call.
   async ProcessNativeEventsInInterruptCall();
 
 child:
+  async InitProfiler(Endpoint<PProfilerChild> aEndPoint);
+
   async DisableFlashProtectedMode();
 
   // Sync query to check if a Flash library indicates it
   // supports async rendering mode.
   intr ModuleSupportsAsyncRender()
     returns (bool result);
 
   // Forces the child process to update its plugin function table.
@@ -90,24 +92,16 @@ child:
                             nsString aDisplayName,
                             nsString aIconPath);
 
   async SetParentHangTimeout(uint32_t seconds);
 
   intr InitCrashReporter(Shmem shmem)
     returns (NativeThreadId tid);
 
-  /**
-   * Control the Gecko Profiler in the plugin process.
-   */
-  async StartProfiler(ProfilerInitParams params);
-  async StopProfiler();
-  async PauseProfiler(bool aPause);
-  async GatherProfile();
-
   async SettingChanged(PluginSettings settings);
 
   async NPP_SetValue_NPNVaudioDeviceChangeDetails(NPAudioDeviceChangeDetailsIPC changeDetails);
 
   async InitPluginModuleChild(Endpoint<PPluginModuleChild> endpoint);
 
 parent:
   async NP_InitializeResult(NPError aError);
@@ -148,18 +142,16 @@ parent:
 
   async NPN_ReloadPlugins(bool aReloadPages);
 
   // Notifies the chrome process that a PluginModuleChild linked to a content
   // process was destroyed. The chrome process may choose to asynchronously shut
   // down the plugin process in response.
   async NotifyContentModuleDestroyed();
 
-  async Profile(nsCString aProfile, bool aIsExitProfile);
-
   // Answers to request about site data
   async ReturnClearSiteData(NPError aRv, uint64_t aCallbackId);
 
   async ReturnSitesWithData(nsCString[] aSites, uint64_t aCallbackId);
 
   intr GetKeyState(int32_t aVirtKey)
     returns (int16_t aState);
 
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -48,17 +48,19 @@
 #include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 #include "mozilla/ipc/CrashReporterClient.h"
 #endif
 
-#include "GeckoProfiler.h"
+#ifdef MOZ_GECKO_PROFILER
+#include "ChildProfilerController.h"
+#endif
 
 using namespace mozilla;
 using namespace mozilla::ipc;
 using namespace mozilla::plugins;
 using namespace mozilla::widget;
 
 #if defined(XP_WIN)
 const wchar_t * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
@@ -206,16 +208,25 @@ PluginModuleChild::InitForContent(Endpoi
 
     mLibrary = GetChrome()->mLibrary;
     mFunctions = GetChrome()->mFunctions;
 
     return true;
 }
 
 mozilla::ipc::IPCResult
+PluginModuleChild::RecvInitProfiler(Endpoint<mozilla::PProfilerChild>&& aEndpoint)
+{
+#ifdef MOZ_GECKO_PROFILER
+    mProfilerController = new ChildProfilerController(Move(aEndpoint));
+#endif
+    return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 PluginModuleChild::RecvDisableFlashProtectedMode()
 {
     MOZ_ASSERT(mIsChrome);
 #ifdef XP_WIN
     HookProtectedMode();
 #else
     MOZ_ASSERT(false, "Should not be called");
 #endif
@@ -745,16 +756,23 @@ PluginModuleChild::AnswerInitCrashReport
     *aOutId = CrashReporter::CurrentThreadId();
 #endif
     return IPC_OK();
 }
 
 void
 PluginModuleChild::ActorDestroy(ActorDestroyReason why)
 {
+#ifdef MOZ_GECKO_PROFILER
+    if (mProfilerController) {
+        mProfilerController->Shutdown();
+        mProfilerController = nullptr;
+    }
+#endif
+
     if (!mIsChrome) {
         PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
         if (chromeInstance) {
             chromeInstance->SendNotifyContentModuleDestroyed();
         }
 
         // Destroy ourselves once we finish other teardown activities.
         RefPtr<DeleteTask<PluginModuleChild>> task =
@@ -2719,64 +2737,16 @@ PluginModuleChild::RecvProcessNativeEven
 
 #ifdef MOZ_WIDGET_COCOA
 void
 PluginModuleChild::ProcessNativeEvents() {
     CallProcessSomeEvents();    
 }
 #endif
 
-mozilla::ipc::IPCResult
-PluginModuleChild::RecvStartProfiler(const ProfilerInitParams& params)
-{
-    nsTArray<const char*> filterArray;
-    for (size_t i = 0; i < params.filters().Length(); ++i) {
-        filterArray.AppendElement(params.filters()[i].get());
-    }
-
-    profiler_start(params.entries(), params.interval(), params.features(),
-                   filterArray.Elements(), filterArray.Length());
-
-    return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-PluginModuleChild::RecvStopProfiler()
-{
-    profiler_stop();
-    return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-PluginModuleChild::RecvPauseProfiler(const bool& aPause)
-{
-    if (aPause) {
-        profiler_pause();
-    } else {
-        profiler_resume();
-    }
-
-    return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-PluginModuleChild::RecvGatherProfile()
-{
-    nsCString profileCString;
-    UniquePtr<char[]> profile = profiler_get_profile();
-    if (profile != nullptr) {
-        profileCString = nsCString(profile.get(), strlen(profile.get()));
-    } else {
-        profileCString = nsCString("", 0);
-    }
-
-    Unused << SendProfile(profileCString, false);
-    return IPC_OK();
-}
-
 NPError
 PluginModuleChild::PluginRequiresAudioDeviceChanges(
                           PluginInstanceChild* aInstance,
                           NPBool aShouldRegister)
 {
 #ifdef XP_WIN
     // Maintain a set of PluginInstanceChildren that we need to tell when the
     // default audio device has changed.
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -41,16 +41,19 @@
 #endif
 
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_GETENTRYPOINTS) (NPPluginFuncs* pCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGININIT) (const NPNetscapeFuncs* pCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINUNIXINIT) (const NPNetscapeFuncs* pCallbacks, NPPluginFuncs* fCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINSHUTDOWN) (void);
 
 namespace mozilla {
+
+class ChildProfilerController;
+
 namespace plugins {
 
 class PluginInstanceChild;
 
 class PluginModuleChild : public PPluginModuleChild
 {
 protected:
     virtual mozilla::ipc::RacyInterruptPolicy
@@ -60,16 +63,17 @@ protected:
         return MediateRace(parent, child);
     }
 
     virtual bool ShouldContinueFromReplyTimeout() override;
 
     virtual mozilla::ipc::IPCResult RecvSettingChanged(const PluginSettings& aSettings) override;
 
     // Implement the PPluginModuleChild interface
+    virtual mozilla::ipc::IPCResult RecvInitProfiler(Endpoint<mozilla::PProfilerChild>&& aEndpoint) override;
     virtual mozilla::ipc::IPCResult RecvDisableFlashProtectedMode() override;
     virtual mozilla::ipc::IPCResult AnswerNP_GetEntryPoints(NPError* rv) override;
     virtual mozilla::ipc::IPCResult AnswerNP_Initialize(const PluginSettings& aSettings, NPError* rv) override;
     virtual mozilla::ipc::IPCResult RecvAsyncNP_Initialize(const PluginSettings& aSettings) override;
     virtual mozilla::ipc::IPCResult AnswerSyncNPP_New(PPluginInstanceChild* aActor, NPError* rv)
                                    override;
     virtual mozilla::ipc::IPCResult RecvAsyncNPP_New(PPluginInstanceChild* aActor) override;
 
@@ -120,21 +124,16 @@ protected:
     AnswerInitCrashReporter(Shmem&& aShmem, mozilla::dom::NativeThreadId* aId) override;
 
     virtual void
     ActorDestroy(ActorDestroyReason why) override;
 
     virtual mozilla::ipc::IPCResult
     RecvProcessNativeEventsInInterruptCall() override;
 
-    virtual mozilla::ipc::IPCResult RecvStartProfiler(const ProfilerInitParams& params) override;
-    virtual mozilla::ipc::IPCResult RecvStopProfiler() override;
-    virtual mozilla::ipc::IPCResult RecvPauseProfiler(const bool& aPause) override;
-    virtual mozilla::ipc::IPCResult RecvGatherProfile() override;
-
     virtual mozilla::ipc::IPCResult
     AnswerModuleSupportsAsyncRender(bool* aResult) override;
 public:
     explicit PluginModuleChild(bool aIsChrome);
     virtual ~PluginModuleChild();
 
     void CommonInit();
 
@@ -250,16 +249,20 @@ private:
 
     PRLibrary* mLibrary;
     nsCString mPluginFilename; // UTF8
     int mQuirks;
 
     bool mIsChrome;
     bool mHasShutdown; // true if NP_Shutdown has run
 
+#ifdef MOZ_GECKO_PROFILER
+    RefPtr<ChildProfilerController> mProfilerController;
+#endif
+
     // we get this from the plugin
     NP_PLUGINSHUTDOWN mShutdownFunc;
 #if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
     NP_PLUGINUNIXINIT mInitializeFunc;
 #elif defined(OS_WIN) || defined(OS_MACOSX)
     NP_PLUGININIT mInitializeFunc;
     NP_GETENTRYPOINTS mGetEntryPointsFunc;
 #endif
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -28,20 +28,18 @@
 #include "nsIObserverService.h"
 #include "nsIXULRuntime.h"
 #include "nsNPAPIPlugin.h"
 #include "nsPrintfCString.h"
 #include "prsystem.h"
 #include "prclist.h"
 #include "PluginQuirks.h"
 #include "gfxPlatform.h"
-#ifdef MOZ_GECKO_PROFILER
-#include "CrossProcessProfilerController.h"
-#endif
 #include "GeckoProfiler.h"
+#include "ProfilerParent.h"
 #include "nsPluginTags.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/layers/TextureClientRecycleAllocator.h"
 
 #ifdef XP_WIN
 #include "mozilla/plugins/PluginSurfaceParent.h"
 #include "mozilla/widget/AudioSession.h"
 #include "PluginHangUIParent.h"
@@ -53,19 +51,16 @@
 #elif XP_MACOSX
 #include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
 
 using base::KillProcess;
 
 using mozilla::PluginLibrary;
-#ifdef MOZ_GECKO_PROFILER
-using mozilla::CrossProcessProfilerController;
-#endif
 using mozilla::ipc::MessageChannel;
 using mozilla::ipc::GeckoChildProcessHost;
 
 using namespace mozilla;
 using namespace mozilla::plugins;
 using namespace mozilla::plugins::parent;
 
 #ifdef MOZ_CRASHREPORTER
@@ -633,17 +628,17 @@ PluginModuleChromeParent::OnProcessLaunc
         if (NS_SUCCEEDED(mAsyncInitRv)) {
             mAsyncInitRv = NP_GetEntryPoints(mNPPIface,
                                              &mAsyncInitError);
         }
 #endif
     }
 
 #ifdef MOZ_GECKO_PROFILER
-    mProfilerController = MakeUnique<CrossProcessProfilerController>(this);
+    Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
 #endif
 }
 
 bool
 PluginModuleChromeParent::WaitForIPCConnection()
 {
     PluginProcessParent* process = Process();
     MOZ_ASSERT(process);
@@ -774,20 +769,16 @@ PluginModuleChromeParent::PluginModuleCh
 }
 
 PluginModuleChromeParent::~PluginModuleChromeParent()
 {
     if (!OkToCleanup()) {
         MOZ_CRASH("unsafe destruction");
     }
 
-#ifdef MOZ_GECKO_PROFILER
-    mProfilerController = nullptr;
-#endif
-
 #ifdef XP_WIN
     // If we registered for audio notifications, stop.
     mozilla::plugins::PluginUtilsWin::RegisterForAudioDeviceChanges(this,
                                                                     false);
 #endif
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
     mSandboxPermissions.RemovePermissionsForProcess(OtherPid());
@@ -3259,28 +3250,16 @@ PluginModuleChromeParent::OnCrash(DWORD 
             NS_ERROR("Failed to open child process when attempting kill.");
         }
     }
 }
 
 #endif // MOZ_CRASHREPORTER_INJECTOR
 
 mozilla::ipc::IPCResult
-PluginModuleChromeParent::RecvProfile(const nsCString& aProfile,
-                                      const bool& aIsExitProfile)
-{
-#ifdef MOZ_GECKO_PROFILER
-    if (mProfilerController) {
-        mProfilerController->RecvProfile(aProfile, aIsExitProfile);
-    }
-#endif
-    return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
 PluginModuleParent::AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet)
 {
     return IPC_FAIL_NO_REASON(this);
 }
 
 mozilla::ipc::IPCResult
 PluginModuleChromeParent::AnswerGetKeyState(const int32_t& aVirtKey,
                                             int16_t* aRet)
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -24,31 +24,25 @@
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
 #ifdef XP_WIN
 #include "nsWindowsHelpers.h"
 #if defined(MOZ_SANDBOX)
 #include "sandboxPermissions.h"
 #endif
 #endif
-#include "ProfilerControllingProcess.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 class nsPluginTag;
 
 namespace mozilla {
 
-#ifdef MOZ_GECKO_PROFILER
-class CrossProcessProfilerController;
-#endif
-
-
 namespace ipc {
 class CrashReporterHost;
 } // namespace ipc
 namespace layers {
 class TextureClientRecycleAllocator;
 } // namespace layers
 
 namespace plugins {
@@ -80,17 +74,16 @@ class FinishInjectorInitTask;
  * the chrome process. In addition, any content process using the plugin will
  * have its own PluginModuleParent. The subclasses PluginModuleChromeParent and
  * PluginModuleContentParent implement functionality that is specific to one
  * case or the other.
  */
 class PluginModuleParent
     : public PPluginModuleParent
     , public PluginLibrary
-    , public mozilla::ProfilerControllingProcess
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     , public CrashReporter::InjectorCrashCallback
 #endif
 {
 protected:
     typedef mozilla::PluginLibrary PluginLibrary;
 
     PPluginInstanceParent*
@@ -142,33 +135,16 @@ public:
 
     virtual nsresult GetRunID(uint32_t* aRunID) override;
     virtual void SetHasLocalInstance() override {
         mHadLocalInstance = true;
     }
 
     int GetQuirks() { return mQuirks; }
 
-    void SendStartProfiler(const ProfilerInitParams& aParams) override
-    {
-        Unused << PPluginModuleParent::SendStartProfiler(aParams);
-    }
-    void SendStopProfiler() override
-    {
-        Unused << PPluginModuleParent::SendStopProfiler();
-    }
-    void SendPauseProfiler(const bool& aPause) override
-    {
-        Unused << PPluginModuleParent::SendPauseProfiler(aPause);
-    }
-    void SendGatherProfile() override
-    {
-        Unused << PPluginModuleParent::SendGatherProfile();
-    }
-
 protected:
     virtual mozilla::ipc::RacyInterruptPolicy
     MediateInterruptRace(const MessageInfo& parent,
                          const MessageInfo& child) override
     {
         return MediateRace(parent, child);
     }
 
@@ -234,18 +210,16 @@ protected:
 protected:
     void SetChildTimeout(const int32_t aChildTimeout);
     static void TimeoutChanged(const char* aPref, void* aModule);
 
     virtual void UpdatePluginTimeout() {}
 
     virtual mozilla::ipc::IPCResult RecvNotifyContentModuleDestroyed() override { return IPC_OK(); }
 
-    virtual mozilla::ipc::IPCResult RecvProfile(const nsCString& aProfile, const bool& aIsExitProfile) override { return IPC_OK(); }
-
     virtual mozilla::ipc::IPCResult AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override;
 
     virtual mozilla::ipc::IPCResult RecvReturnClearSiteData(const NPError& aRv,
                                                             const uint64_t& aCallbackId) override;
 
     virtual mozilla::ipc::IPCResult RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
                                                             const uint64_t& aCallbackId) override;
 
@@ -528,19 +502,16 @@ class PluginModuleChromeParent
     SetContentParent(dom::ContentParent* aContentParent);
 
     bool
     SendAssociatePluginId();
 
     void CachedSettingChanged();
 
     virtual mozilla::ipc::IPCResult
-    RecvProfile(const nsCString& aProfile, const bool& aIsExitProfile) override;
-
-    virtual mozilla::ipc::IPCResult
     AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override;
 
     // Proxy GetOpenFileName/GetSaveFileName on Windows.
     virtual mozilla::ipc::IPCResult
     AnswerGetFileName(const GetFileNameFunc& aFunc,
                       const OpenFileNameIPC& aOfnIn,
                       OpenFileNameRetIPC* aOfnOut, bool* aResult) override;
 
@@ -685,19 +656,16 @@ private:
     nsresult            mAsyncInitRv;
     NPError             mAsyncInitError;
     // mContentParent is to be used ONLY during the IPC dance that occurs
     // when ContentParent::RecvLoadPlugin is called under async plugin init!
     // In other contexts it is *unsafe*, as there might be multiple content
     // processes in existence!
     dom::ContentParent* mContentParent;
     nsCOMPtr<nsIObserver> mPluginOfflineObserver;
-#ifdef MOZ_GECKO_PROFILER
-    UniquePtr<CrossProcessProfilerController> mProfilerController;
-#endif
     bool mIsBlocklisted;
     static bool sInstantiated;
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
     mozilla::SandboxPermissions mSandboxPermissions;
 #endif
 };
 
 } // namespace plugins
--- a/gfx/ipc/GPUChild.cpp
+++ b/gfx/ipc/GPUChild.cpp
@@ -15,19 +15,17 @@
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/gfx/gfxVars.h"
 #if defined(XP_WIN)
 # include "mozilla/gfx/DeviceManagerDx.h"
 #endif
 #include "mozilla/ipc/CrashReporterHost.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/Unused.h"
-#ifdef MOZ_GECKO_PROFILER
-#include "CrossProcessProfilerController.h"
-#endif
+#include "ProfilerParent.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace layers;
 
 GPUChild::GPUChild(GPUProcessHost* aHost)
  : mHost(aHost),
@@ -73,17 +71,17 @@ GPUChild::Init()
     mappings.AppendElement(LayerTreeIdMapping(aLayersId, aProcessId));
   });
 
   SendInit(prefs, updates, devicePrefs, mappings);
 
   gfxVars::AddReceiver(this);
 
 #ifdef MOZ_GECKO_PROFILER
-  mProfilerController = MakeUnique<CrossProcessProfilerController>(this);
+  Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
 #endif
 }
 
 void
 GPUChild::OnVarChanged(const GfxVarUpdate& aVar)
 {
   SendUpdateVar(aVar);
 }
@@ -200,27 +198,16 @@ GPUChild::RecvRecordChildEvents(nsTArray
 mozilla::ipc::IPCResult
 GPUChild::RecvNotifyDeviceReset(const GPUDeviceData& aData)
 {
   gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData);
   mHost->mListener->OnProcessDeviceReset(mHost);
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult
-GPUChild::RecvProfile(const nsCString& aProfile, const bool& aIsExitProfile)
-{
-#ifdef MOZ_GECKO_PROFILER
-  if (mProfilerController) {
-    mProfilerController->RecvProfile(aProfile, aIsExitProfile);
-  }
-#endif
-  return IPC_OK();
-}
-
 bool
 GPUChild::SendRequestMemoryReport(const uint32_t& aGeneration,
                                   const bool& aAnonymize,
                                   const bool& aMinimizeMemoryUsage,
                                   const MaybeFileDesc& aDMDFile)
 {
   mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
   Unused << PGPUChild::SendRequestMemoryReport(
@@ -246,40 +233,16 @@ GPUChild::RecvFinishMemoryReport(const u
   if (mMemoryReportRequest) {
     mMemoryReportRequest->Finish(aGeneration);
     mMemoryReportRequest = nullptr;
   }
   return IPC_OK();
 }
 
 void
-GPUChild::SendStartProfiler(const ProfilerInitParams& aParams)
-{
-  Unused << PGPUChild::SendStartProfiler(aParams);
-}
-
-void
-GPUChild::SendStopProfiler()
-{
-  Unused << PGPUChild::SendStopProfiler();
-}
-
-void
-GPUChild::SendPauseProfiler(const bool& aPause)
-{
-  Unused << PGPUChild::SendPauseProfiler(aPause);
-}
-
-void
-GPUChild::SendGatherProfile()
-{
-  Unused << PGPUChild::SendGatherProfile();
-}
-
-void
 GPUChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (aWhy == AbnormalShutdown) {
 #ifdef MOZ_CRASHREPORTER
     if (mCrashReporter) {
       mCrashReporter->GenerateCrashReport(OtherPid());
       mCrashReporter = nullptr;
     }
@@ -290,20 +253,16 @@ GPUChild::ActorDestroy(ActorDestroyReaso
 
     // Notify the Telemetry environment so that we can refresh and do a subsession split
     if (nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService()) {
       obsvc->NotifyObservers(nullptr, "compositor:process-aborted", nullptr);
     }
 
   }
 
-#ifdef MOZ_GECKO_PROFILER
-  mProfilerController = nullptr;
-#endif
-
   gfxVars::RemoveReceiver(this);
   mHost->OnChannelClosed();
 }
 
 class DeferredDeleteGPUChild : public Runnable
 {
 public:
   explicit DeferredDeleteGPUChild(UniquePtr<GPUChild>&& aChild)
--- a/gfx/ipc/GPUChild.h
+++ b/gfx/ipc/GPUChild.h
@@ -5,38 +5,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef _include_mozilla_gfx_ipc_GPUChild_h_
 #define _include_mozilla_gfx_ipc_GPUChild_h_
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/gfx/PGPUChild.h"
 #include "mozilla/gfx/gfxVarReceiver.h"
-#include "ProfilerControllingProcess.h"
 
 namespace mozilla {
 
-#ifdef MOZ_GECKO_PROFILER
-class CrossProcessProfilerController;
-#endif
-
 namespace ipc {
 class CrashReporterHost;
 } // namespace ipc
 namespace dom {
 class MemoryReportRequestHost;
 } // namespace dom
 namespace gfx {
 
 class GPUProcessHost;
 
 class GPUChild final
   : public PGPUChild
   , public gfxVarReceiver
-  , public ProfilerControllingProcess
 {
   typedef mozilla::dom::MemoryReportRequestHost MemoryReportRequestHost;
 
 public:
   explicit GPUChild(GPUProcessHost* aHost);
   ~GPUChild();
 
   void Init();
@@ -56,38 +50,29 @@ public:
   mozilla::ipc::IPCResult RecvUpdateChildScalars(InfallibleTArray<ScalarAction>&& aScalarActions) override;
   mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(InfallibleTArray<KeyedScalarAction>&& aScalarActions) override;
   mozilla::ipc::IPCResult RecvRecordChildEvents(nsTArray<ChildEventData>&& events) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& aError) override;
   mozilla::ipc::IPCResult RecvNotifyUiObservers(const nsCString& aTopic) override;
   mozilla::ipc::IPCResult RecvNotifyDeviceReset(const GPUDeviceData& aData) override;
-  mozilla::ipc::IPCResult RecvProfile(const nsCString& aProfile, const bool& aIsExitProfile) override;
   mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override;
   mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override;
 
-  void SendStartProfiler(const ProfilerInitParams& aParams) override;
-  void SendStopProfiler() override;
-  void SendPauseProfiler(const bool& aPause) override;
-  void SendGatherProfile() override;
-
   bool SendRequestMemoryReport(const uint32_t& aGeneration,
                                const bool& aAnonymize,
                                const bool& aMinimizeMemoryUsage,
                                const MaybeFileDesc& aDMDFile);
 
   static void Destroy(UniquePtr<GPUChild>&& aChild);
 
 private:
   GPUProcessHost* mHost;
   UniquePtr<ipc::CrashReporterHost> mCrashReporter;
   UniquePtr<MemoryReportRequestHost> mMemoryReportRequest;
   bool mGPUReady;
-#ifdef MOZ_GECKO_PROFILER
-  UniquePtr<CrossProcessProfilerController> mProfilerController;
-#endif
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _include_mozilla_gfx_ipc_GPUChild_h_
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -2,17 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifdef XP_WIN
 #include "WMF.h"
 #endif
 #include "GPUParent.h"
-#include "GeckoProfiler.h"
 #include "gfxConfig.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "GPUProcessHost.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/MemoryReportRequest.h"
@@ -40,16 +39,19 @@
 #include "VsyncBridgeParent.h"
 #if defined(XP_WIN)
 # include "mozilla/gfx/DeviceManagerDx.h"
 # include <process.h>
 #endif
 #ifdef MOZ_WIDGET_GTK
 # include <gtk/gtk.h>
 #endif
+#ifdef MOZ_GECKO_PROFILER
+#include "ChildProfilerController.h"
+#endif
 
 namespace mozilla {
 namespace gfx {
 
 using namespace ipc;
 using namespace layers;
 
 static GPUParent* sGPUParent;
@@ -236,16 +238,25 @@ GPUParent::RecvInitVRManager(Endpoint<PV
 mozilla::ipc::IPCResult
 GPUParent::RecvInitUiCompositorController(const uint64_t& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint)
 {
   UiCompositorControllerParent::Start(aRootLayerTreeId, Move(aEndpoint));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+GPUParent::RecvInitProfiler(Endpoint<PProfilerChild>&& aEndpoint)
+{
+#ifdef MOZ_GECKO_PROFILER
+  mProfilerController = new ChildProfilerController(Move(aEndpoint));
+#endif
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 GPUParent::RecvUpdatePref(const GfxPrefSetting& setting)
 {
   gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
   pref->SetCachedValue(setting.value());
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
@@ -378,61 +389,16 @@ GPUParent::RecvNotifyGpuObservers(const 
   MOZ_ASSERT(obsSvc);
   if (obsSvc) {
     obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-GPUParent::RecvStartProfiler(const ProfilerInitParams& params)
-{
-  nsTArray<const char*> filterArray;
-  for (size_t i = 0; i < params.filters().Length(); ++i) {
-    filterArray.AppendElement(params.filters()[i].get());
-  }
-  profiler_start(params.entries(), params.interval(), params.features(),
-                 filterArray.Elements(), filterArray.Length());
-
- return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-GPUParent::RecvStopProfiler()
-{
-  profiler_stop();
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-GPUParent::RecvPauseProfiler(const bool& aPause)
-{
-  if (aPause) {
-    profiler_pause();
-  } else {
-    profiler_resume();
-  }
-
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-GPUParent::RecvGatherProfile()
-{
-  nsCString profileCString;
-  UniquePtr<char[]> profile = profiler_get_profile();
-  if (profile) {
-    profileCString = nsDependentCString(profile.get());
-  }
-
-  Unused << SendProfile(profileCString, false /* aIsExitProfile */);
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
 GPUParent::RecvRequestMemoryReport(const uint32_t& aGeneration,
                                    const bool& aAnonymize,
                                    const bool& aMinimizeMemoryUsage,
                                    const MaybeFileDesc& aDMDFile)
 {
   nsPrintfCString processName("GPU (pid %u)", (unsigned)getpid());
 
   mozilla::dom::MemoryReportRequestClient::Start(
@@ -453,16 +419,23 @@ GPUParent::ActorDestroy(ActorDestroyReas
 #endif
 
 #ifndef NS_FREE_PERMANENT_DATA
   // No point in going through XPCOM shutdown because we don't keep persistent
   // state.
   ProcessChild::QuickExit();
 #endif
 
+#ifdef MOZ_GECKO_PROFILER
+  if (mProfilerController) {
+    mProfilerController->Shutdown();
+    mProfilerController = nullptr;
+  }
+#endif
+
   if (mVsyncBridge) {
     mVsyncBridge->Shutdown();
     mVsyncBridge = nullptr;
   }
   dom::VideoDecoderManagerParent::ShutdownVideoBridge();
   CompositorThreadHolder::Shutdown();
   if (gfxVars::UseWebRender()) {
     wr::RenderThread::ShutDown();
--- a/gfx/ipc/GPUParent.h
+++ b/gfx/ipc/GPUParent.h
@@ -7,16 +7,17 @@
 #define _include_gfx_ipc_GPUParent_h__
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/gfx/PGPUParent.h"
 
 namespace mozilla {
 
 class TimeStamp;
+class ChildProfilerController;
 
 namespace gfx {
 
 class VsyncBridgeParent;
 
 class GPUParent final : public PGPUParent
 {
 public:
@@ -33,16 +34,17 @@ public:
   mozilla::ipc::IPCResult RecvInit(nsTArray<GfxPrefSetting>&& prefs,
                                    nsTArray<GfxVarUpdate>&& vars,
                                    const DevicePrefs& devicePrefs,
                                    nsTArray<LayerTreeIdMapping>&& mappings) override;
   mozilla::ipc::IPCResult RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) override;
   mozilla::ipc::IPCResult RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvInitUiCompositorController(const uint64_t& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint) override;
+  mozilla::ipc::IPCResult RecvInitProfiler(Endpoint<PProfilerChild>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvUpdatePref(const GfxPrefSetting& pref) override;
   mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref) override;
   mozilla::ipc::IPCResult RecvNewWidgetCompositor(
       Endpoint<PCompositorBridgeParent>&& aEndpoint,
       const CSSToLayoutDeviceScale& aScale,
       const TimeDuration& aVsyncRate,
       const CompositorOptions& aOptions,
       const bool& aUseExternalSurface,
@@ -50,29 +52,28 @@ public:
   mozilla::ipc::IPCResult RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvNewContentVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvNewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvGetDeviceStatus(GPUDeviceData* aOutStatus) override;
   mozilla::ipc::IPCResult RecvAddLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override;
   mozilla::ipc::IPCResult RecvRemoveLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override;
   mozilla::ipc::IPCResult RecvNotifyGpuObservers(const nsCString& aTopic) override;
-  mozilla::ipc::IPCResult RecvStartProfiler(const ProfilerInitParams& params) override;
-  mozilla::ipc::IPCResult RecvPauseProfiler(const bool& aPause) override;
-  mozilla::ipc::IPCResult RecvStopProfiler() override;
-  mozilla::ipc::IPCResult RecvGatherProfile() override;
   mozilla::ipc::IPCResult RecvRequestMemoryReport(
     const uint32_t& generation,
     const bool& anonymize,
     const bool& minimizeMemoryUsage,
     const MaybeFileDesc& DMDFile) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
 private:
   const TimeStamp mLaunchTime;
   RefPtr<VsyncBridgeParent> mVsyncBridge;
+#ifdef MOZ_GECKO_PROFILER
+  RefPtr<ChildProfilerController> mProfilerController;
+#endif
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _include_gfx_ipc_GPUParent_h__
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include GraphicsMessages;
-include ProfilerTypes;
 include MemoryReportTypes;
 include protocol PCompositorBridge;
 include protocol PImageBridge;
+include protocol PProfiler;
 include protocol PVRManager;
 include protocol PVsyncBridge;
 include protocol PUiCompositorController;
 include protocol PVideoDecoderManager;
 
 using base::ProcessId from "base/process.h";
 using mozilla::TimeDuration from "mozilla/TimeStamp.h";
 using mozilla::CSSToLayoutDeviceScale from "Units.h";
@@ -54,16 +54,17 @@ parent:
              GfxVarUpdate[] vars,
              DevicePrefs devicePrefs,
              LayerTreeIdMapping[] mapping);
 
   async InitVsyncBridge(Endpoint<PVsyncBridgeParent> endpoint);
   async InitImageBridge(Endpoint<PImageBridgeParent> endpoint);
   async InitVRManager(Endpoint<PVRManagerParent> endpoint);
   async InitUiCompositorController(uint64_t rootLayerTreeId, Endpoint<PUiCompositorControllerParent> endpoint);
+  async InitProfiler(Endpoint<PProfilerChild> endpoint);
 
   // Called to update a gfx preference or variable.
   async UpdatePref(GfxPrefSetting pref);
   async UpdateVar(GfxVarUpdate var);
 
   // Create a new top-level compositor.
   async NewWidgetCompositor(Endpoint<PCompositorBridgeParent> endpoint,
                             CSSToLayoutDeviceScale scale,
@@ -85,22 +86,16 @@ parent:
   // Request the current DeviceStatus from the GPU process. This blocks until
   // one is available (i.e., Init has completed).
   sync GetDeviceStatus() returns (GPUDeviceData status);
 
   // Have a message be broadcasted to the GPU process by the GPU process
   // observer service.
   async NotifyGpuObservers(nsCString aTopic);
 
-  // Control the Gecko Profiler in the GPU process.
-  async StartProfiler(ProfilerInitParams params);
-  async StopProfiler();
-  async PauseProfiler(bool aPause);
-  async GatherProfile();
-
   async RequestMemoryReport(uint32_t generation,
                             bool anonymize,
                             bool minimizeMemoryUsage,
                             MaybeFileDesc DMDFile);
 
 child:
   // Sent when the GPU process has initialized devices. This occurs once, after
   // Init().
@@ -122,16 +117,14 @@ child:
   async AccumulateChildHistograms(Accumulation[] accumulations);
   async AccumulateChildKeyedHistograms(KeyedAccumulation[] accumulations);
   async UpdateChildScalars(ScalarAction[] actions);
   async UpdateChildKeyedScalars(KeyedScalarAction[] actions);
   async RecordChildEvents(ChildEventData[] events);
 
   async NotifyDeviceReset(GPUDeviceData status);
 
-  // Called in response to GatherProfile.
-  async Profile(nsCString aProfile, bool aIsExitProfile);
   async AddMemoryReport(MemoryReport aReport);
   async FinishMemoryReport(uint32_t aGeneration);
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -29,16 +29,17 @@
 #include "nsIObserverService.h"
 #include "nsIXULAppInfo.h"
 #include "nsIXULRuntime.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsMemoryReporterManager.h"
 #include "nsXULAppAPI.h"
 #include "nsProfilerStartParams.h"
+#include "ProfilerParent.h"
 #include "mozilla/Services.h"
 #include "nsThreadUtils.h"
 #include "ProfilerMarkerPayload.h"
 #include "shared-libraries.h"
 #include "prdtoa.h"
 #include "prtime.h"
 
 #ifdef MOZ_TASK_TRACER
@@ -2024,16 +2025,17 @@ NotifyProfilerStarted(const int aEntries
   nsTArray<nsCString> filtersArray;
   for (size_t i = 0; i < aFilterCount; ++i) {
     filtersArray.AppendElement(aFilters[i]);
   }
 
   nsCOMPtr<nsIProfilerStartParams> params =
     new nsProfilerStartParams(aEntries, aInterval, aFeatures, filtersArray);
 
+  ProfilerParent::ProfilerStarted(params);
   NotifyObserversAsync("profiler-started", params);
 }
 
 static void
 locked_profiler_start(PSLockRef aLock, const int aEntries, double aInterval,
                       uint32_t aFeatures,
                       const char** aFilters, uint32_t aFilterCount);
 
@@ -2169,16 +2171,17 @@ profiler_shutdown()
 #ifdef MOZ_TASK_TRACER
     mozilla::tasktracer::ShutdownTaskTracer();
 #endif
   }
 
   // We do these operations with gPSMutex unlocked. The comments in
   // profiler_stop() explain why.
   if (samplerThread) {
+    ProfilerParent::ProfilerStopped();
     NotifyObserversAsync("profiler-stopped");
     delete samplerThread;
   }
 }
 
 UniquePtr<char[]>
 profiler_get_profile(double aSinceTime)
 {
@@ -2423,16 +2426,17 @@ profiler_start(int aEntries, double aInt
 
     locked_profiler_start(lock, aEntries, aInterval, aFeatures,
                           aFilters, aFilterCount);
   }
 
   // We do these operations with gPSMutex unlocked. The comments in
   // profiler_stop() explain why.
   if (samplerThread) {
+    ProfilerParent::ProfilerStopped();
     NotifyObserversAsync("profiler-stopped");
     delete samplerThread;
   }
   NotifyProfilerStarted(aEntries, aInterval, aFeatures,
                         aFilters, aFilterCount);
 }
 
 static MOZ_MUST_USE SamplerThread*
@@ -2500,16 +2504,17 @@ profiler_stop()
     samplerThread = locked_profiler_stop(lock);
   }
 
   // We notify observers with gPSMutex unlocked. Otherwise we might get a
   // deadlock, if code run by these functions calls a profiler function that
   // locks gPSMutex, for example when it wants to insert a marker.
   // (This has been seen in practise in bug 1346356, when we were still firing
   // these notifications synchronously.)
+  ProfilerParent::ProfilerStopped();
   NotifyObserversAsync("profiler-stopped");
 
   // We delete with gPSMutex unlocked. Otherwise we would get a deadlock: we
   // would be waiting here with gPSMutex locked for SamplerThread::Run() to
   // return so the join operation within the destructor can complete, but Run()
   // needs to lock gPSMutex to return.
   //
   // Because this call occurs with gPSMutex unlocked, it -- including the final
@@ -2546,16 +2551,17 @@ profiler_pause()
     if (!ActivePS::Exists(lock)) {
       return;
     }
 
     ActivePS::SetIsPaused(lock, true);
   }
 
   // gPSMutex must be unlocked when we notify, to avoid potential deadlocks.
+  ProfilerParent::ProfilerPaused();
   NotifyObserversAsync("profiler-paused");
 }
 
 void
 profiler_resume()
 {
   LOG("profiler_resume");
 
@@ -2567,16 +2573,17 @@ profiler_resume()
     if (!ActivePS::Exists(lock)) {
       return;
     }
 
     ActivePS::SetIsPaused(lock, false);
   }
 
   // gPSMutex must be unlocked when we notify, to avoid potential deadlocks.
+  ProfilerParent::ProfilerResumed();
   NotifyObserversAsync("profiler-resumed");
 }
 
 bool
 profiler_feature_active(uint32_t aFeature)
 {
   // This function runs both on and off the main thread.
 
new file mode 100644
--- /dev/null
+++ b/tools/profiler/gecko/ChildProfilerController.cpp
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ChildProfilerController.h"
+#include "nsThreadUtils.h"
+#include "ProfilerChild.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+
+ChildProfilerController::ChildProfilerController(Endpoint<PProfilerChild>&& aEndpoint)
+{
+  MOZ_COUNT_CTOR(ChildProfilerController);
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  if (NS_SUCCEEDED(NS_NewNamedThread("ProfilerChild", getter_AddRefs(mThread)))) {
+    // Now that mThread has been set, run SetupProfilerChild on the thread.
+    mThread->Dispatch(NewRunnableMethod<Endpoint<PProfilerChild>&&>(
+      this, &ChildProfilerController::SetupProfilerChild, Move(aEndpoint)),
+      NS_DISPATCH_NORMAL);
+  }
+}
+
+nsCString
+ChildProfilerController::GrabShutdownProfileAndShutdown()
+{
+  nsCString shutdownProfile;
+  ShutdownAndMaybeGrabShutdownProfileFirst(&shutdownProfile);
+  return shutdownProfile;
+}
+
+void
+ChildProfilerController::Shutdown()
+{
+  ShutdownAndMaybeGrabShutdownProfileFirst(nullptr);
+}
+
+void
+ChildProfilerController::ShutdownAndMaybeGrabShutdownProfileFirst(nsCString* aOutShutdownProfile)
+{
+  if (mThread) {
+    mThread->Dispatch(NewRunnableMethod<nsCString*>(
+      this, &ChildProfilerController::ShutdownProfilerChild, aOutShutdownProfile),
+      NS_DISPATCH_NORMAL);
+    // Shut down the thread. This call will spin until all runnables (including
+    // the ShutdownProfilerChild runnable) have been processed.
+    mThread->Shutdown();
+    mThread = nullptr;
+  }
+}
+
+ChildProfilerController::~ChildProfilerController()
+{
+  MOZ_COUNT_DTOR(ChildProfilerController);
+
+  MOZ_ASSERT(!mThread, "Please call Shutdown before destroying ChildProfilerController");
+  MOZ_ASSERT(!mProfilerChild);
+}
+
+void
+ChildProfilerController::SetupProfilerChild(Endpoint<PProfilerChild>&& aEndpoint)
+{
+  MOZ_RELEASE_ASSERT(mThread == NS_GetCurrentThread());
+  MOZ_ASSERT(aEndpoint.IsValid());
+
+  mProfilerChild = new ProfilerChild();
+  Endpoint<PProfilerChild> endpoint = Move(aEndpoint);
+
+  if (!endpoint.Bind(mProfilerChild)) {
+    MOZ_CRASH("Failed to bind ProfilerChild!");
+  }
+}
+
+void
+ChildProfilerController::ShutdownProfilerChild(nsCString* aOutShutdownProfile)
+{
+  MOZ_RELEASE_ASSERT(mThread == NS_GetCurrentThread());
+
+  if (aOutShutdownProfile) {
+    *aOutShutdownProfile = mProfilerChild->GrabShutdownProfile();
+  }
+  mProfilerChild->Destroy();
+  mProfilerChild = nullptr;
+}
+
+} // namespace mozilla
deleted file mode 100644
--- a/tools/profiler/gecko/CrossProcessProfilerController.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "CrossProcessProfilerController.h"
-
-#include "mozilla/Move.h"
-#include "mozilla/ProfilerTypes.h"
-#include "nsIProfiler.h"
-#include "nsISupports.h"
-#include "nsIObserver.h"
-#include "nsProfiler.h"
-#include "ProfilerControllingProcess.h"
-
-namespace mozilla {
-
-static const char* sObserverTopics[] = {
-  "profiler-started",
-  "profiler-stopped",
-  "profiler-paused",
-  "profiler-resumed",
-  "profiler-subprocess-gather",
-};
-
-// ProfilerObserver is a refcounted class that gets registered with the
-// observer service and just forwards Observe() calls to mController.
-// This indirection makes the CrossProcessProfilerController API nicer because
-// it doesn't require a separate Init() method to register with the observer
-// service, and because not being refcounted allows CPPC to be managed with a
-// UniquePtr.
-// The life time of ProfilerObserver is bounded by the life time of CPPC: CPPC
-// unregisters the ProfilerObserver from the observer service in its
-// destructor, and then it drops its reference to the ProfilerObserver, which
-// destroys the ProfilerObserver.
-class ProfilerObserver final : public nsIObserver
-{
-public:
-  explicit ProfilerObserver(CrossProcessProfilerController& aController)
-    : mController(aController)
-  {}
-
-  NS_DECL_ISUPPORTS
-
-  NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
-                     const char16_t* aData) override
-  {
-    mController.Observe(aSubject, aTopic);
-    return NS_OK;
-  }
-
-private:
-  ~ProfilerObserver() {}
-
-  CrossProcessProfilerController& mController;
-};
-
-NS_IMPL_ISUPPORTS(ProfilerObserver, nsIObserver)
-
-CrossProcessProfilerController::CrossProcessProfilerController(
-  ProfilerControllingProcess* aProcess)
-  : mProcess(aProcess)
-  , mObserver(new ProfilerObserver(*this))
-{
-  if (profiler_is_active()) {
-    // If the profiler is already running in this process, start it in the
-    // child process immediately.
-    nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
-    nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
-    DebugOnly<nsresult> rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-    StartProfiler(currentProfilerParams);
-  }
-
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    size_t length = ArrayLength(sObserverTopics);
-    for (size_t i = 0; i < length; ++i) {
-      obs->AddObserver(mObserver, sObserverTopics[i], false);
-    }
-  }
-}
-
-CrossProcessProfilerController::~CrossProcessProfilerController()
-{
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    size_t length = ArrayLength(sObserverTopics);
-    for (size_t i = 0; i < length; ++i) {
-      obs->RemoveObserver(mObserver, sObserverTopics[i]);
-    }
-  }
-}
-
-void
-CrossProcessProfilerController::StartProfiler(nsIProfilerStartParams* aParams)
-{
-  if (NS_WARN_IF(!aParams)) {
-    return;
-  }
-
-  ProfilerInitParams ipcParams;
-
-  ipcParams.enabled() = true;
-  aParams->GetEntries(&ipcParams.entries());
-  aParams->GetInterval(&ipcParams.interval());
-  aParams->GetFeatures(&ipcParams.features());
-  ipcParams.filters() = aParams->GetFilters();
-
-  mProcess->SendStartProfiler(ipcParams);
-}
-
-void
-CrossProcessProfilerController::Observe(nsISupports* aSubject,
-                                        const char* aTopic)
-{
-  if (!strcmp(aTopic, "profiler-subprocess-gather")) {
-    // profiler-subprocess-gather is the request to capture the profile. We
-    // need to tell the other process that we're interested in its profile,
-    // and we tell the gatherer that we've forwarded the request, so that it
-    // can keep track of the number of pending profiles.
-    nsProfiler::GetOrCreate()->WillGatherOOPProfile();
-    mProcess->SendGatherProfile();
-  }
-  // These four notifications are sent by the profiler when its corresponding
-  // methods are called inside this process. These state changes just need to
-  // be forwarded to the other process.
-  else if (!strcmp(aTopic, "profiler-started")) {
-    nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
-    StartProfiler(params);
-  }
-  else if (!strcmp(aTopic, "profiler-stopped")) {
-    mProcess->SendStopProfiler();
-  }
-  else if (!strcmp(aTopic, "profiler-paused")) {
-    mProcess->SendPauseProfiler(true);
-  }
-  else if (!strcmp(aTopic, "profiler-resumed")) {
-    mProcess->SendPauseProfiler(false);
-  }
-}
-
-// This is called in response to a SendGatherProfile request, or when the
-// other process exits while the profiler is running.
-void
-CrossProcessProfilerController::RecvProfile(const nsCString& aProfile,
-                                            bool aIsExitProfile)
-{
-  // Pass our process's profile along to nsProfiler.
-  if (aIsExitProfile) {
-    nsProfiler::GetOrCreate()->OOPExitProfile(aProfile);
-  } else {
-    nsProfiler::GetOrCreate()->GatheredOOPProfile(aProfile);
-  }
-}
-
-} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/tools/profiler/gecko/PProfiler.ipdl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include ProfilerTypes;
+
+namespace mozilla {
+
+// PProfiler is a top-level protocol. It is used to let the main process
+// control the Gecko Profiler in other processes, and request profiles from
+// those processes.
+// It is a top-level protocol so that its child endpoint can be on a
+// background thread, so that profiles can be gathered even if the main thread
+// is unresponsive.
+async protocol PProfiler
+{
+child:
+  async Start(ProfilerInitParams params);
+  async Stop();
+  async Pause();
+  async Resume();
+
+  async GatherProfile();
+
+parent:
+  async Profile(nsCString aProfile);
+};
+
+} // namespace mozilla
+
--- a/tools/profiler/gecko/ProfileGatherer.cpp
+++ b/tools/profiler/gecko/ProfileGatherer.cpp
@@ -55,48 +55,36 @@ ProfileGatherer::GatheredOOPProfile(cons
 
   if (mPendingProfiles == 0) {
     // We've got all of the async profiles now. Let's
     // finish off the profile and resolve the Promise.
     Finish();
   }
 }
 
-void
-ProfileGatherer::WillGatherOOPProfile()
-{
-  mPendingProfiles++;
-}
-
 RefPtr<ProfileGatherer::ProfileGatherPromise>
 ProfileGatherer::Start(double aSinceTime)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (mGathering) {
     // If we're already gathering, return a rejected promise - this isn't
     // going to end well.
     return ProfileGatherPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
   }
 
   mGathering = true;
-  mPendingProfiles = 0;
 
-  // Send a notification to request profiles from other processes. The
-  // observers of this notification will call WillGatherOOPProfile() which
-  // increments mPendingProfiles.
+  // Request profiles from the other processes. This will trigger
+  // asynchronous calls to ProfileGatherer::GatheredOOPProfile as the
+  // profiles arrive.
   // Do this before the call to profiler_stream_json_for_this_process because
   // that call is slow and we want to let the other processes grab their
   // profiles as soon as possible.
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os) {
-    DebugOnly<nsresult> rv =
-      os->NotifyObservers(this, "profiler-subprocess-gather", nullptr);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyObservers failed");
-  }
+  mPendingProfiles = ProfilerParent::GatherProfiles();
 
   mWriter.emplace();
 
   // Start building up the JSON result and grab the profile from this process.
   mWriter->Start(SpliceableJSONWriter::SingleLineStyle);
   if (!profiler_stream_json_for_this_process(*mWriter, aSinceTime)) {
     // The profiler is inactive. This either means that it was inactive even
     // at the time that ProfileGatherer::Start() was called, or that it was
--- a/tools/profiler/gecko/ProfileGatherer.h
+++ b/tools/profiler/gecko/ProfileGatherer.h
@@ -15,17 +15,16 @@ namespace mozilla {
 class ProfileGatherer final : public nsISupports
 {
 public:
   NS_DECL_ISUPPORTS
 
   typedef MozPromise<nsCString, nsresult, false> ProfileGatherPromise;
 
   explicit ProfileGatherer();
-  void WillGatherOOPProfile();
   void GatheredOOPProfile(const nsACString& aProfile);
   RefPtr<ProfileGatherPromise> Start(double aSinceTime);
   void OOPExitProfile(const nsACString& aProfile);
 
 private:
   ~ProfileGatherer();
   void Cancel();
   void Finish();
new file mode 100644
--- /dev/null
+++ b/tools/profiler/gecko/ProfilerChild.cpp
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GeckoProfiler.h"
+#include "ProfilerChild.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+ProfilerChild::ProfilerChild()
+  : mThread(NS_GetCurrentThread())
+  , mDestroyed(false)
+{
+  MOZ_COUNT_CTOR(ProfilerChild);
+}
+
+ProfilerChild::~ProfilerChild()
+{
+  MOZ_COUNT_DTOR(ProfilerChild);
+}
+
+mozilla::ipc::IPCResult
+ProfilerChild::RecvStart(const ProfilerInitParams& params)
+{
+  nsTArray<const char*> filterArray;
+  for (size_t i = 0; i < params.filters().Length(); ++i) {
+    filterArray.AppendElement(params.filters()[i].get());
+  }
+
+  profiler_start(params.entries(), params.interval(),
+                 params.features(),
+                 filterArray.Elements(),
+                 filterArray.Length());
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ProfilerChild::RecvStop()
+{
+  profiler_stop();
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ProfilerChild::RecvPause()
+{
+  profiler_pause();
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ProfilerChild::RecvResume()
+{
+  profiler_resume();
+  return IPC_OK();
+}
+
+static nsCString
+CollectProfileOrEmptyString()
+{
+  nsCString profileCString;
+  UniquePtr<char[]> profile = profiler_get_profile();
+  if (profile) {
+    profileCString = nsCString(profile.get(), strlen(profile.get()));
+  } else {
+    profileCString = EmptyCString();
+  }
+  return profileCString;
+}
+
+mozilla::ipc::IPCResult
+ProfilerChild::RecvGatherProfile()
+{
+  if (!mDestroyed) {
+    Unused << SendProfile(CollectProfileOrEmptyString());
+  }
+  return IPC_OK();
+}
+
+void
+ProfilerChild::ActorDestroy(ActorDestroyReason aActorDestroyReason)
+{
+  mDestroyed = true;
+}
+
+void
+ProfilerChild::Destroy()
+{
+  if (!mDestroyed) {
+    Close();
+  }
+}
+
+nsCString
+ProfilerChild::GrabShutdownProfile()
+{
+  return CollectProfileOrEmptyString();
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/tools/profiler/gecko/ProfilerParent.cpp
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ProfilerParent.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Unused.h"
+
+#include "nsProfiler.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+class ProfilerParentTracker final {
+public:
+  static void StartTracking(ProfilerParent* aParent);
+  static void StopTracking(ProfilerParent* aParent);
+
+  template<typename FuncType>
+  static void Enumerate(FuncType aIterFunc);
+
+  ProfilerParentTracker();
+  ~ProfilerParentTracker();
+
+private:
+  nsTArray<ProfilerParent*> mProfilerParents;
+  static UniquePtr<ProfilerParentTracker> sInstance;
+};
+
+UniquePtr<ProfilerParentTracker> ProfilerParentTracker::sInstance;
+
+/* static */ void
+ProfilerParentTracker::StartTracking(ProfilerParent* aProfilerParent)
+{
+  if (!sInstance) {
+    sInstance = MakeUnique<ProfilerParentTracker>();
+    ClearOnShutdown(&sInstance);
+  }
+  sInstance->mProfilerParents.AppendElement(aProfilerParent);
+}
+
+/* static */ void
+ProfilerParentTracker::StopTracking(ProfilerParent* aParent)
+{
+  if (sInstance) {
+    sInstance->mProfilerParents.RemoveElement(aParent);
+  }
+}
+
+template<typename FuncType>
+/* static */ void
+ProfilerParentTracker::Enumerate(FuncType aIterFunc)
+{
+  if (sInstance) {
+    for (ProfilerParent* profilerParent : sInstance->mProfilerParents) {
+      if (!profilerParent->mDestroyed) {
+        aIterFunc(profilerParent);
+      }
+    }
+  }
+}
+
+ProfilerParentTracker::ProfilerParentTracker()
+{
+  MOZ_COUNT_CTOR(ProfilerParentTracker);
+}
+
+ProfilerParentTracker::~ProfilerParentTracker()
+{
+  MOZ_COUNT_DTOR(ProfilerParentTracker);
+
+  nsTArray<ProfilerParent*> parents;
+  parents = mProfilerParents;
+  // Close the channels of any profiler parents that haven't been destroyed.
+  for (ProfilerParent* profilerParent : parents) {
+    if (!profilerParent->mDestroyed) {
+      // Keep the object alive until the call to Close() has completed.
+      // Close() will trigger a call to DeallocPProfilerParent.
+      RefPtr<ProfilerParent> actor = profilerParent;
+      actor->Close();
+    }
+  }
+}
+
+/* static */ Endpoint<PProfilerChild>
+ProfilerParent::CreateForProcess(base::ProcessId aOtherPid)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  Endpoint<PProfilerParent> parent;
+  Endpoint<PProfilerChild> child;
+  nsresult rv = PProfiler::CreateEndpoints(base::GetCurrentProcId(),
+                                           aOtherPid,
+                                           &parent, &child);
+
+  if (NS_FAILED(rv)) {
+    MOZ_CRASH("Failed to create top level actor for PProfiler!");
+  }
+
+  RefPtr<ProfilerParent> actor = new ProfilerParent();
+  if (!parent.Bind(actor)) {
+    MOZ_CRASH("Failed to bind parent actor for PProfiler!");
+  }
+
+  // mSelfRef will be cleared in DeallocPProfilerParent.
+  actor->mSelfRef = actor;
+  actor->Init();
+
+  return child;
+}
+
+ProfilerParent::ProfilerParent()
+  : mPendingRequestedProfilesCount(0)
+  , mDestroyed(false)
+{
+  MOZ_COUNT_CTOR(ProfilerParent);
+
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+}
+
+void
+ProfilerParent::Init()
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+  ProfilerParentTracker::StartTracking(this);
+
+  if (profiler_is_active()) {
+    // If the profiler is already running in this process, start it in the
+    // child process immediately.
+    int entries = 0;
+    double interval = 0;
+    mozilla::Vector<const char*> filters;
+    uint32_t features;
+    profiler_get_start_params(&entries, &interval, &features, &filters);
+
+    ProfilerInitParams ipcParams;
+    ipcParams.enabled() = true;
+    ipcParams.entries() = entries;
+    ipcParams.interval() = interval;
+    ipcParams.features() = features;
+
+    for (uint32_t i = 0; i < filters.length(); ++i) {
+      ipcParams.filters().AppendElement(filters[i]);
+    }
+
+    Unused << SendStart(ipcParams);
+  }
+}
+
+ProfilerParent::~ProfilerParent()
+{
+  MOZ_COUNT_DTOR(ProfilerParent);
+
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  ProfilerParentTracker::StopTracking(this);
+}
+
+/* static */ uint32_t
+ProfilerParent::GatherProfiles()
+{
+  if (!NS_IsMainThread()) {
+    return 0;
+  }
+
+  uint32_t count = 0;
+  ProfilerParentTracker::Enumerate([&](ProfilerParent* profilerParent) {
+    profilerParent->mPendingRequestedProfilesCount++;
+    Unused << profilerParent->SendGatherProfile();
+    count++;
+  });
+  return count;
+}
+
+/* static */ void
+ProfilerParent::ProfilerStarted(nsIProfilerStartParams* aParams)
+{
+  if (!NS_IsMainThread()) {
+    return;
+  }
+
+  ProfilerInitParams ipcParams;
+  ipcParams.enabled() = true;
+  aParams->GetEntries(&ipcParams.entries());
+  aParams->GetInterval(&ipcParams.interval());
+  aParams->GetFeatures(&ipcParams.features());
+  ipcParams.filters() = aParams->GetFilters();
+
+  ProfilerParentTracker::Enumerate([&](ProfilerParent* profilerParent) {
+    Unused << profilerParent->SendStart(ipcParams);
+  });
+}
+
+/* static */ void
+ProfilerParent::ProfilerStopped()
+{
+  if (!NS_IsMainThread()) {
+    return;
+  }
+
+  ProfilerParentTracker::Enumerate([](ProfilerParent* profilerParent) {
+    Unused << profilerParent->SendStop();
+  });
+}
+
+/* static */ void
+ProfilerParent::ProfilerPaused()
+{
+  if (!NS_IsMainThread()) {
+    return;
+  }
+
+  ProfilerParentTracker::Enumerate([](ProfilerParent* profilerParent) {
+    Unused << profilerParent->SendPause();
+  });
+}
+
+/* static */ void
+ProfilerParent::ProfilerResumed()
+{
+  if (!NS_IsMainThread()) {
+    return;
+  }
+
+  ProfilerParentTracker::Enumerate([](ProfilerParent* profilerParent) {
+    Unused << profilerParent->SendResume();
+  });
+}
+
+mozilla::ipc::IPCResult
+ProfilerParent::RecvProfile(const nsCString& aProfile)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  MOZ_RELEASE_ASSERT(mPendingRequestedProfilesCount > 0);
+  mPendingRequestedProfilesCount--;
+  nsProfiler::GetOrCreate()->GatheredOOPProfile(aProfile);
+  return IPC_OK();
+}
+
+void
+ProfilerParent::ActorDestroy(ActorDestroyReason aActorDestroyReason)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+  RefPtr<nsProfiler> profiler = nsProfiler::GetOrCreate();
+  while (mPendingRequestedProfilesCount > 0) {
+    profiler->GatheredOOPProfile(NS_LITERAL_CSTRING(""));
+    mPendingRequestedProfilesCount--;
+  }
+  mDestroyed = true;
+}
+
+void
+ProfilerParent::DeallocPProfilerParent()
+{
+  mSelfRef = nullptr;
+}
+
+} // namespace mozilla
--- a/tools/profiler/gecko/nsIProfiler.idl
+++ b/tools/profiler/gecko/nsIProfiler.idl
@@ -5,16 +5,17 @@
 
 #include "nsISupports.idl"
 
 %{C++
 #include "nsTArrayForwardDeclare.h"
 class nsCString;
 %}
 
+[ref] native nsCString(const nsCString);
 [ref] native StringArrayRef(const nsTArray<nsCString>);
 
 /**
  * Start-up parameters for subprocesses are passed through nsIObserverService,
  * which, unfortunately, means we need to implement nsISupports in order to
  * go through it.
  */
 [builtinclass, uuid(0a175ba7-8fcf-4ce9-9c4b-ccc6272f4425)]
@@ -105,9 +106,12 @@ interface nsIProfiler : nsISupports
    */
   [implicit_jscontext]
   readonly attribute jsval sharedLibraries;
 
   /**
    * Dump the collected profile to a file.
    */
   void dumpProfileToFile(in string aFilename);
+
+  [noscript, notxpcom, nostdcall]
+  void receiveShutdownProfile(in nsCString aProfile);
 };
--- a/tools/profiler/gecko/nsProfiler.cpp
+++ b/tools/profiler/gecko/nsProfiler.cpp
@@ -487,43 +487,30 @@ nsProfiler::GetBufferInfo(uint32_t* aCur
   MOZ_ASSERT(aCurrentPosition);
   MOZ_ASSERT(aTotalSize);
   MOZ_ASSERT(aGeneration);
   profiler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration);
   return NS_OK;
 }
 
 void
-nsProfiler::WillGatherOOPProfile()
-{
-  MOZ_RELEASE_ASSERT(NS_IsMainThread());
-
-  if (!mGatherer) {
-    return;
-  }
-
-  mGatherer->WillGatherOOPProfile();
-}
-
-void
 nsProfiler::GatheredOOPProfile(const nsACString& aProfile)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!mGatherer) {
     return;
   }
 
   mGatherer->GatheredOOPProfile(aProfile);
 }
 
 void
-nsProfiler::OOPExitProfile(const nsACString& aProfile)
+nsProfiler::ReceiveShutdownProfile(const nsCString& aProfile)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!mGatherer) {
     return;
   }
 
   mGatherer->OOPExitProfile(aProfile);
 }
-
--- a/tools/profiler/gecko/nsProfiler.h
+++ b/tools/profiler/gecko/nsProfiler.h
@@ -28,19 +28,17 @@ public:
 
     static nsProfiler* GetOrCreate()
     {
 	nsCOMPtr<nsIProfiler> iprofiler =
 	    do_GetService("@mozilla.org/tools/profiler;1");
 	return static_cast<nsProfiler*>(iprofiler.get());
     }
 
-    void WillGatherOOPProfile();
     void GatheredOOPProfile(const nsACString& aProfile);
-    void OOPExitProfile(const nsACString& aProfile);
 
 private:
     ~nsProfiler();
 
     RefPtr<mozilla::ProfileGatherer> mGatherer;
     bool mLockedForPrivateBrowsing;
 };
 
--- a/tools/profiler/gecko/nsProfilerStartParams.h
+++ b/tools/profiler/gecko/nsProfilerStartParams.h
@@ -8,17 +8,17 @@
 
 #include "nsIProfiler.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 class nsProfilerStartParams : public nsIProfilerStartParams
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIPROFILERSTARTPARAMS
 
   nsProfilerStartParams(uint32_t aEntries,
                         double aInterval,
                         uint32_t aFeatures,
                         const nsTArray<nsCString>& aFilters);
 
 private:
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -5,34 +5,38 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['MOZ_GECKO_PROFILER']:
     XPIDL_MODULE = 'profiler'
     XPIDL_SOURCES += [
         'gecko/nsIProfiler.idl',
     ]
     EXPORTS += [
-        'public/CrossProcessProfilerController.h',
+        'public/ChildProfilerController.h',
+        'public/ProfilerChild.h',
         'public/ProfilerMarkerPayload.h',
+        'public/ProfilerParent.h',
         'public/shared-libraries.h',
     ]
     UNIFIED_SOURCES += [
         'core/platform.cpp',
         'core/ProfileBuffer.cpp',
         'core/ProfileBufferEntry.cpp',
         'core/ProfileJSONWriter.cpp',
         'core/ProfilerBacktrace.cpp',
         'core/ProfilerMarkerPayload.cpp',
         'core/StackTop.cpp',
         'core/ThreadInfo.cpp',
-        'gecko/CrossProcessProfilerController.cpp',
+        'gecko/ChildProfilerController.cpp',
         'gecko/nsProfilerFactory.cpp',
         'gecko/nsProfilerStartParams.cpp',
         'gecko/ProfileGatherer.cpp',
+        'gecko/ProfilerChild.cpp',
         'gecko/ProfilerIOInterposeObserver.cpp',
+        'gecko/ProfilerParent.cpp',
         'gecko/ThreadResponsiveness.cpp',
     ]
     if CONFIG['OS_TARGET'] == 'Darwin':
         # This file cannot be built in unified mode because it includes
         # "nsLocalFile.h", which pulls in a system header which uses a type
         # called TextRange, which conflicts with mozilla::TextRange due to
         # a "using namespace mozilla;" declaration from a different file.
         SOURCES += [
@@ -103,25 +107,25 @@ if CONFIG['MOZ_GECKO_PROFILER']:
         DEFINES['ARCH_ARMV6'] = True
 
     if CONFIG['ENABLE_TESTS']:
         DIRS += ['tests/gtest']
 
     FINAL_LIBRARY = 'xul'
 
 IPDL_SOURCES += [
+    'gecko/PProfiler.ipdl',
     'gecko/ProfilerTypes.ipdlh',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 EXPORTS += [
     'public/GeckoProfiler.h',
     'public/GeckoProfilerReporter.h',
-    'public/ProfilerControllingProcess.h',
 ]
 
 if CONFIG['MOZ_TASK_TRACER']:
     EXPORTS += [
         'tasktracer/GeckoTaskTracer.h',
         'tasktracer/GeckoTaskTracerImpl.h',
         'tasktracer/SourceEventTypeMap.h',
         'tasktracer/TracedTaskCommon.h',
new file mode 100644
--- /dev/null
+++ b/tools/profiler/public/ChildProfilerController.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ChildProfilerController_h
+#define ChildProfilerController_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/RefPtr.h"
+#include "base/process.h"
+
+class nsCString;
+
+namespace mozilla {
+
+class ProfilerChild;
+class PProfilerChild;
+class PProfilerParent;
+
+// ChildProfilerController manages the setup and teardown of ProfilerChild.
+// It's used on the main thread.
+// It manages a background thread that ProfilerChild runs on.
+class ChildProfilerController final
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChildProfilerController)
+
+  explicit ChildProfilerController(mozilla::ipc::Endpoint<PProfilerChild>&& aEndpoint);
+  MOZ_MUST_USE nsCString GrabShutdownProfileAndShutdown();
+  void Shutdown();
+
+private:
+  ~ChildProfilerController();
+  void ShutdownAndMaybeGrabShutdownProfileFirst(nsCString* aOutShutdownProfile);
+
+  // Called on mThread:
+  void SetupProfilerChild(mozilla::ipc::Endpoint<PProfilerChild>&& aEndpoint);
+  void ShutdownProfilerChild(nsCString* aOutShutdownProfile);
+
+  RefPtr<ProfilerChild> mProfilerChild; // only accessed on mThread
+  RefPtr<nsIThread> mThread;
+};
+
+} // namespace mozilla
+
+#endif  // ChildProfilerController_h
deleted file mode 100644
--- a/tools/profiler/public/CrossProcessProfilerController.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef CrossProcessProfilerController_h
-#define CrossProcessProfilerController_h
-
-#include "nsString.h"
-
-class nsIProfilerStartParams;
-
-namespace mozilla {
-
-class ProfilerObserver;
-class ProfilerControllingProcess;
-
-// A class that calls methods on aProcess to coordinate the profiler in other
-// processes. aProcess needs to implement a number of calls that trigger calls
-// to the relevant profiler_* functions in the other process.
-// RecvProfile needs to be called when the other process replies with its
-// profile.
-class CrossProcessProfilerController final
-{
-public:
-  // aProcess is expected to outlast this CrossProcessProfilerController object.
-  explicit CrossProcessProfilerController(ProfilerControllingProcess* aProcess);
-  ~CrossProcessProfilerController();
-  void RecvProfile(const nsCString& aProfile, bool aIsExitProfile);
-
-private:
-  void StartProfiler(nsIProfilerStartParams* aParams);
-  void Observe(nsISupports* aSubject, const char* aTopic);
-
-  friend class ProfilerObserver;
-
-  ProfilerControllingProcess* mProcess;
-  RefPtr<ProfilerObserver> mObserver;
-};
-
-} // namespace mozilla
-
-#endif
new file mode 100644
--- /dev/null
+++ b/tools/profiler/public/ProfilerChild.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ProfilerChild_h
+#define ProfilerChild_h
+
+#include "mozilla/PProfilerChild.h"
+#include "mozilla/RefPtr.h"
+
+class nsIThread;
+
+namespace mozilla {
+
+// The ProfilerChild actor is created in all processes except for the main
+// process. The corresponding ProfilerParent actor is created in the main
+// process, and it will notify us about profiler state changes and request
+// profiles from us.
+class ProfilerChild final : public PProfilerChild
+{
+  NS_INLINE_DECL_REFCOUNTING(ProfilerChild)
+
+  ProfilerChild();
+
+  // Collects and returns a profile.
+  // This method can be used to grab a profile just before PProfiler is torn
+  // down. The collected profile should then be sent through a different
+  // message channel that is guaranteed to stay open long enough.
+  nsCString GrabShutdownProfile();
+
+  void Destroy();
+
+private:
+  virtual ~ProfilerChild();
+
+  mozilla::ipc::IPCResult RecvStart(const ProfilerInitParams& params) override;
+  mozilla::ipc::IPCResult RecvStop() override;
+  mozilla::ipc::IPCResult RecvPause() override;
+  mozilla::ipc::IPCResult RecvResume() override;
+  mozilla::ipc::IPCResult RecvGatherProfile() override;
+
+  void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
+
+  nsCOMPtr<nsIThread> mThread;
+  bool mDestroyed;
+};
+
+} // namespace mozilla
+
+#endif  // ProfilerChild_h
deleted file mode 100644
--- a/tools/profiler/public/ProfilerControllingProcess.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef ProfilerControllingProcess_h
-#define ProfilerControllingProcess_h
-
-namespace mozilla {
-
-class ProfilerInitParams;
-
-// Top-level process actors should implement this to integrate with
-// CrossProcessProfilerController.
-class ProfilerControllingProcess {
-public:
-  virtual void SendStartProfiler(const ProfilerInitParams& aParams) = 0;
-  virtual void SendPauseProfiler(const bool& aPause) = 0;
-  virtual void SendStopProfiler() = 0;
-  virtual void SendGatherProfile() = 0;
-
-  virtual ~ProfilerControllingProcess() {}
-};
-
-} // namespace mozilla
-
-#endif
new file mode 100644
--- /dev/null
+++ b/tools/profiler/public/ProfilerParent.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ProfilerParent_h
+#define ProfilerParent_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/PProfilerParent.h"
+
+namespace mozilla {
+
+class ProfilerParentTracker;
+
+// This is the main process side of the PProfiler protocol.
+// ProfilerParent instances only exist on the main thread of the main process.
+// The other side (ProfilerChild) lives on a background thread in the other
+// process.
+// The creation of PProfiler actors is initiated from the main process, after
+// the other process has been launched.
+// ProfilerParent instances are destroyed once the message channel closes,
+// which can be triggered by either process, depending on which one shuts down
+// first.
+// All ProfilerParent instances are registered with a manager class called
+// ProfilerParentTracker, which has the list of living ProfilerParent instances
+// and handles shutdown.
+class ProfilerParent final : public PProfilerParent
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(ProfilerParent)
+
+  static mozilla::ipc::Endpoint<PProfilerChild> CreateForProcess(base::ProcessId aOtherPid);
+
+  // The following static methods can be called on any thread, but they are
+  // no-ops on anything other than the main thread.
+  // If called on the main thread, the call will be broadcast to all
+  // registered processes (all processes for which we have a ProfilerParent
+  // object).
+  // At the moment, the main process always calls these methods on the main
+  // thread, and that's the only process in which we need to forward these
+  // calls to other processes. The other processes will call these methods on
+  // the ProfilerChild background thread, but those processes don't need to
+  // forward these calls any further.
+
+  // Returns the number of profiles to expect. The gathered profiles will be
+  // provided asynchronously with a call to ProfileGatherer::ReceiveGatheredProfile.
+  static uint32_t GatherProfiles();
+
+  static void ProfilerStarted(nsIProfilerStartParams* aParams);
+  static void ProfilerStopped();
+  static void ProfilerPaused();
+  static void ProfilerResumed();
+
+private:
+  friend class ProfilerParentTracker;
+
+  ProfilerParent();
+  virtual ~ProfilerParent();
+
+  void Init();
+  void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
+  void DeallocPProfilerParent() override;
+
+  mozilla::ipc::IPCResult RecvProfile(const nsCString& aProfile) override;
+
+  RefPtr<ProfilerParent> mSelfRef;
+  uint32_t mPendingRequestedProfilesCount;
+  bool mDestroyed;
+};
+
+} // namespace mozilla
+
+#endif  // ProfilerParent_h