Bug 1330185 - Use a top-level PProfiler protocol to control the profiler in other processes. r=njn, r=billm
authorMarkus Stange <mstange@themasta.com>
Sun, 21 May 2017 14:06:50 -0400
changeset 359860 51cdc2541486d5ddf2d385ec0459a9ec38b8775b
parent 359859 327c145ded03d39970351a9cc01492f0541d9149
child 359861 61095bd2cbcab67baccd113105786bdd6b23ebb9
push id31860
push userihsiao@mozilla.com
push dateMon, 22 May 2017 03:30:59 +0000
treeherdermozilla-central@9851fcb0bf4d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn, billm
bugs1330185
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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
@@ -69,16 +69,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"
 
@@ -1045,16 +1049,21 @@ ContentChild::InitXPCOM(const XPCOMInitD
     ProcessGlobal* global = ProcessGlobal::Get();
     global->SetInitialProcessData(data);
   }
 
   // The stylesheet cache is not ready yet. Store this URL for future use.
   nsCOMPtr<nsIURI> ucsURL = DeserializeURI(aXPCOMInit.userContentSheetURL());
   nsLayoutStylesheetCache::SetUserContentCSSURL(ucsURL);
 
+#ifdef MOZ_GECKO_PROFILER
+  mProfilerController = new ChildProfilerController();
+  Unused << SendInitProfiler(mProfilerController->SetUpEndpoints(OtherPid()));
+#endif
+
   // This will register cross-process observer.
   mozilla::dom::time::InitializeDateCacheCleaner();
 
   GfxInfoBase::SetFeatureStatus(aXPCOMInit.gfxFeatureStatus());
 
   DataStorage::SetCachedStorageEntries(aXPCOMInit.dataStorage());
 }
 
@@ -2647,70 +2656,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);
@@ -2866,21 +2821,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
 
@@ -447,24 +448,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,
@@ -687,18 +680,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
@@ -733,16 +724,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
@@ -20,19 +20,16 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #endif
 
 #include "chrome/common/process_watcher.h"
 
 #include "mozilla/a11y/PDocAccessible.h"
 #include "AudioChannelService.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
@@ -184,16 +181,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"
 
@@ -251,16 +249,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;
@@ -1063,48 +1065,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
@@ -1348,20 +1318,16 @@ ContentParent::Init()
     Unused <<
       SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
 #else
     Unused << SendActivateA11y(0);
 #endif
   }
 #endif
 
-#ifdef MOZ_GECKO_PROFILER
-  mProfilerController = MakeUnique<CrossProcessProfilerController>(this);
-#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();
 
   mScriptableHelper = new ScriptableCPInfo(this);
@@ -1746,20 +1712,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);
@@ -2812,16 +2774,25 @@ ContentParent::RecvInitBackground(Endpoi
 {
   if (!BackgroundParent::Alloc(this, Move(aEndpoint))) {
     return IPC_FAIL(this, "BackgroundParent::Alloc failed");
   }
 
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+ContentParent::RecvInitProfiler(Endpoint<PProfilerParent>&& aEndpoint)
+{
+  if (!ProfilerParent::Alloc(Move(aEndpoint))) {
+    NS_WARNING("ProfilerParent::Alloc failed");
+  }
+  return IPC_OK();
+}
+
 mozilla::jsipc::PJavaScriptParent *
 ContentParent::AllocPJavaScriptParent()
 {
   MOZ_ASSERT(ManagedPJavaScriptParent().IsEmpty());
   return nsIContentParent::AllocPJavaScriptParent();
 }
 
 bool
@@ -4718,22 +4689,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
 
   /**
@@ -820,16 +810,17 @@ private:
                                   uint64_t* aId);
 
   /**
    * Get or create the corresponding content parent array to |aContentProcessType|.
    */
   static nsTArray<ContentParent*>& GetOrCreatePool(const nsAString& aContentProcessType);
 
   virtual mozilla::ipc::IPCResult RecvInitBackground(Endpoint<mozilla::ipc::PBackgroundParent>&& aEndpoint) override;
+  virtual mozilla::ipc::IPCResult RecvInitProfiler(Endpoint<mozilla::PProfilerParent>&& aEndpoint) override;
 
   mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override;
   mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override;
 
   virtual bool
   DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;
 
   virtual bool
@@ -1133,18 +1124,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;
 
@@ -1251,20 +1241,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
@@ -40,30 +40,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";
 
@@ -506,25 +506,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);
 
@@ -624,16 +615,17 @@ child:
     async SetPermissionsWithKey(nsCString aPermissionKey, Permission[] aPermissions);
 
     async RefreshScreens(ScreenDetails[] aScreens);
 
     async PIPCBlobInputStream(nsID aID, uint64_t aSize);
 
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
+    async InitProfiler(Endpoint<PProfilerParent> aEndpoint);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority,
                             TabId openerTabId,
                             TabId tabId)
         returns (ContentParentId cpId, bool isForBrowser);
     sync BridgeToChildProcess(ContentParentId cpId)
         returns (Endpoint<PContentBridgeParent> endpoint);
