Bug 1369280 (part 2) - Merge ProfileGatherer into nsProfiler. r=mstange.
authorNicholas Nethercote <nnethercote@mozilla.com>
Fri, 02 Jun 2017 11:59:32 +1000
changeset 361995 9b2fc8bf9cad366dbded5b56f0f309d3f86d7f81
parent 361994 ca2579919062397cc5bd3f39e801f8d6f766816f
child 361996 5ab80eaba78ccbfe4603899d78ffcd16395322cf
push id31953
push usercbook@mozilla.com
push dateFri, 02 Jun 2017 12:22:33 +0000
treeherdermozilla-central@2a8478029a0c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1369280
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 1369280 (part 2) - Merge ProfileGatherer into nsProfiler. r=mstange. This makes things simpler in general, and lets us remove the profiler_is_active() check in nsProfiler().
tools/profiler/gecko/ProfileGatherer.cpp
tools/profiler/gecko/ProfileGatherer.h
tools/profiler/gecko/nsProfiler.cpp
tools/profiler/gecko/nsProfiler.h
tools/profiler/moz.build
deleted file mode 100644
--- a/tools/profiler/gecko/ProfileGatherer.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/* -*- 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 "ProfileGatherer.h"
-
-#include "mozilla/Services.h"
-#include "nsIObserverService.h"
-
-namespace mozilla {
-
-/**
- * When a subprocess exits before we've gathered profiles, we'll
- * store profiles for those processes until gathering starts. We'll
- * only store up to MAX_SUBPROCESS_EXIT_PROFILES. The buffer is
- * circular, so as soon as we receive another exit profile, we'll
- * bump the oldest one out of the buffer.
- */
-static const uint32_t MAX_SUBPROCESS_EXIT_PROFILES = 5;
-
-NS_IMPL_ISUPPORTS0(ProfileGatherer)
-
-ProfileGatherer::ProfileGatherer()
-  : mPendingProfiles(0)
-  , mGathering(false)
-{
-}
-
-ProfileGatherer::~ProfileGatherer()
-{
-  Cancel();
-}
-
-void
-ProfileGatherer::GatheredOOPProfile(const nsACString& aProfile)
-{
-  MOZ_RELEASE_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;
-  }
-
-  MOZ_RELEASE_ASSERT(mWriter.isSome(), "Should always have a writer if mGathering is true");
-
-  if (!aProfile.IsEmpty()) {
-    mWriter->Splice(PromiseFlatCString(aProfile).get());
-  }
-
-  mPendingProfiles--;
-
-  if (mPendingProfiles == 0) {
-    // We've got all of the async profiles now. Let's
-    // finish off the profile and resolve the Promise.
-    Finish();
-  }
-}
-
-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;
-
-  // 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.
-  nsTArray<RefPtr<ProfilerParent::SingleProcessProfilePromise>> profiles =
-    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
-    // stopped on a different thread since that call. Either way, we need to
-    // reject the promise and stop gathering.
-    return ProfileGatherPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
-  }
-
-  mWriter->StartArrayProperty("processes");
-
-  // If we have any process exit profiles, add them immediately, and clear
-  // mExitProfiles.
-  for (size_t i = 0; i < mExitProfiles.Length(); ++i) {
-    if (!mExitProfiles[i].IsEmpty()) {
-      mWriter->Splice(mExitProfiles[i].get());
-    }
-  }
-  mExitProfiles.Clear();
-
-  mPromiseHolder.emplace();
-  RefPtr<ProfileGatherPromise> promise = mPromiseHolder->Ensure(__func__);
-
-  // Keep the array property "processes" and the root object in mWriter open
-  // until Finish() is called. As profiles from the other processes come in,
-  // they will be inserted and end up in the right spot. Finish() will close
-  // the array and the root object.
-
-  mPendingProfiles = profiles.Length();
-  RefPtr<ProfileGatherer> self = this;
-  for (auto profile : profiles) {
-    profile->Then(AbstractThread::MainThread(), __func__,
-      [self](const nsCString& aResult) {
-        self->GatheredOOPProfile(aResult);
-      },
-      [self](PromiseRejectReason aReason) {
-        self->GatheredOOPProfile(NS_LITERAL_CSTRING(""));
-      });
-  }
-  if (!mPendingProfiles) {
-    Finish();
-  }
-
-  return promise;
-}
-
-void
-ProfileGatherer::Finish()
-{
-  MOZ_RELEASE_ASSERT(NS_IsMainThread());
-  MOZ_RELEASE_ASSERT(mWriter.isSome());
-  MOZ_RELEASE_ASSERT(mPromiseHolder.isSome());
-
-  // Close the "processes" array property.
-  mWriter->EndArray();
-
-  // Close the root object of the generated JSON.
-  mWriter->End();
-
-  UniquePtr<char[]> buf = mWriter->WriteFunc()->CopyData();
-  nsCString result(buf.get());
-  mPromiseHolder->Resolve(result, __func__);
-
-  Reset();
-}
-
-void
-ProfileGatherer::Reset()
-{
-  mPromiseHolder.reset();
-  mPendingProfiles = 0;
-  mGathering = false;
-  mWriter.reset();
-}
-
-void
-ProfileGatherer::Cancel()
-{
-  // If we have a Promise in flight, we should reject it.
-  if (mPromiseHolder.isSome()) {
-    mPromiseHolder->RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
-  }
-  Reset();
-}
-
-void
-ProfileGatherer::OOPExitProfile(const nsACString& aProfile)
-{
-  // Append the exit profile to mExitProfiles so that it can be picked up the
-  // next time a profile is requested. If we're currently gathering a profile,
-  // do not add this exit profile to it; chances are that we already have a
-  // profile from the exiting process and we don't want another one.
-  // We only keep around at most MAX_SUBPROCESS_EXIT_PROFILES exit profiles.
-  if (mExitProfiles.Length() >= MAX_SUBPROCESS_EXIT_PROFILES) {
-    mExitProfiles.RemoveElementAt(0);
-  }
-  mExitProfiles.AppendElement(aProfile);
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/tools/profiler/gecko/ProfileGatherer.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 MOZ_PROFILE_GATHERER_H
-#define MOZ_PROFILE_GATHERER_H
-
-#include "nsIFile.h"
-#include "ProfileJSONWriter.h"
-#include "mozilla/MozPromise.h"
-
-namespace mozilla {
-
-// This class holds the state for an async profile-gathering request.
-class ProfileGatherer final : public nsISupports
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  typedef MozPromise<nsCString, nsresult, false> ProfileGatherPromise;
-
-  explicit ProfileGatherer();
-  void GatheredOOPProfile(const nsACString& aProfile);
-  RefPtr<ProfileGatherPromise> Start(double aSinceTime);
-  void OOPExitProfile(const nsACString& aProfile);
-
-private:
-  ~ProfileGatherer();
-  void Cancel();
-  void Finish();
-  void Reset();
-
-  nsTArray<nsCString> mExitProfiles;
-  Maybe<MozPromiseHolder<ProfileGatherPromise>> mPromiseHolder;
-  Maybe<SpliceableChunkedJSONWriter> mWriter;
-  uint32_t mPendingProfiles;
-  bool mGathering;
-};
-
-} // namespace mozilla
-
-#endif
--- a/tools/profiler/gecko/nsProfiler.cpp
+++ b/tools/profiler/gecko/nsProfiler.cpp
@@ -18,37 +18,33 @@
 #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"
 #include "mozilla/dom/TypedArray.h"
