Bug 1116188 - Add async ProfileGatherer as the mechanism for gathering profiles from subprocesses. r=bgirard,bz
authorMike Conley <mconley@mozilla.com>
Wed, 10 Jun 2015 17:58:30 -0400
changeset 279365 46ff7d8a323177ab814c0ba4977706b9118dceec
parent 279364 61e83526454d42e7d200cfd06fea9521edff1d03
child 279366 dc1cfa1c6dd854f782c04b8a185ef5e74c8e9ef3
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgirard, bz
bugs1116188
milestone41.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 1116188 - Add async ProfileGatherer as the mechanism for gathering profiles from subprocesses. r=bgirard,bz
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
tools/profiler/GeckoProfiler.h
tools/profiler/GeckoProfilerFunc.h
tools/profiler/GeckoProfilerImpl.h
tools/profiler/ProfileEntry.cpp
tools/profiler/ProfileGatherer.cpp
tools/profiler/ProfileGatherer.h
tools/profiler/TableTicker.cpp
tools/profiler/TableTicker.h
tools/profiler/moz.build
tools/profiler/nsIProfiler.idl
tools/profiler/nsProfiler.cpp
tools/profiler/platform.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2629,24 +2629,27 @@ ContentChild::RecvStartProfiler(const ui
 bool
 ContentChild::RecvStopProfiler()
 {
     profiler_stop();
     return true;
 }
 
 bool
-ContentChild::RecvGetProfile(nsCString* aProfile)
+ContentChild::RecvGatherProfile()
 {
+    nsCString profileCString;
     UniquePtr<char[]> profile = profiler_get_profile();
     if (profile) {
-        *aProfile = nsCString(profile.get(), strlen(profile.get()));
+        profileCString = nsCString(profile.get(), strlen(profile.get()));
     } else {
-        *aProfile = EmptyCString();
+        profileCString = EmptyCString();
     }
+
+    unused << SendProfile(profileCString);
     return true;
 }
 
 bool
 ContentChild::RecvLoadPluginResult(const uint32_t& aPluginId, const bool& aResult)
 {
     nsresult rv;
     bool finalResult = aResult &&
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -393,17 +393,17 @@ public:
                                       const bool& aResult) override;
     virtual bool RecvUpdateWindow(const uintptr_t& aChildId) override;
 
     virtual bool RecvStartProfiler(const uint32_t& aEntries,
                                    const double& aInterval,
                                    nsTArray<nsCString>&& aFeatures,
                                    nsTArray<nsCString>&& aThreadNameFilters) override;
     virtual bool RecvStopProfiler() override;
-    virtual bool RecvGetProfile(nsCString* aProfile) override;
+    virtual bool RecvGatherProfile() override;
     virtual bool RecvDomainSetChanged(const uint32_t& aSetType, const uint32_t& aChangeType,
                                       const OptionalURIParams& aDomain) override;
     virtual bool RecvShutdown() override;
 
     virtual bool
     RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
                           const uint32_t& aAction) override;
     virtual bool RecvEndDragSession(const bool& aDoneDrag,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -74,16 +74,17 @@
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/SharedBufferManagerParent.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
+#include "mozilla/ProfileGatherer.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/unused.h"
 #include "nsAnonymousTemporaryFile.h"
 #include "nsAppRunner.h"
 #include "nsAutoPtr.h"
 #include "nsCDefaultURIFixup.h"
@@ -233,16 +234,17 @@ using namespace mozilla::system;
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadMonitoring.h"
 #endif
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
 using base::ChildPrivileges;
 using base::KillProcess;
+using mozilla::ProfileGatherer;
 
 #ifdef MOZ_CRASHREPORTER
 using namespace CrashReporter;
 #endif
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::cellbroadcast;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::icc;
@@ -656,16 +658,17 @@ static const char* sObserverTopics[] = {
 #endif
 #ifdef ACCESSIBILITY
     "a11y-init-or-shutdown",
 #endif
     "app-theme-changed",
 #ifdef MOZ_ENABLE_PROFILER_SPS
     "profiler-started",
     "profiler-stopped",
+    "profiler-subprocess-gather",
     "profiler-subprocess",
 #endif
 };
 
 /* static */ already_AddRefed<ContentParent>
 ContentParent::RunNuwaProcess()
 {
     MOZ_ASSERT(NS_IsMainThread());
@@ -3127,23 +3130,27 @@ ContentParent::Observe(nsISupports* aSub
         params->GetInterval(&interval);
         const nsTArray<nsCString>& features = params->GetFeatures();
         const nsTArray<nsCString>& threadFilterNames = params->GetThreadFilterNames();
         unused << SendStartProfiler(entries, interval, features, threadFilterNames);
     }
     else if (!strcmp(aTopic, "profiler-stopped")) {
         unused << SendStopProfiler();
     }
+    else if (!strcmp(aTopic, "profiler-subprocess-gather")) {
+        mGatherer = static_cast<ProfileGatherer*>(aSubject);
+        mGatherer->WillGatherOOPProfile();
+        unused << SendGatherProfile();
+    }
     else if (!strcmp(aTopic, "profiler-subprocess")) {
         nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
         if (pse) {
-            nsCString result;
-            unused << SendGetProfile(&result);
-            if (!result.IsEmpty()) {
-                pse->AddSubProfile(result.get());
+            if (!mProfile.IsEmpty()) {
+                pse->AddSubProfile(mProfile.get());
+                mProfile.Truncate();
             }
         }
     }
 #endif
     return NS_OK;
 }
 
 PGMPServiceParent*
@@ -5091,16 +5098,28 @@ ContentParent::RecvGamepadListenerRemove
         return false;
     }
     mHasGamepadListener = false;
     MaybeStopGamepadMonitoring();
 #endif
     return true;
 }
 
