Bug 1287392 - Part 4: Expose a file writer API for GeckoProfiler. r=mstange
authorThinker K.F. Li <thinker@codemud.net>
Sat, 19 Nov 2016 02:48:00 +0800
changeset 323916 09620b9e6d069a45be1f7e2375acfad144b24f53
parent 323915 71a163471efd17c6a74c42093efddb3c7a84ad6b
child 323917 4c3e92ce19904a458e91090569f9ca51538e3566
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersmstange
bugs1287392
milestone53.0a1
Bug 1287392 - Part 4: Expose a file writer API for GeckoProfiler. r=mstange
tools/profiler/core/GeckoSampler.cpp
tools/profiler/core/GeckoSampler.h
tools/profiler/core/platform.cpp
tools/profiler/gecko/ProfileGatherer.cpp
tools/profiler/moz.build
tools/profiler/public/GeckoProfilerFunc.h
tools/profiler/public/ProfileGatherer.h
--- a/tools/profiler/core/GeckoSampler.cpp
+++ b/tools/profiler/core/GeckoSampler.cpp
@@ -439,16 +439,25 @@ void GeckoSampler::ToJSObjectAsync(doubl
 {
   if (NS_WARN_IF(!mGatherer)) {
     return;
   }
 
   mGatherer->Start(aSinceTime, aPromise);
 }
 
+void GeckoSampler::ToFileAsync(const nsACString& aFileName, double aSinceTime)
+{
+  if (NS_WARN_IF(!mGatherer)) {
+    return;
+  }
+
+  mGatherer->Start(aSinceTime, aFileName);
+}
+
 struct SubprocessClosure {
   explicit SubprocessClosure(SpliceableJSONWriter* aWriter)
     : mWriter(aWriter)
   {}
 
   SpliceableJSONWriter* mWriter;
 };
 
--- a/tools/profiler/core/GeckoSampler.h
+++ b/tools/profiler/core/GeckoSampler.h
@@ -107,16 +107,17 @@ class GeckoSampler: public Sampler {
 
   void ToStreamAsJSON(std::ostream& stream, double aSinceTime = 0);
 #ifndef SPS_STANDALONE
   virtual JSObject *ToJSObject(JSContext *aCx, double aSinceTime = 0);
   void GetGatherer(nsISupports** aRetVal);
 #endif
   mozilla::UniquePtr<char[]> ToJSON(double aSinceTime = 0);
   virtual void ToJSObjectAsync(double aSinceTime = 0, mozilla::dom::Promise* aPromise = 0);
+  void ToFileAsync(const nsACString& aFileName, double aSinceTime = 0);
   void StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter);
   void StreamTaskTracer(SpliceableJSONWriter& aWriter);
   void FlushOnJSShutdown(JSContext* aContext);
   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; }
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -619,16 +619,30 @@ void mozilla_sampler_get_profile_data_as
   GeckoSampler *t = tlsTicker.get();
   if (NS_WARN_IF(!t)) {
     return;
   }
 
   t->ToJSObjectAsync(aSinceTime, aPromise);
 }
 