-#include "ProfileGatherer.h"
 #include "nsLocalFile.h"
+#include "ProfilerParent.h"
 #include "platform.h"
 
 using namespace mozilla;
 
 using dom::AutoJSAPI;
 using dom::Promise;
 using std::string;
 
 NS_IMPL_ISUPPORTS(nsProfiler, nsIProfiler)
 
 nsProfiler::nsProfiler()
   : mLockedForPrivateBrowsing(false)
+  , mPendingProfiles(0)
+  , mGathering(false)
 {
-  // If MOZ_PROFILER_STARTUP is set, the profiler will already be running. We
-  // need to create a ProfileGatherer in that case.
-  // XXX: this is probably not the best approach. See bug 1356694 for details.
-  if (profiler_is_active()) {
-    mGatherer = new ProfileGatherer();
-  }
 }
 
 nsProfiler::~nsProfiler()
 {
   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
   if (observerService) {
     observerService->RemoveObserver(this, "chrome-document-global-created");
     observerService->RemoveObserver(this, "last-pb-context-exited");
@@ -120,29 +116,27 @@ nsProfiler::StartProfiler(uint32_t aEntr
     }
 
   // Convert the array of strings to a bitfield.
   uint32_t features = 0;
   PROFILER_FOR_EACH_FEATURE(ADD_FEATURE_BIT)
 
   #undef ADD_FEATURE_BIT
 
-  profiler_start(aEntries, aInterval, features, aFilters, aFilterCount);
+  ResetGathering();
 
-  // Do this after profiler_start().
-  mGatherer = new ProfileGatherer();
+  profiler_start(aEntries, aInterval, features, aFilters, aFilterCount);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::StopProfiler()
 {
-  // Do this before profiler_stop().
-  mGatherer = nullptr;
+  CancelGathering();
 
   profiler_stop();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::IsPaused(bool *aIsPaused)
@@ -248,17 +242,17 @@ nsProfiler::GetProfileData(double aSince
 }
 
 NS_IMETHODIMP
 nsProfiler::GetProfileDataAsync(double aSinceTime, JSContext* aCx,
                                 nsISupports** aPromise)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (!mGatherer) {
+  if (!profiler_is_active()) {
     return NS_ERROR_FAILURE;
   }
 
   if (NS_WARN_IF(!aCx)) {
     return NS_ERROR_FAILURE;
   }
 
   nsIGlobalObject* globalObject =
@@ -269,17 +263,17 @@ nsProfiler::GetProfileDataAsync(double a
   }
 
   ErrorResult result;
   RefPtr<Promise> promise = Promise::Create(globalObject, result);
   if (NS_WARN_IF(result.Failed())) {
     return result.StealNSResult();
   }
 
-  mGatherer->Start(aSinceTime)->Then(
+  StartGathering(aSinceTime)->Then(
     AbstractThread::MainThread(), __func__,
     [promise](nsCString aResult) {
       AutoJSAPI jsapi;
       if (NS_WARN_IF(!jsapi.Init(promise->GlobalJSObject()))) {
         // We're really hosed if we can't get a JS context for some reason.
         promise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
         return;
       }
@@ -316,17 +310,17 @@ nsProfiler::GetProfileDataAsync(double a
 }
 
 NS_IMETHODIMP
 nsProfiler::GetProfileDataAsArrayBuffer(double aSinceTime, JSContext* aCx,
                                         nsISupports** aPromise)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (!mGatherer) {
+  if (!profiler_is_active()) {
     return NS_ERROR_FAILURE;
   }
 
   if (NS_WARN_IF(!aCx)) {
     return NS_ERROR_FAILURE;
   }
 
   nsIGlobalObject* globalObject =
@@ -337,17 +331,17 @@ nsProfiler::GetProfileDataAsArrayBuffer(
   }
 
   ErrorResult result;
   RefPtr<Promise> promise = Promise::Create(globalObject, result);
   if (NS_WARN_IF(result.Failed())) {
     return result.StealNSResult();
   }
 
-  mGatherer->Start(aSinceTime)->Then(
+  StartGathering(aSinceTime)->Then(
     AbstractThread::MainThread(), __func__,
     [promise](nsCString aResult) {
       AutoJSAPI jsapi;
       if (NS_WARN_IF(!jsapi.Init(promise->GlobalJSObject()))) {
         // We're really hosed if we can't get a JS context for some reason.
         promise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
         return;
       }
@@ -372,23 +366,23 @@ nsProfiler::GetProfileDataAsArrayBuffer(
 }
 
 NS_IMETHODIMP
 nsProfiler::DumpProfileToFileAsync(const nsACString& aFilename,
                                    double aSinceTime)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (!mGatherer) {
+  if (!profiler_is_active()) {
     return NS_ERROR_FAILURE;
   }
 
   nsCString filename(aFilename);
 
-  mGatherer->Start(aSinceTime)->Then(
+  StartGathering(aSinceTime)->Then(
     AbstractThread::MainThread(), __func__,
     [filename](const nsCString& aResult) {
       nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
       nsresult rv = file->InitWithNativePath(filename);
       if (NS_FAILED(rv)) {
         MOZ_CRASH();
       }
       nsCOMPtr<nsIFileOutputStream> of =
@@ -491,26 +485,171 @@ nsProfiler::GetBufferInfo(uint32_t* aCur
   return NS_OK;
 }
 
 void
 nsProfiler::GatheredOOPProfile(const nsACString& aProfile)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
-  if (!mGatherer) {
+  if (!profiler_is_active()) {
+    return;
+  }
+
+  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;
   }
 
-  mGatherer->GatheredOOPProfile(aProfile);
+  MOZ_RELEASE_ASSERT(mWriter.isSome(),
+                     "Should always have a writer if mGathering is true");
+
+  if (!aProfile.IsEmpty()) {
+    mWriter->Splice(PromiseFlatCString(aProfile).get());
+  }
+
+  mPendingProfiles--;
+
+  if (mPendingProfiles == 0) {
+    // We've got all of the async profiles now. Let's
+    // finish off the profile and resolve the Promise.
+    FinishGathering();
+  }
 }
 
+// When a subprocess exits before we've gathered profiles, we'll store profiles
+// for those processes until gathering starts. We'll only store up to
+// MAX_SUBPROCESS_EXIT_PROFILES. The buffer is circular, so as soon as we
+// receive another exit profile, we'll bump the oldest one out of the buffer.
+static const uint32_t MAX_SUBPROCESS_EXIT_PROFILES = 5;
+
 void
 nsProfiler::ReceiveShutdownProfile(const nsCString& aProfile)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
-  if (!mGatherer) {
+  if (!profiler_is_active()) {
     return;
   }
 
-  mGatherer->OOPExitProfile(aProfile);
+  // Append the exit profile to mExitProfiles so that it can be picked up the
+  // next time a profile is requested. If we're currently gathering a profile,
+  // do not add this exit profile to it; chances are that we already have a
+  // profile from the exiting process and we don't want another one.
+  // We only keep around at most MAX_SUBPROCESS_EXIT_PROFILES exit profiles.
+  if (mExitProfiles.Length() >= MAX_SUBPROCESS_EXIT_PROFILES) {
+    mExitProfiles.RemoveElementAt(0);
+  }
+  mExitProfiles.AppendElement(aProfile);
 }
+
+RefPtr<nsProfiler::GatheringPromise>
+nsProfiler::StartGathering(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 GatheringPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
+  }
+
+  mGathering = true;
+
+  // 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.
+  nsTArray<RefPtr<ProfilerParent::SingleProcessProfilePromise>> profiles =
+    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
+    // stopped on a different thread since that call. Either way, we need to
+    // reject the promise and stop gathering.
+    return GatheringPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
+  }
+
+  mWriter->StartArrayProperty("processes");
+
+  // If we have any process exit profiles, add them immediately, and clear
+  // mExitProfiles.
+  for (size_t i = 0; i < mExitProfiles.Length(); ++i) {
+    if (!mExitProfiles[i].IsEmpty()) {
+      mWriter->Splice(mExitProfiles[i].get());
+    }
+  }
+  mExitProfiles.Clear();
+
+  mPromiseHolder.emplace();
+  RefPtr<GatheringPromise> promise = mPromiseHolder->Ensure(__func__);
+
+  // Keep the array property "processes" and the root object in mWriter open
+  // until FinishGathering() is called. As profiles from the other processes
+  // come in, they will be inserted and end up in the right spot.
+  // FinishGathering() will close the array and the root object.
+
+  mPendingProfiles = profiles.Length();
+  RefPtr<nsProfiler> self = this;
+  for (auto profile : profiles) {
+    profile->Then(AbstractThread::MainThread(), __func__,
+      [self](const nsCString& aResult) {
+        self->GatheredOOPProfile(aResult);
+      },
+      [self](ipc::PromiseRejectReason aReason) {
+        self->GatheredOOPProfile(NS_LITERAL_CSTRING(""));
+      });
+  }
+  if (!mPendingProfiles) {
+    FinishGathering();
+  }
+
+  return promise;
+}
+
+void
+nsProfiler::CancelGathering()
+{
+  // If we have a Promise in flight, we should reject it.
+  if (mPromiseHolder.isSome()) {
+    mPromiseHolder->RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
+  }
+  ResetGathering();
+}
+
+void
+nsProfiler::FinishGathering()
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  MOZ_RELEASE_ASSERT(mWriter.isSome());
+  MOZ_RELEASE_ASSERT(mPromiseHolder.isSome());
+
+  // Close the "processes" array property.
+  mWriter->EndArray();
+
+  // Close the root object of the generated JSON.
+  mWriter->End();
+
+  UniquePtr<char[]> buf = mWriter->WriteFunc()->CopyData();
+  nsCString result(buf.get());
+  mPromiseHolder->Resolve(result, __func__);
+
+  ResetGathering();
+}
+
+void
+nsProfiler::ResetGathering()
+{
+  mPromiseHolder.reset();
+  mPendingProfiles = 0;
+  mGathering = false;
+  mWriter.reset();
+}
+
--- a/tools/profiler/gecko/nsProfiler.h
+++ b/tools/profiler/gecko/nsProfiler.h
@@ -5,21 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsProfiler_h
 #define nsProfiler_h
 
 #include "nsIProfiler.h"
 #include "nsIObserver.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MozPromise.h"
 #include "nsServiceManagerUtils.h"
-
-namespace mozilla {
-class ProfileGatherer;
-}
+#include "ProfileJSONWriter.h"
 
 class nsProfiler final : public nsIProfiler, public nsIObserver
 {
 public:
   nsProfiler();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
@@ -34,14 +33,27 @@ public:
     return static_cast<nsProfiler*>(iprofiler.get());
   }
 
   void GatheredOOPProfile(const nsACString& aProfile);
 
 private:
   ~nsProfiler();
 
-  RefPtr<mozilla::ProfileGatherer> mGatherer;
+  typedef mozilla::MozPromise<nsCString, nsresult, false> GatheringPromise;
+
+  RefPtr<GatheringPromise> StartGathering(double aSinceTime);
+  void CancelGathering();
+  void FinishGathering();
+  void ResetGathering();
+
   bool mLockedForPrivateBrowsing;
+
+  // These fields are all related to profile gathering.
+  nsTArray<nsCString> mExitProfiles;
+  mozilla::Maybe<mozilla::MozPromiseHolder<GatheringPromise>> mPromiseHolder;
+  mozilla::Maybe<SpliceableChunkedJSONWriter> mWriter;
+  uint32_t mPendingProfiles;
+  bool mGathering;
 };
 
 #endif // nsProfiler_h
 
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -23,17 +23,16 @@ if CONFIG['MOZ_GECKO_PROFILER']:
         'core/ProfileJSONWriter.cpp',
         'core/ProfilerBacktrace.cpp',
         'core/ProfilerMarkerPayload.cpp',
         'core/StackTop.cpp',
         'core/ThreadInfo.cpp',
         'gecko/ChildProfilerController.cpp',
         'gecko/nsProfilerFactory.cpp',
         'gecko/nsProfilerStartParams.cpp',
-        'gecko/ProfileGatherer.cpp',
         'gecko/ProfilerChild.cpp',
         'gecko/ProfilerIOInterposeObserver.cpp',
         'gecko/ProfilerParent.cpp',
         'gecko/ThreadResponsiveness.cpp',
     ]
     if CONFIG['OS_TARGET'] == 'Darwin':
         # This file cannot be built in unified mode because it includes
         # "nsLocalFile.h", which pulls in a system header which uses a type