+bool
+ContentParent::RecvProfile(const nsCString& aProfile)
+{
+    if (NS_WARN_IF(!mGatherer)) {
+        return true;
+    }
+    mProfile = aProfile;
+    mGatherer->GatheredOOPProfile();
+    mGatherer = nullptr;
+    return true;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) {
     mozilla::unused << mParent->SendNotifyIdleObserver(mObserver,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -32,16 +32,17 @@ class nsConsoleService;
 class nsICycleCollectorLogSink;
 class nsIDumpGCAndCCLogsCallback;
 class nsITimer;
 class ParentIdleListener;
 class nsIWidget;
 
 namespace mozilla {
 class PRemoteSpellcheckEngineParent;
+class ProfileGatherer;
 
 namespace ipc {
 class OptionalURIParams;
 class PFileDescriptorSetParent;
 class URIParams;
 class TestShellParent;
 } // namespace ipc
 
@@ -851,16 +852,17 @@ private:
 
     virtual bool RecvUpdateDropEffect(const uint32_t& aDragAction,
                                       const uint32_t& aDropEffect) override;
 
     virtual bool RecvGetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration* aConfig) override;
 
     virtual bool RecvGamepadListenerAdded() override;
     virtual bool RecvGamepadListenerRemoved() override;
+    virtual bool RecvProfile(const nsCString& aProfile) override;
 
     // If you add strong pointers to cycle collected objects here, be sure to
     // release these objects in ShutDownProcess.  See the comment there for more
     // details.
 
     GeckoChildProcessHost* mSubprocess;
     ContentParent* mOpener;
 
@@ -926,16 +928,18 @@ private:
 #endif
 
 #ifdef MOZ_NUWA_PROCESS
     static int32_t sNuwaPid;
     static bool sNuwaReady;
 #endif
 
     PProcessHangMonitorParent* mHangMonitorActor;
+    nsRefPtr<mozilla::ProfileGatherer> mGatherer;
+    nsCString mProfile;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 class ParentIdleListener : public nsIObserver {
   friend class mozilla::dom::ContentParent;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -619,18 +619,18 @@ child:
     async LoadPluginResult(uint32_t aPluginId, bool aResult);
 
     /**
      * Control the Gecko Profiler in the child process.
      */
     async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures,
                         nsCString[] aThreadNameFilters);
     async StopProfiler();
-    prio(high) sync GetProfile()
-      returns (nsCString aProfile);
+
+    async GatherProfile();
 
     InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action);
 
     EndDragSession(bool aDoneDrag, bool aUserCancelled);
 
     async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, OptionalURIParams aDomain);
 
     /**
@@ -1049,15 +1049,17 @@ parent:
      */
     GamepadListenerAdded();
 
     /**
      * Tells the parent to stop the gamepad listening service if it hasn't already.
      */
     GamepadListenerRemoved();
 
+    async Profile(nsCString aProfile);
+
 both:
      AsyncMessage(nsString aMessage, ClonedMessageData aData,
                   CpowEntry[] aCpows, Principal aPrincipal);
 };
 
 }
 }
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -94,18 +94,17 @@ child:
 
   /**
    * Control the Gecko Profiler in the plugin process.
    */
   async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures,
                       nsCString[] aThreadNameFilters);
   async StopProfiler();
 