+void mozilla_sampler_save_profile_to_file_async(double aSinceTime,
+						const char* aFileName)
+{
+  nsCString filename(aFileName);
+  NS_DispatchToMainThread(NS_NewRunnableFunction([=] () {
+	GeckoSampler *t = tlsTicker.get();
+	if (NS_WARN_IF(!t)) {
+	  return;
+	}
+
+	t->ToFileAsync(filename, aSinceTime);
+      }));
+}
+
 void mozilla_sampler_get_profiler_start_params(int* aEntrySize,
                                                double* aInterval,
                                                mozilla::Vector<const char*>* aFilters,
                                                mozilla::Vector<const char*>* aFeatures)
 {
   if (NS_WARN_IF(!aEntrySize) || NS_WARN_IF(!aInterval) ||
       NS_WARN_IF(!aFilters) || NS_WARN_IF(!aFeatures)) {
     return;
--- a/tools/profiler/gecko/ProfileGatherer.cpp
+++ b/tools/profiler/gecko/ProfileGatherer.cpp
@@ -1,16 +1,20 @@
 /* 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 "nsIProfileSaveEvent.h"
 #include "GeckoSampler.h"
+#include "nsLocalFile.h"
+#include "nsIFileStreams.h"
 
 using mozilla::dom::AutoJSAPI;
 using mozilla::dom::Promise;
 
 namespace mozilla {
 
 /**
  * When a subprocess exits before we've gathered profiles, we'll
@@ -37,17 +41,17 @@ ProfileGatherer::GatheredOOPProfile()
   MOZ_ASSERT(NS_IsMainThread());
   if (!mGathering) {
     // If we're not actively gathering, then we don't actually
     // care that we gathered a profile here. This can happen for
     // processes that exit while profiling.
     return;
   }
 
-  if (NS_WARN_IF(!mPromise)) {
+  if (NS_WARN_IF(!mPromise && !mFile)) {
     // If we're not holding on to a Promise, then someone is
     // calling us erroneously.
     return;
   }
 
   mPendingProfiles--;
 
   if (mPendingProfiles == 0) {
@@ -92,28 +96,79 @@ ProfileGatherer::Start(double aSinceTime
   }
 
   if (!mPendingProfiles) {
     Finish();
   }
 }
 
 void
+ProfileGatherer::Start(double aSinceTime,
+                       nsIFile* aFile)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mGathering) {
+    return;
+  }
+
+  mSinceTime = aSinceTime;
+  mFile = aFile;
+  mGathering = true;
+  mPendingProfiles = 0;
+
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os) {
+    DebugOnly<nsresult> rv =
+      os->AddObserver(this, "profiler-subprocess", false);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "AddObserver failed");
+    rv = os->NotifyObservers(this, "profiler-subprocess-gather", nullptr);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyObservers failed");
+  }
+
+  if (!mPendingProfiles) {
+    Finish();
+  }
+}
+
+void
+ProfileGatherer::Start(double aSinceTime,
+                       const nsACString& aFileName)
+{
+  nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+  nsresult rv = file->InitWithNativePath(aFileName);
+  if (NS_FAILED(rv)) {
+    MOZ_CRASH();
+  }
+  Start(aSinceTime, file);
+}
+
+void
 ProfileGatherer::Finish()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mTicker) {
     // We somehow got called after we were cancelled! This shouldn't
     // be possible, but doing a belt-and-suspenders check to be sure.
     return;
   }
 
   UniquePtr<char[]> buf = mTicker->ToJSON(mSinceTime);
 
+  if (mFile) {
+    nsCOMPtr<nsIFileOutputStream> of =
+      do_CreateInstance("@mozilla.org/network/file-output-stream;1");
+    of->Init(mFile, -1, -1, 0);
+    uint32_t sz;
+    of->Write(buf.get(), strlen(buf.get()), &sz);
+    of->Close();
+    Reset();
+    return;
+  }
+
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
     DebugOnly<nsresult> rv = os->RemoveObserver(this, "profiler-subprocess");
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RemoveObserver failed");
   }
 
   AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.Init(mPromise->GlobalJSObject()))) {
@@ -148,28 +203,31 @@ ProfileGatherer::Finish()
   Reset();
 }
 
 void
 ProfileGatherer::Reset()
 {
   mSinceTime = 0;
   mPromise = nullptr;
+  mFile = nullptr;
   mPendingProfiles = 0;
   mGathering = false;
 }
 
 void
 ProfileGatherer::Cancel()
 {
   // The GeckoSampler is going away. If we have a Promise in flight, we
   // should reject it.
   if (mPromise) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
+  mPromise = nullptr;
+  mFile = nullptr;
 
   // Clear out the GeckoSampler reference, since it's being destroyed.
   mTicker = nullptr;
 }
 
 void
 ProfileGatherer::OOPExitProfile(const nsCString& aProfile)
 {
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -34,21 +34,28 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
         'core/ProfilerMarkers.cpp',
         'core/StackTop.cpp',
         'core/SyncProfile.cpp',
         'core/ThreadInfo.cpp',
         'core/ThreadProfile.cpp',
         'gecko/nsProfiler.cpp',
         'gecko/nsProfilerFactory.cpp',
         'gecko/nsProfilerStartParams.cpp',
-        'gecko/ProfileGatherer.cpp',
         'gecko/ProfilerIOInterposeObserver.cpp',
         'gecko/SaveProfileTask.cpp',
         'gecko/ThreadResponsiveness.cpp',
     ]
+    if CONFIG['OS_TARGET'] == 'Darwin':
+        SOURCES += [
+            'gecko/ProfileGatherer.cpp',
+        ]
+    else:
+        UNIFIED_SOURCES += [
+            'gecko/ProfileGatherer.cpp',
+        ]
 
     if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
         UNIFIED_SOURCES += [
             'lul/AutoObjectMapper.cpp',
             'lul/LulCommon.cpp',
             'lul/LulDwarf.cpp',
             'lul/LulDwarfSummariser.cpp',
             'lul/LulElf.cpp',
--- a/tools/profiler/public/GeckoProfilerFunc.h
+++ b/tools/profiler/public/GeckoProfilerFunc.h
@@ -66,16 +66,19 @@ const double* mozilla_sampler_get_respon
 void mozilla_sampler_save();
 
 mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime);
 
 #ifndef SPS_STANDALONE
 JSObject *mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime);
 void mozilla_sampler_get_profile_data_async(double aSinceTime,
                                             mozilla::dom::Promise* aPromise);
+MOZ_EXPORT
+void mozilla_sampler_save_profile_to_file_async(double aSinceTime,
+                                                const char* aFileName);
 void mozilla_sampler_get_profiler_start_params(int* aEntrySize,
                                                double* aInterval,
                                                mozilla::Vector<const char*>* aFilters,
                                                mozilla::Vector<const char*>* aFeatures);
 void mozilla_sampler_get_gatherer(nsISupports** aRetVal);
 #endif
 
 // Make this function easily callable from a debugger in a build without
--- a/tools/profiler/public/ProfileGatherer.h
+++ b/tools/profiler/public/ProfileGatherer.h
@@ -1,41 +1,45 @@
 /* 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"
+#include "nsIFile.h"
 
 class GeckoSampler;
 
 namespace mozilla {
 
 class ProfileGatherer final : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   explicit ProfileGatherer(GeckoSampler* aTicker);
   void WillGatherOOPProfile();
   void GatheredOOPProfile();
   void Start(double aSinceTime, mozilla::dom::Promise* aPromise);
+  void Start(double aSinceTime, nsIFile* aFile);
+  void Start(double aSinceTime, const nsACString& aFileName);
   void Cancel();
   void OOPExitProfile(const nsCString& aProfile);
 
 private:
   ~ProfileGatherer() {};
   void Finish();
   void Reset();
 
   nsTArray<nsCString> mExitProfiles;
   RefPtr<mozilla::dom::Promise> mPromise;
+  nsCOMPtr<nsIFile> mFile;
   GeckoSampler* mTicker;
   double mSinceTime;
   uint32_t mPendingProfiles;
   bool mGathering;
 };
 
 } // namespace mozilla