@@ -996,17 +988,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";
@@ -90,33 +90,27 @@ 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);
 
+  async InitProfiler(Endpoint<PProfilerParent> aEndPoint);
+
   /**
    * This message is only used on X11 platforms.
    *
    * Send a dup of the plugin process's X socket to the parent
    * process.  In theory, this scheme keeps the plugin's X resources
    * around until after both the plugin process shuts down *and* the
    * parent process closes the dup fd.  This is used to prevent the
    * parent process from crashing on X errors if, e.g., the plugin
@@ -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";
@@ -279,16 +281,21 @@ PluginModuleChild::InitForChrome(const s
     CommonInit();
 
     if (!Open(aChannel, aParentPid, aIOLoop)) {
         return false;
     }
 
     GetIPCChannel()->SetAbortOnError(true);
 
+#ifdef MOZ_GECKO_PROFILER
+    mProfilerController = new ChildProfilerController();
+    Unused << SendInitProfiler(mProfilerController->SetUpEndpoints(OtherPid()));
+#endif
+
     // TODO: use PluginPRLibrary here
 
 #if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
     mShutdownFunc =
         (NP_PLUGINSHUTDOWN) PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
 
     // create the new plugin handler
 
@@ -741,16 +748,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 =
@@ -2660,64 +2674,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
@@ -120,21 +123,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 +248,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
@@ -631,20 +626,16 @@ PluginModuleChromeParent::OnProcessLaunc
 
 #if defined(XP_MACOSX)
         if (NS_SUCCEEDED(mAsyncInitRv)) {
             mAsyncInitRv = NP_GetEntryPoints(mNPPIface,
                                              &mAsyncInitError);
         }
 #endif
     }
-
-#ifdef MOZ_GECKO_PROFILER
-    mProfilerController = MakeUnique<CrossProcessProfilerController>(this);
-#endif
 }
 
 bool
 PluginModuleChromeParent::WaitForIPCConnection()
 {
     PluginProcessParent* process = Process();
     MOZ_ASSERT(process);
     process->SetCallRunnableImmediately(true);
@@ -774,20 +765,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());
@@ -3128,16 +3115,25 @@ PluginModuleParent::RecvReturnSitesWithD
 
     if (!!mSitesWithDataCallbacks[aCallbackId]) {
         mSitesWithDataCallbacks[aCallbackId]->SitesWithData(aSites);
     }
     mSitesWithDataCallbacks.erase(aCallbackId);
     return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+PluginModuleParent::RecvInitProfiler(Endpoint<PProfilerParent>&& aEndpoint)
+{
+  if (!ProfilerParent::Alloc(Move(aEndpoint))) {
+    NS_WARNING("ProfilerParent::Alloc failed");
+  }
+  return IPC_OK();
+}
+
 layers::TextureClientRecycleAllocator*
 PluginModuleParent::EnsureTextureAllocatorForDirectBitmap()
 {
     if (!mTextureAllocatorForDirectBitmap) {
         mTextureAllocatorForDirectBitmap = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton().get());
     }
     return mTextureAllocatorForDirectBitmap;
 }
@@ -3259,28 +3255,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);
     }
 
@@ -228,26 +204,26 @@ 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;
 
+    virtual mozilla::ipc::IPCResult RecvInitProfiler(Endpoint<mozilla::PProfilerParent>&& aEndpoint) override;
+
     void SetPluginFuncs(NPPluginFuncs* aFuncs);
 
     nsresult NPP_NewInternal(NPMIMEType pluginType, NPP instance,
                              InfallibleTArray<nsCString>& names,
                              InfallibleTArray<nsCString>& values,
                              NPSavedData* saved, NPError* error);
 
     // NPP-like API that Gecko calls are trampolined into.  These 
@@ -522,19 +498,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;
 
@@ -675,19 +648,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),
@@ -71,20 +69,16 @@ GPUChild::Init()
   nsTArray<LayerTreeIdMapping> mappings;
   LayerTreeOwnerTracker::Get()->Iterate([&](uint64_t aLayersId, base::ProcessId aProcessId) {
     mappings.AppendElement(LayerTreeIdMapping(aLayersId, aProcessId));
   });
 
   SendInit(prefs, updates, devicePrefs, mappings);
 
   gfxVars::AddReceiver(this);
-
-#ifdef MOZ_GECKO_PROFILER
-  mProfilerController = MakeUnique<CrossProcessProfilerController>(this);
-#endif
 }
 
 void
 GPUChild::OnVarChanged(const GfxVarUpdate& aVar)
 {
   SendUpdateVar(aVar);
 }
 
@@ -200,43 +194,42 @@ 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(
     aGeneration,
     aAnonymize,
     aMinimizeMemoryUsage,
     aDMDFile);
   return true;
 }
 
 mozilla::ipc::IPCResult
+GPUChild::RecvInitProfiler(Endpoint<PProfilerParent>&& aEndpoint)
+{
+  if (!ProfilerParent::Alloc(Move(aEndpoint))) {
+    return IPC_FAIL(this, "ProfilerParent::Alloc failed");
+  }
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 GPUChild::RecvAddMemoryReport(const MemoryReport& aReport)
 {
   if (mMemoryReportRequest) {
     mMemoryReportRequest->RecvReport(aReport);
   }
   return IPC_OK();
 }
 
@@ -246,40 +239,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 +259,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,30 @@ 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 RecvInitProfiler(Endpoint<mozilla::PProfilerParent>&& aEndpoint) 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;
@@ -112,16 +114,20 @@ GPUParent::Init(base::ProcessId aParentP
   CompositorThreadHolder::Start();
   APZThreadUtils::SetControllerThread(CompositorThreadHolder::Loop());
   APZCTreeManager::InitializeGlobalState();
   LayerTreeOwnerTracker::Initialize();
   mozilla::ipc::SetThisProcessName("GPU Process");
 #ifdef XP_WIN
   wmf::MFStartup();
 #endif
+#ifdef MOZ_GECKO_PROFILER
+  mProfilerController = new ChildProfilerController();
+  Unused << SendInitProfiler(mProfilerController->SetUpEndpoints(OtherPid()));
+#endif
   return true;
 }
 
 void
 GPUParent::NotifyDeviceReset()
 {
   if (!NS_IsMainThread()) {
     NS_DispatchToMainThread(NS_NewRunnableFunction([] () -> void {
@@ -378,61 +384,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 +414,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:
@@ -50,29 +51,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";
@@ -85,32 +85,29 @@ 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().
   async InitComplete(GPUDeviceData data);
 
+  // Sent to the UI process to register the GPU process's profiler.
+  async InitProfiler(Endpoint<PProfilerParent> endpoint);
+
   // Sent when APZ detects checkerboarding and apz checkerboard reporting is enabled.
   async ReportCheckerboard(uint32_t severity, nsCString log);
 
   // Graphics errors, analogous to PContent::GraphicsError
   async GraphicsError(nsCString aError);
 
   async InitCrashReporter(Shmem shmem, NativeThreadId threadId);
 
@@ -122,16 +119,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
@@ -30,16 +30,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
@@ -2021,16 +2022,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);
   NotifyObservers("profiler-started", params);
 }
 
 static void
 locked_profiler_start(PSLockRef aLock, const int aEntries, double aInterval,
                       uint32_t aFeatures,
                       const char** aFilters, uint32_t aFilterCount);
 
@@ -2166,16 +2168,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();
     NotifyObservers("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();
     NotifyObservers("profiler-stopped");
     delete samplerThread;
   }
   NotifyProfilerStarted(aEntries, aInterval, aFeatures,
                         aFilters, aFilterCount);
 }
 
 static MOZ_MUST_USE SamplerThread*
@@ -2498,16 +2502,17 @@ profiler_stop()
     }
 
     samplerThread = locked_profiler_stop(lock);
   }
 
   // We notify observers with gPSMutex unlocked. Otherwise we might get a
   // deadlock, if code run by the observer calls a profiler function that locks
   // gPSMutex. (This has been seen in practise in bug 1346356.)
+  ProfilerParent::ProfilerStopped();
   NotifyObservers("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
@@ -2544,16 +2549,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();
   NotifyObservers("profiler-paused");
 }
 
 void
 profiler_resume()
 {
   LOG("profiler_resume");
 
@@ -2565,16 +2571,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();
   NotifyObservers("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,106 @@
+/* -*- 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()
+{
+  MOZ_COUNT_CTOR(ChildProfilerController);
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+}
+
+Endpoint<PProfilerParent>
+ChildProfilerController::SetUpEndpoints(base::ProcessId aOtherPid)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  Endpoint<PProfilerParent> parent;
+  Endpoint<PProfilerChild> child;
+  nsresult rv = PProfiler::CreateEndpoints(aOtherPid,
+                                           base::GetCurrentProcId(),
+                                           &parent, &child);
+
+  if (NS_FAILED(rv)) {
+    MOZ_CRASH("Failed to create top level actor for PProfiler!");
+  }
+
+  nsCOMPtr<nsIRunnable> setupRunnable =
+    NewRunnableMethod<Endpoint<PProfilerChild>&&>(
+      this, &ChildProfilerController::SetupProfilerChild, Move(child));
+
+  NS_NewNamedThread("ProfilerChild", getter_AddRefs(mThread), setupRunnable);
+
+  return parent;
+}
+
+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,247 @@
+/* -*- 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, ShutdownPhase::Shutdown);
+  }
+  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()
+{
+  nsTArray<ProfilerParent*> parents;
+  parents = mProfilerParents;
+  // Close the channels of any profiler parents that haven't been destroyed.
+  for (ProfilerParent* profilerParent : parents) {
+    if (!profilerParent->mDestroyed) {
+      // This will trigger a call to DeallocPProfilerParent.
+      profilerParent->Close();
+    }
+  }
+}
+
+/* static */ bool
+ProfilerParent::Alloc(Endpoint<PProfilerParent>&& aEndpoint)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  Endpoint<PProfilerParent> endpoint = Move(aEndpoint);
+  ProfilerParent* actor = new ProfilerParent();
+
+  if (!endpoint.Bind(actor)) {
+    return false;
+  }
+
+  actor->Init();
+
+  // The actor will be deleted from DeallocPProfilerParent.
+  return true;
+}
+
+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()
+{
+  delete this;
+}
+
+} // 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
@@ -475,55 +475,42 @@ nsProfiler::GetStartParams(nsIProfilerSt
     nsCOMPtr<nsIProfilerStartParams> startParams =
       new nsProfilerStartParams(entries, interval, features, filtersArray);
 
     startParams.forget(aRetVal);
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsProfiler::GetBufferInfo(uint32_t* aCurrentPosition, uint32_t* aTotalSize,
-                          uint32_t* aGeneration)
-{
-  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);
 }
 