-  intr GetProfile()
-    returns (nsCString aProfile);
+  async GatherProfile();
 
   async SettingChanged(PluginSettings settings);
 
 parent:
   async NP_InitializeResult(NPError aError);
 
   /**
    * This message is only used on X11 platforms.
@@ -142,12 +141,14 @@ parent:
   sync NPN_SetException(nsCString message);
 
   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);
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -33,16 +33,17 @@
 #ifdef MOZ_X11
 # include "mozilla/X11Util.h"
 #endif
 #include "mozilla/plugins/PluginInstanceChild.h"
 #include "mozilla/plugins/StreamNotifyChild.h"
 #include "mozilla/plugins/BrowserStreamChild.h"
 #include "mozilla/plugins/PluginStreamChild.h"
 #include "mozilla/dom/CrashReporterChild.h"
+#include "mozilla/unused.h"
 
 #include "nsNPAPIPlugin.h"
 
 #ifdef XP_WIN
 #include "nsWindowsDllInterceptor.h"
 #include "mozilla/widget/AudioSession.h"
 #endif
 
@@ -2565,18 +2566,21 @@ PluginModuleChild::RecvStartProfiler(con
 bool
 PluginModuleChild::RecvStopProfiler()
 {
     profiler_stop();
     return true;
 }
 
 bool
-PluginModuleChild::AnswerGetProfile(nsCString* aProfile)
+PluginModuleChild::RecvGatherProfile()
 {
+    nsCString profileCString;
     UniquePtr<char[]> profile = profiler_get_profile();
     if (profile != nullptr) {
-        *aProfile = nsCString(profile.get(), strlen(profile.get()));
+        profileCString = nsCString(profile.get(), strlen(profile.get()));
     } else {
-        *aProfile = nsCString("", 0);
+        profileCString = nsCString("", 0);
     }
+
+    unused << SendProfile(profileCString);
     return true;
 }
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -146,17 +146,17 @@ protected:
     virtual bool
     RecvProcessNativeEventsInInterruptCall() override;
 
     virtual bool RecvStartProfiler(const uint32_t& aEntries,
                                    const double& aInterval,
                                    nsTArray<nsCString>&& aFeatures,
                                    nsTArray<nsCString>&& aThreadNameFilters) override;
     virtual bool RecvStopProfiler() override;
-    virtual bool AnswerGetProfile(nsCString* aProfile) override;
+    virtual bool RecvGatherProfile() override;
 
 public:
     explicit PluginModuleChild(bool aIsChrome);
     virtual ~PluginModuleChild();
 
     bool CommonInit(base::ProcessId aParentPid,
                     MessageLoop* aIOLoop,
                     IPC::Channel* aChannel);
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/dom/PCrashReporterParent.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/plugins/BrowserStreamParent.h"
 #include "mozilla/plugins/PluginAsyncSurrogate.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/ProfileGatherer.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/unused.h"
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
 #include "nsIFile.h"
 #include "nsIObserverService.h"
@@ -53,16 +54,17 @@
 #elif XP_MACOSX
 #include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
 
 using base::KillProcess;
 
 using mozilla::PluginLibrary;
+using mozilla::ProfileGatherer;
 using mozilla::ipc::MessageChannel;
 using mozilla::ipc::GeckoChildProcessHost;
 using mozilla::dom::PCrashReporterParent;
 using mozilla::dom::CrashReporterParent;
 
 using namespace mozilla;
 using namespace mozilla::plugins;
 using namespace mozilla::plugins::parent;
@@ -3021,23 +3023,23 @@ PluginModuleChromeParent::OnCrash(DWORD 
 #ifdef MOZ_ENABLE_PROFILER_SPS
 class PluginProfilerObserver final : public nsIObserver,
                                      public nsSupportsWeakReference
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
 
-    explicit PluginProfilerObserver(PluginModuleParent* pmp)
+    explicit PluginProfilerObserver(PluginModuleChromeParent* pmp)
       : mPmp(pmp)
     {}
 
 private:
     ~PluginProfilerObserver() {}
-    PluginModuleParent* mPmp;
+    PluginModuleChromeParent* mPmp;
 };
 
 NS_IMPL_ISUPPORTS(PluginProfilerObserver, nsIObserver, nsISupportsWeakReference)
 
 NS_IMETHODIMP
 PluginProfilerObserver::Observe(nsISupports *aSubject,
                                 const char *aTopic,
                                 const char16_t *aData)
@@ -3048,44 +3050,74 @@ PluginProfilerObserver::Observe(nsISuppo
         double interval;
         params->GetEntries(&entries);
         params->GetInterval(&interval);
         const nsTArray<nsCString>& features = params->GetFeatures();
         const nsTArray<nsCString>& threadFilterNames = params->GetThreadFilterNames();
         unused << mPmp->SendStartProfiler(entries, interval, features, threadFilterNames);
     } else if (!strcmp(aTopic, "profiler-stopped")) {
         unused << mPmp->SendStopProfiler();
+    } else if (!strcmp(aTopic, "profiler-subprocess-gather")) {
+        nsRefPtr<ProfileGatherer> gatherer = static_cast<ProfileGatherer*>(aSubject);
+        mPmp->GatherAsyncProfile(gatherer);
     } else if (!strcmp(aTopic, "profiler-subprocess")) {
         nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
-        if (pse) {
-            nsCString result;
-            bool success = mPmp->CallGetProfile(&result);
-            if (success && !result.IsEmpty()) {
-                pse->AddSubProfile(result.get());
-            }
-        }
+        mPmp->GatheredAsyncProfile(pse);
     }
     return NS_OK;
 }
 
 void
 PluginModuleChromeParent::InitPluginProfiling()
 {
     nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
     if (observerService) {
         mProfilerObserver = new PluginProfilerObserver(this);
         observerService->AddObserver(mProfilerObserver, "profiler-started", false);
         observerService->AddObserver(mProfilerObserver, "profiler-stopped", false);
+        observerService->AddObserver(mProfilerObserver, "profiler-subprocess-gather", false);
         observerService->AddObserver(mProfilerObserver, "profiler-subprocess", false);
     }
 }
 
 void
 PluginModuleChromeParent::ShutdownPluginProfiling()
 {
     nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
     if (observerService) {
         observerService->RemoveObserver(mProfilerObserver, "profiler-started");
         observerService->RemoveObserver(mProfilerObserver, "profiler-stopped");
+        observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess-gather");
         observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess");
     }
 }
+
+void
+PluginModuleChromeParent::GatherAsyncProfile(ProfileGatherer* aGatherer)
+{
+    mGatherer = aGatherer;
+    mGatherer->WillGatherOOPProfile();
+    unused << SendGatherProfile();
+}
+
+void
+PluginModuleChromeParent::GatheredAsyncProfile(nsIProfileSaveEvent* aSaveEvent)
+{
+    if (aSaveEvent && !mProfile.IsEmpty()) {
+        aSaveEvent->AddSubProfile(mProfile.get());
+        mProfile.Truncate();
+    }
+}
+
+bool
+PluginModuleChromeParent::RecvProfile(const nsCString& aProfile)
+{
+    if (NS_WARN_IF(!mGatherer)) {
+        return true;
+    }
+
+    mProfile = aProfile;
+    mGatherer->GatheredOOPProfile();
+    mGatherer = nullptr;
+    return true;
+}
+
 #endif
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -26,19 +26,21 @@
 #ifdef XP_WIN
 #include "nsWindowsHelpers.h"
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
+class nsIProfileSaveEvent;
 class nsPluginTag;
 
 namespace mozilla {
+class ProfileGatherer;
 namespace dom {
 class PCrashReporterParent;
 class CrashReporterParent;
 }
 
 namespace plugins {
 //-----------------------------------------------------------------------------
 
@@ -186,16 +188,18 @@ protected:
 protected:
     void SetChildTimeout(const int32_t aChildTimeout);
     static void TimeoutChanged(const char* aPref, void* aModule);
 
     virtual void UpdatePluginTimeout() {}
 
     virtual bool RecvNotifyContentModuleDestroyed() override { return true; }
 
+    virtual bool RecvProfile(const nsCString& aProfile) override { return true; }
+
     void SetPluginFuncs(NPPluginFuncs* aFuncs);
 
     nsresult NPP_NewInternal(NPMIMEType pluginType, NPP instance, uint16_t mode,
                              InfallibleTArray<nsCString>& names,
                              InfallibleTArray<nsCString>& values,
                              NPSavedData* saved, NPError* error);
 
     // NPP-like API that Gecko calls are trampolined into.  These 
@@ -408,16 +412,22 @@ class PluginModuleChromeParent
 
     void CachedSettingChanged();
 
     void OnEnteredCall() override;
     void OnExitedCall() override;
     void OnEnteredSyncSend() override;
     void OnExitedSyncSend() override;
 
+    void GatherAsyncProfile(mozilla::ProfileGatherer* aGatherer);
+    void GatheredAsyncProfile(nsIProfileSaveEvent* aSaveEvent);
+
+    virtual bool
+    RecvProfile(const nsCString& aProfile) override;
+
 private:
     virtual void
     EnteredCxxStack() override;
 
     void
     ExitedCxxStack() override;
 
     mozilla::ipc::IProtocol* GetInvokingProtocol();
@@ -565,16 +575,18 @@ private:
 
     friend class LaunchedTask;
 
     bool                mInitOnAsyncConnect;
     nsresult            mAsyncInitRv;
     NPError             mAsyncInitError;
     dom::ContentParent* mContentParent;
     nsCOMPtr<nsIObserver> mOfflineObserver;
+    nsRefPtr<mozilla::ProfileGatherer> mGatherer;
+    nsCString mProfile;
     bool mIsBlocklisted;
     static bool sInstantiated;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // mozilla_plugins_PluginModuleParent_h
--- a/tools/profiler/GeckoProfiler.h
+++ b/tools/profiler/GeckoProfiler.h
@@ -49,17 +49,22 @@
 #ifndef SAMPLER_H
 #define SAMPLER_H
 
 #include "js/TypeDecls.h"
 #include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 class TimeStamp;
-}
+
+namespace dom {
+class Promise;
+} // namespace dom
+
+} // namespace mozilla
 
 enum TracingMetadata {
   TRACING_DEFAULT,
   TRACING_INTERVAL_START,
   TRACING_INTERVAL_END,
   TRACING_EVENT,
   TRACING_EVENT_BACKTRACE,
   TRACING_TIMESTAMP
@@ -165,16 +170,20 @@ static inline mozilla::UniquePtr<char[]>
 }
 
 // Get the profile encoded as a JSON object.
 static inline JSObject* profiler_get_profile_jsobject(JSContext* aCx,
                                                       float aSinceTime = 0) {
   return nullptr;
 }
 
+// Get the profile encoded as a JSON object.
+static inline void profiler_get_profile_jsobject_async(float aSinceTime = 0,
+                                                       mozilla::dom::Promise* = 0) {}
+
 // Get the profile and write it into a file
 static inline void profiler_save_profile_to_file(char* aFilename) { }
 
 // Get the features supported by the profiler that are accepted by profiler_init.
 // Returns a null terminated char* array.
 static inline char** profiler_get_features() { return nullptr; }
 
 // Get information about the current buffer status.
--- a/tools/profiler/GeckoProfilerFunc.h
+++ b/tools/profiler/GeckoProfilerFunc.h
@@ -8,17 +8,22 @@
 
 #include "js/TypeDecls.h"
 #include "js/ProfilingStack.h"
 #include "mozilla/UniquePtr.h"
 #include <stdint.h>
 
 namespace mozilla {
 class TimeStamp;
-}
+
+namespace dom {
+class Promise;
+} // namespace dom
+
+} // namespace mozilla
 
 class ProfilerBacktrace;
 class ProfilerMarkerPayload;
 
 // Returns a handle to pass on exit. This can check that we are popping the
 // correct callstack.
 inline void* mozilla_sampler_call_enter(const char *aInfo, js::ProfileEntry::Category aCategory,
                                         void *aFrameAddress = nullptr, bool aCopy = false,
@@ -52,16 +57,18 @@ void mozilla_sampler_frame_number(int fr
 
 const double* mozilla_sampler_get_responsiveness();
 
 void mozilla_sampler_save();
 
 mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(float aSinceTime);
 
 JSObject *mozilla_sampler_get_profile_data(JSContext *aCx, float aSinceTime);
+void mozilla_sampler_get_profile_data_async(float aSinceTime,
+                                            mozilla::dom::Promise* aPromise);
 
 // Make this function easily callable from a debugger in a build without
 // debugging information (work around http://llvm.org/bugs/show_bug.cgi?id=22211)
 extern "C" {
   void mozilla_sampler_save_profile_to_file(const char* aFilename);
 }
 
 const char** mozilla_sampler_get_features();
--- a/tools/profiler/GeckoProfilerImpl.h
+++ b/tools/profiler/GeckoProfilerImpl.h
@@ -151,16 +151,23 @@ mozilla::UniquePtr<char[]> profiler_get_
 
 static inline
 JSObject* profiler_get_profile_jsobject(JSContext* aCx, float aSinceTime = 0)
 {
   return mozilla_sampler_get_profile_data(aCx, aSinceTime);
 }
 
 static inline
+void profiler_get_profile_jsobject_async(float aSinceTime = 0,
+                                         mozilla::dom::Promise* aPromise = 0)
+{
+  mozilla_sampler_get_profile_data_async(aSinceTime, aPromise);
+}
+
+static inline
 void profiler_save_profile_to_file(const char* aFilename)
 {
   return mozilla_sampler_save_profile_to_file(aFilename);
 }
 
 static inline
 const char** profiler_get_features()
 {
--- a/tools/profiler/ProfileEntry.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -9,19 +9,16 @@
 #include "nsXULAppAPI.h"
 #include "mozilla/HashFunctions.h"
 
 // JS
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/TrackedOptimizationInfo.h"
 
-// JSON
-#include "ProfileJSONWriter.h"
-
 // Self
 #include "ProfileEntry.h"
 
 #if defined(_MSC_VER) && _MSC_VER < 1900
  #define snprintf _snprintf
 #endif
 
 using mozilla::MakeUnique;
new file mode 100644
--- /dev/null
+++ b/tools/profiler/ProfileGatherer.cpp
@@ -0,0 +1,110 @@
+/* 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 "mozilla/ProfileGatherer.h"
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+#include "TableTicker.h"
+
+using mozilla::dom::AutoJSAPI;
+using mozilla::dom::Promise;
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS0(ProfileGatherer)
+
+ProfileGatherer::ProfileGatherer(TableTicker* aTicker,
+                                 float aSinceTime,
+                                 Promise* aPromise)
+  : mPromise(aPromise)
+  , mTicker(aTicker)
+  , mSinceTime(aSinceTime)
+  , mPendingProfiles(0)
+{
+}
+
+void
+ProfileGatherer::GatheredOOPProfile()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (NS_WARN_IF(!mPromise)) {
+    // If we're not holding on to a Promise, then someone is
+    // calling us erroneously.
+    return;
+  }
+
+  mPendingProfiles--;
+
+  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++;
+}
+
+void
+ProfileGatherer::Start()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os) {
+    nsresult rv = os->NotifyObservers(this, "profiler-subprocess-gather", nullptr);
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+
+  if (!mPendingProfiles) {
+    Finish();
+  }
+}
+
+void
+ProfileGatherer::Finish()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  UniquePtr<char[]> buf = mTicker->ToJSON(mSinceTime);
+
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(mPromise->GlobalJSObject()))) {
+    // We're really hosed if we can't get a JS context for some reason.
+    // We'll tell the TableTicker that we've gathered the profile just
+    // so that it can drop the reference to this ProfileGatherer and maybe
+    // the user can try again.
+    mTicker->ProfileGathered();
+    return;
+  }
+
+  JSContext* cx = jsapi.cx();
+
+  // Now parse the JSON so that we resolve with a JS Object.
+  JS::RootedValue val(cx);
+  {
+    NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
+    if (!JS_ParseJSON(cx, static_cast<const char16_t*>(js_string.get()),
+                      js_string.Length(), &val)) {
+      if (!jsapi.HasException()) {
+        mPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
+      } else {
+        JS::RootedValue exn(cx);
+        DebugOnly<bool> gotException = jsapi.StealException(&exn);
+        MOZ_ASSERT(gotException);
+
+        jsapi.ClearException();
+        mPromise->MaybeReject(cx, exn);
+      }
+    } else {
+      mPromise->MaybeResolve(val);
+    }
+  }
+
+  mTicker->ProfileGathered();
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/tools/profiler/ProfileGatherer.h
@@ -0,0 +1,38 @@
+/* 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 MOZ_PROFILE_GATHERER_H
+#define MOZ_PROFILE_GATHERER_H
+
+#include "mozilla/dom/Promise.h"
+
+class TableTicker;
+
+namespace mozilla {
+
+class ProfileGatherer final : public nsISupports
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  ProfileGatherer(TableTicker* aTicker,
+                  float aSinceTime,
+                  mozilla::dom::Promise* aPromise);
+  void WillGatherOOPProfile();
+  void GatheredOOPProfile();
+  void Start();
+
+private:
+  ~ProfileGatherer() {};
+  void Finish();
+
+  nsRefPtr<mozilla::dom::Promise> mPromise;
+  TableTicker* mTicker;
+  float mSinceTime;
+  uint32_t mPendingProfiles;
+};
+
+} // namespace mozilla
+
+#endif
\ No newline at end of file
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -103,16 +103,121 @@ using namespace mozilla;
 #endif
 
 
 ///////////////////////////////////////////////////////////////////////
 // BEGIN SaveProfileTask et al
 
 std::string GetSharedLibraryInfoString();
 
+static bool
+hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature) {
+  for(size_t i = 0; i < aFeatureCount; i++) {
+    if (strcmp(aFeatures[i], aFeature) == 0)
+      return true;
+  }
+  return false;
+}
+
+TableTicker::TableTicker(double aInterval, int aEntrySize,
+                         const char** aFeatures, uint32_t aFeatureCount,
+                         const char** aThreadNameFilters, uint32_t aFilterCount)
+  : Sampler(aInterval, true, aEntrySize)
+  , mPrimaryThreadProfile(nullptr)
+  , mBuffer(new ProfileBuffer(aEntrySize))
+  , mSaveRequested(false)
+#if defined(XP_WIN)
+  , mIntelPowerGadget(nullptr)
+#endif
+{
+  mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
+
+  mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
+  mProfileJava = hasFeature(aFeatures, aFeatureCount, "java");
+  mProfileGPU = hasFeature(aFeatures, aFeatureCount, "gpu");
+  mProfilePower = hasFeature(aFeatures, aFeatureCount, "power");
+  // Users sometimes ask to filter by a list of threads but forget to request
+  // profiling non main threads. Let's make it implificit if we have a filter
+  mProfileThreads = hasFeature(aFeatures, aFeatureCount, "threads") || aFilterCount > 0;
+  mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
+  mPrivacyMode = hasFeature(aFeatures, aFeatureCount, "privacy");
+  mAddMainThreadIO = hasFeature(aFeatures, aFeatureCount, "mainthreadio");
+  mProfileMemory = hasFeature(aFeatures, aFeatureCount, "memory");
+  mTaskTracer = hasFeature(aFeatures, aFeatureCount, "tasktracer");
+  mLayersDump = hasFeature(aFeatures, aFeatureCount, "layersdump");
+  mDisplayListDump = hasFeature(aFeatures, aFeatureCount, "displaylistdump");
+  mProfileRestyle = hasFeature(aFeatures, aFeatureCount, "restyle");
+
+#if defined(XP_WIN)
+  if (mProfilePower) {
+    mIntelPowerGadget = new IntelPowerGadget();
+    mProfilePower = mIntelPowerGadget->Init();
+  }
+#endif
+
+  // Deep copy aThreadNameFilters
+  MOZ_ALWAYS_TRUE(mThreadNameFilters.resize(aFilterCount));
+  for (uint32_t i = 0; i < aFilterCount; ++i) {
+    mThreadNameFilters[i] = aThreadNameFilters[i];
+  }
+
+  sStartTime = mozilla::TimeStamp::Now();
+
+  {
+    mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
+
+    // Create ThreadProfile for each registered thread
+    for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
+      ThreadInfo* info = sRegisteredThreads->at(i);
+
+      RegisterThread(info);
+    }
+
+    SetActiveSampler(this);
+  }
+
+#ifdef MOZ_TASK_TRACER
+  if (mTaskTracer) {
+    mozilla::tasktracer::StartLogging();
+  }
+#endif
+}
+
+TableTicker::~TableTicker()
+{
+  if (IsActive())
+    Stop();
+
+  SetActiveSampler(nullptr);
+
+  // Destroy ThreadProfile for all threads
+  {
+    mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
+
+    for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
+      ThreadInfo* info = sRegisteredThreads->at(i);
+      ThreadProfile* profile = info->Profile();
+      if (profile) {
+        delete profile;
+        info->SetProfile(nullptr);
+      }
+      // We've stopped profiling. We no longer need to retain
+      // information for an old thread.
+      if (info->IsPendingDelete()) {
+        delete info;
+        sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
+        i--;
+      }
+    }
+  }
+#if defined(XP_WIN)
+  delete mIntelPowerGadget;
+#endif
+}
+
 void TableTicker::HandleSaveRequest()
 {
   if (!mSaveRequested)
     return;
   mSaveRequested = false;
 
   // TODO: Use use the ipc/chromium Tasks here to support processes
   // without XPCOM.
@@ -253,16 +358,32 @@ JSObject* TableTicker::ToJSObject(JSCont
 
 UniquePtr<char[]> TableTicker::ToJSON(float aSinceTime)
 {
   SpliceableChunkedJSONWriter b;
   StreamJSON(b, aSinceTime);
   return b.WriteFunc()->CopyData();
 }
 
+void TableTicker::ToJSObjectAsync(float aSinceTime,
+                                  Promise* aPromise)
+{
+  if (NS_WARN_IF(mGatherer)) {
+    return;
+  }
+
+  mGatherer = new ProfileGatherer(this, aSinceTime, aPromise);
+  mGatherer->Start();
+}
+
+void TableTicker::ProfileGathered()
+{
+  mGatherer = nullptr;
+}
+
 struct SubprocessClosure {
   explicit SubprocessClosure(SpliceableJSONWriter* aWriter)
     : mWriter(aWriter)
   {}
 
   SpliceableJSONWriter* mWriter;
 };
 
--- a/tools/profiler/TableTicker.h
+++ b/tools/profiler/TableTicker.h
@@ -10,24 +10,19 @@
 #include "ProfileEntry.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Vector.h"
 #include "IntelPowerGadget.h"
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
-static bool
-hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature) {
-  for(size_t i = 0; i < aFeatureCount; i++) {
-    if (strcmp(aFeatures[i], aFeature) == 0)
-      return true;
-  }
-  return false;
-}
+namespace mozilla {
+class ProfileGatherer;
+} // namespace mozilla
 
 typedef mozilla::Vector<std::string> ThreadNameFilterList;
 
 static bool
 threadSelected(ThreadInfo* aInfo, const ThreadNameFilterList &aThreadNameFilters) {
   if (aThreadNameFilters.empty()) {
     return true;
   }
@@ -44,108 +39,18 @@ threadSelected(ThreadInfo* aInfo, const 
 extern mozilla::TimeStamp sLastTracerEvent;
 extern int sFrameNumber;
 extern int sLastFrameNumber;
 
 class TableTicker: public Sampler {
  public:
   TableTicker(double aInterval, int aEntrySize,
               const char** aFeatures, uint32_t aFeatureCount,
-              const char** aThreadNameFilters, uint32_t aFilterCount)
-    : Sampler(aInterval, true, aEntrySize)
-    , mPrimaryThreadProfile(nullptr)
-    , mBuffer(new ProfileBuffer(aEntrySize))
-    , mSaveRequested(false)
-#if defined(XP_WIN)
-    , mIntelPowerGadget(nullptr)
-#endif
-  {
-    mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
-
-    mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
-    mProfileJava = hasFeature(aFeatures, aFeatureCount, "java");
-    mProfileGPU = hasFeature(aFeatures, aFeatureCount, "gpu");
-    mProfilePower = hasFeature(aFeatures, aFeatureCount, "power");
-    // Users sometimes ask to filter by a list of threads but forget to request
-    // profiling non main threads. Let's make it implificit if we have a filter
-    mProfileThreads = hasFeature(aFeatures, aFeatureCount, "threads") || aFilterCount > 0;
-    mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
-    mPrivacyMode = hasFeature(aFeatures, aFeatureCount, "privacy");
-    mAddMainThreadIO = hasFeature(aFeatures, aFeatureCount, "mainthreadio");
-    mProfileMemory = hasFeature(aFeatures, aFeatureCount, "memory");
-    mTaskTracer = hasFeature(aFeatures, aFeatureCount, "tasktracer");
-    mLayersDump = hasFeature(aFeatures, aFeatureCount, "layersdump");
-    mDisplayListDump = hasFeature(aFeatures, aFeatureCount, "displaylistdump");
-    mProfileRestyle = hasFeature(aFeatures, aFeatureCount, "restyle");
-
-#if defined(XP_WIN)
-    if (mProfilePower) {
-      mIntelPowerGadget = new IntelPowerGadget();
-      mProfilePower = mIntelPowerGadget->Init();
-    }
-#endif
-
-    // Deep copy aThreadNameFilters
-    MOZ_ALWAYS_TRUE(mThreadNameFilters.resize(aFilterCount));
-    for (uint32_t i = 0; i < aFilterCount; ++i) {
-      mThreadNameFilters[i] = aThreadNameFilters[i];
-    }
-
-    sStartTime = mozilla::TimeStamp::Now();
-
-    {
-      mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
-
-      // Create ThreadProfile for each registered thread
-      for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
-        ThreadInfo* info = sRegisteredThreads->at(i);
-
-        RegisterThread(info);
-      }
-
-      SetActiveSampler(this);
-    }
-
-#ifdef MOZ_TASK_TRACER
-    if (mTaskTracer) {
-      mozilla::tasktracer::StartLogging();
-    }
-#endif
-  }
-
-  ~TableTicker() {
-    if (IsActive())
-      Stop();
-
-    SetActiveSampler(nullptr);
-
-    // Destroy ThreadProfile for all threads
-    {
-      mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
-
-      for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
-        ThreadInfo* info = sRegisteredThreads->at(i);
-        ThreadProfile* profile = info->Profile();
-        if (profile) {
-          delete profile;
-          info->SetProfile(nullptr);
-        }
-        // We've stopped profiling. We no longer need to retain
-        // information for an old thread.
-        if (info->IsPendingDelete()) {
-          delete info;
-          sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
-          i--;
-        }
-      }
-    }
-#if defined(XP_WIN)
-    delete mIntelPowerGadget;
-#endif
-  }
+              const char** aThreadNameFilters, uint32_t aFilterCount);
+  ~TableTicker();
 
   void RegisterThread(ThreadInfo* aInfo) {
     if (!aInfo->IsMainThread() && !mProfileThreads) {
       return;
     }
 
     if (!threadSelected(aInfo, mThreadNameFilters)) {
       return;
@@ -190,16 +95,17 @@ class TableTicker: public Sampler {
     }
 
     return mPrimaryThreadProfile;
   }
 
   void ToStreamAsJSON(std::ostream& stream, float aSinceTime = 0);
   virtual JSObject *ToJSObject(JSContext *aCx, float aSinceTime = 0);
   mozilla::UniquePtr<char[]> ToJSON(float aSinceTime = 0);
+  virtual void ToJSObjectAsync(float aSinceTime = 0, mozilla::dom::Promise* aPromise = 0);
   void StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter);
   void StreamTaskTracer(SpliceableJSONWriter& aWriter);
   void FlushOnJSShutdown(JSRuntime* aRuntime);
   bool ProfileJS() const { return mProfileJS; }
   bool ProfileJava() const { return mProfileJava; }
   bool ProfileGPU() const { return mProfileGPU; }
   bool ProfilePower() const { return mProfilePower; }
   bool ProfileThreads() const override { return mProfileThreads; }
@@ -208,16 +114,18 @@ class TableTicker: public Sampler {
   bool ProfileMemory() const { return mProfileMemory; }
   bool TaskTracer() const { return mTaskTracer; }
   bool LayersDump() const { return mLayersDump; }
   bool DisplayListDump() const { return mDisplayListDump; }
   bool ProfileRestyle() const { return mProfileRestyle; }
 
   void GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration);
 
+  void ProfileGathered();
+
 protected:
   // Called within a signal. This function must be reentrant
   virtual void InplaceTick(TickSample* sample);
 
   // Not implemented on platforms which do not support backtracing
   void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
 
   void StreamJSON(SpliceableJSONWriter& aWriter, float aSinceTime);
@@ -242,12 +150,15 @@ protected:
   ThreadNameFilterList mThreadNameFilters;
   bool mPrivacyMode;
   bool mAddMainThreadIO;
   bool mProfileMemory;
   bool mTaskTracer;
 #if defined(XP_WIN)
   IntelPowerGadget* mIntelPowerGadget;
 #endif
+
+private:
+  nsRefPtr<mozilla::ProfileGatherer> mGatherer;
 };
 
 #endif
 
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -15,25 +15,29 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
     EXPORTS += [
         'GeckoProfilerFunc.h',
         'GeckoProfilerImpl.h',
         'ProfilerBacktrace.h',
         'ProfilerMarkers.h',
         'PseudoStack.h',
         'shared-libraries.h',
     ]
+    EXPORTS.mozilla += [
+        'ProfileGatherer.h',
+    ]
     EXTRA_JS_MODULES += [
         'Profiler.jsm',
     ]
     UNIFIED_SOURCES += [
         'nsProfiler.cpp',
         'nsProfilerFactory.cpp',
         'nsProfilerStartParams.cpp',
         'platform.cpp',
         'ProfileEntry.cpp',
+        'ProfileGatherer.cpp',
         'ProfileJSONWriter.cpp',
         'ProfilerBacktrace.cpp',
         'ProfilerIOInterposeObserver.cpp',
         'ProfilerMarkers.cpp',
         'SaveProfileTask.cpp',
         'SyncProfile.cpp',
         'TableTicker.cpp',
         'ThreadResponsiveness.cpp',
--- a/tools/profiler/nsIProfiler.idl
+++ b/tools/profiler/nsIProfiler.idl
@@ -7,17 +7,17 @@
 
 %{C++
 template<class T> class nsTArray;
 class nsCString;
 %}
 
 [ref] native StringArrayRef(const nsTArray<nsCString>);
 
-[scriptable, uuid(9f3e7c97-abcf-425c-83fd-34d354eb95e8)]
+[scriptable, uuid(0f474ec5-b95c-45d9-a7c8-156da0e3fa25)]
 interface nsIProfiler : nsISupports
 {
   void StartProfiler(in uint32_t aEntries, in double aInterval,
                       [array, size_is(aFeatureCount)] in string aFeatures,
                       in uint32_t aFeatureCount,
                       [array, size_is(aFilterCount), optional] in string aThreadNameFilters,
                       [optional] in uint32_t aFilterCount);
   void StopProfiler();
@@ -33,16 +33,19 @@ interface nsIProfiler : nsISupports
 
   /*
    * Returns a JS object of the profile. If aSinceTime is passed, only report
    * samples taken at >= aSinceTime.
    */
   [implicit_jscontext]
   jsval getProfileData([optional] in float aSinceTime);
 
+  [implicit_jscontext]
+  nsISupports getProfileDataAsync([optional] in float aSinceTime);
+
   boolean IsActive();
   void GetFeatures(out uint32_t aCount, [retval, array, size_is(aCount)] out string aFeatures);
 
   void GetBufferInfo(out uint32_t aCurrentPosition, out uint32_t aTotalSize,
                      out uint32_t aGeneration);
 
   /**
    * Returns the elapsed time, in milliseconds, since the last StartProfiler call.
--- a/tools/profiler/nsProfiler.cpp
+++ b/tools/profiler/nsProfiler.cpp
@@ -12,17 +12,21 @@
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsILoadContext.h"
 #include "nsIWebNavigation.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "shared-libraries.h"
 #include "js/Value.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/Promise.h"
 
+using mozilla::ErrorResult;
+using mozilla::dom::Promise;
 using std::string;
 
 NS_IMPL_ISUPPORTS(nsProfiler, nsIProfiler)
 
 nsProfiler::nsProfiler()
   : mLockedForPrivateBrowsing(false)
 {
 }
@@ -208,16 +212,44 @@ nsProfiler::GetProfileData(float aSinceT
   if (!obj) {
     return NS_ERROR_FAILURE;
   }
   aResult.setObject(*obj);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsProfiler::GetProfileDataAsync(float aSinceTime, JSContext* aCx,
+                                nsISupports** aPromise)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_WARN_IF(!aCx)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsIGlobalObject* go = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
+
+  if (NS_WARN_IF(!go)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ErrorResult result;
+  nsRefPtr<Promise> promise = Promise::Create(go, result);
+  if (NS_WARN_IF(result.Failed())) {
+    return result.StealNSResult();
+  }
+
+  profiler_get_profile_jsobject_async(aSinceTime, promise);
+
+  promise.forget(aPromise);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsProfiler::GetElapsedTime(float* aElapsedTime)
 {
   *aElapsedTime = static_cast<float>(profiler_time());
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::IsActive(bool *aIsActive)
--- a/tools/profiler/platform.cpp
+++ b/tools/profiler/platform.cpp
@@ -576,16 +576,27 @@ JSObject *mozilla_sampler_get_profile_da
   TableTicker *t = tlsTicker.get();
   if (!t) {
     return nullptr;
   }
 
   return t->ToJSObject(aCx, aSinceTime);
 }
 
+void mozilla_sampler_get_profile_data_async(float aSinceTime,
+                                            mozilla::dom::Promise* aPromise)
+{
+  TableTicker *t = tlsTicker.get();
+  if (NS_WARN_IF(!t)) {
+    return;
+  }
+
+  t->ToJSObjectAsync(aSinceTime, aPromise);
+}
+
 void mozilla_sampler_save_profile_to_file(const char* aFilename)
 {
   TableTicker *t = tlsTicker.get();
   if (!t) {
     return;
   }
 
   std::ofstream stream;