+NS_IMETHODIMP
+nsProfiler::GetBufferInfo(uint32_t* aCurrentPosition, uint32_t* aTotalSize,
+                          uint32_t* aGeneration)
+{
+  MOZ_ASSERT(aCurrentPosition);
+  MOZ_ASSERT(aTotalSize);
+  MOZ_ASSERT(aGeneration);
+  profiler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration);
+  return NS_OK;
+}
--- 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,31 +5,31 @@
 # 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/ProfilerMarkerPayload.h',
         'public/PseudoStack.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/ProfilerIOInterposeObserver.cpp',
         'gecko/ThreadResponsiveness.cpp',
     ]
     if CONFIG['OS_TARGET'] == 'Darwin':
         # This file cannot be built in unified mode because it includes
@@ -104,25 +104,32 @@ 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',
+    'public/ProfilerChild.h',
+    'public/ProfilerParent.h',
+]
+
+UNIFIED_SOURCES += [
+    'gecko/ProfilerChild.cpp',
+    'gecko/ProfilerParent.cpp',
 ]
 
 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,48 @@
+/* -*- 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)
+
+  ChildProfilerController();
+  mozilla::ipc::Endpoint<PProfilerParent> SetUpEndpoints(base::ProcessId aOtherPid);
+  MOZ_MUST_USE nsCString GrabShutdownProfileAndShutdown();
+  void Shutdown();
+
+private:
+  virtual ~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 will be 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,70 @@
+/* -*- 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/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 other process.
+// 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:
+  static bool Alloc(mozilla::ipc::Endpoint<PProfilerParent>&& aEndpoint);
+
+  // 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();
+
+  ProfilerParent();
+  virtual ~ProfilerParent();
+
+private:
+  friend class ProfilerParentTracker;
+
+  void Init();
+  void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
+  void DeallocPProfilerParent() override;
+
+  mozilla::ipc::IPCResult RecvProfile(const nsCString& aProfile) override;
+
+  uint32_t mPendingRequestedProfilesCount;
+  bool mDestroyed;
+};
+
+} // namespace mozilla
+
+#endif  // ProfilerParent_h