Bug 1360471 (part 4) - Use a bitfield to represent profiler features. r=mstange.
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 01 May 2017 14:23:34 +1000
changeset 356562 bc25cff90b6ede8d18edfa2d4539c3fc2df3486a
parent 356561 ec6226c96adc7e2a9c4b171073892c054e393293
child 356563 86bff7bb38dfc514fb51d5aed9584fa3cd2f748e
push id89936
push usernnethercote@mozilla.com
push dateFri, 05 May 2017 01:06:18 +0000
treeherdermozilla-inbound@bc25cff90b6e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1360471
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 1360471 (part 4) - Use a bitfield to represent profiler features. r=mstange. Currently the profiler mostly uses an array of strings to represent which features are available and in use. This patch changes the profiler core to use a uint32_t bitfield, which is a much simpler and faster representation. (nsProfiler and the profiler add-on still use the array of strings, alas.) The new ProfilerFeature type defines the values in the bitfield. One side-effect of this change is that profiler_feature_active() now can be used to query all features. Previously it was just a subset. Another side-effect is that profiler_get_available_features() no longer incorrectly indicates support for Java and stack-walking when they aren't supported. (The handling of task tracer support is unchanged, because the old code handled it correctly.)
dom/ipc/ContentChild.cpp
dom/plugins/ipc/PluginModuleChild.cpp
gfx/ipc/GPUParent.cpp
gfx/layers/client/ContentClient.cpp
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TiledContentClient.cpp
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/TextureHost.cpp
gfx/layers/composite/TiledContentHost.cpp
gfx/thebes/ContextStateTracker.cpp
layout/base/RestyleTracker.cpp
layout/base/RestyleTracker.h
layout/base/nsLayoutUtils.cpp
tools/profiler/core/platform.cpp
tools/profiler/gecko/CrossProcessProfilerController.cpp
tools/profiler/gecko/ProfilerTypes.ipdlh
tools/profiler/gecko/nsIProfiler.idl
tools/profiler/gecko/nsProfiler.cpp
tools/profiler/gecko/nsProfilerStartParams.cpp
tools/profiler/gecko/nsProfilerStartParams.h
tools/profiler/public/GeckoProfiler.h
tools/profiler/tests/gtest/GeckoProfiler.cpp
widget/android/ANRReporter.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2565,28 +2565,22 @@ ContentChild::DeallocPOfflineCacheUpdate
     static_cast<OfflineCacheUpdateChild*>(actor);
   NS_RELEASE(offlineCacheUpdate);
   return true;
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvStartProfiler(const ProfilerInitParams& params)
 {
-  nsTArray<const char*> featureArray;
-  for (size_t i = 0; i < params.features().Length(); ++i) {
-    featureArray.AppendElement(params.features()[i].get());
-  }
-
   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(),
-                 featureArray.Elements(), featureArray.Length(),
+  profiler_start(params.entries(), params.interval(), params.features(),
                  filterArray.Elements(), filterArray.Length());
 
  return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvStopProfiler()
 {
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -2654,28 +2654,22 @@ void
 PluginModuleChild::ProcessNativeEvents() {
     CallProcessSomeEvents();    
 }
 #endif
 
 mozilla::ipc::IPCResult
 PluginModuleChild::RecvStartProfiler(const ProfilerInitParams& params)
 {
-    nsTArray<const char*> featureArray;
-    for (size_t i = 0; i < params.features().Length(); ++i) {
-        featureArray.AppendElement(params.features()[i].get());
-    }
-
     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(),
-                   featureArray.Elements(), featureArray.Length(),
+    profiler_start(params.entries(), params.interval(), params.features(),
                    filterArray.Elements(), filterArray.Length());
 
     return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 PluginModuleChild::RecvStopProfiler()
 {
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -384,27 +384,21 @@ GPUParent::RecvNotifyGpuObservers(const 
     obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 GPUParent::RecvStartProfiler(const ProfilerInitParams& params)
 {
-  nsTArray<const char*> featureArray;
-  for (size_t i = 0; i < params.features().Length(); ++i) {
-    featureArray.AppendElement(params.features()[i].get());
-  }
-
   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(),
-                 featureArray.Elements(), featureArray.Length(),
+  profiler_start(params.entries(), params.interval(), params.features(),
                  filterArray.Elements(), filterArray.Length());
 
  return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 GPUParent::RecvStopProfiler()
 {
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -97,17 +97,17 @@ ContentClient::EndPaint(nsTArray<Readbac
 }
 
 void
 ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
 
-  if (profiler_feature_active("displaylistdump")) {
+  if (profiler_feature_active(ProfilerFeature::DisplayListDump)) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     Dump(aStream, pfx.get(), false);
   }
 }
 
 // We pass a null pointer for the ContentClient Forwarder argument, which means
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1354,17 +1354,18 @@ TextureClient::PrintInfo(std::stringstre
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("TextureClient (0x%p)", this).get();
   AppendToString(aStream, GetSize(), " [size=", "]");
   AppendToString(aStream, GetFormat(), " [format=", "]");
   AppendToString(aStream, mFlags, " [flags=", "]");
 
 #ifdef MOZ_DUMP_PAINTING
-  if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+  if (gfxPrefs::LayersDumpTexture() ||
+      profiler_feature_active(ProfilerFeature::LayersDump)) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     aStream << "\n" << pfx.get() << "Surface: ";
     RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
     if (dSurf) {
       aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
     }
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1392,17 +1392,17 @@ ClientMultiTiledLayerBuffer::Progressive
 }
 
 void
 TiledContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("%sTiledContentClient (0x%p)", mName, this).get();
 
-  if (profiler_feature_active("displaylistdump")) {
+  if (profiler_feature_active(ProfilerFeature::DisplayListDump)) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     Dump(aStream, pfx.get(), false);
   }
 }
 
 void
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -831,17 +831,17 @@ LayerManagerComposite::Render(const nsIn
   bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0);
 
   // Set LayerScope begin/end frame
   LayerScopeAutoFrame frame(PR_Now());
 
   // Dump to console
   if (gfxPrefs::LayersDump()) {
     this->Dump(/* aSorted= */true);
-  } else if (profiler_feature_active("layersdump")) {
+  } else if (profiler_feature_active(ProfilerFeature::LayersDump)) {
     std::stringstream ss;
     Dump(ss);
     profiler_log(ss.str().c_str());
   }
 
   // Dump to LayerScope Viewer
   if (LayerScope::CheckSendable()) {
     // Create a LayersPacket, dump Layers into it and transfer the
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -403,17 +403,18 @@ TextureHost::PrintInfo(std::stringstream
   //       GetSize() and GetFormat() on it.
   if (Lock()) {
     AppendToString(aStream, GetSize(), " [size=", "]");
     AppendToString(aStream, GetFormat(), " [format=", "]");
     Unlock();
   }
   AppendToString(aStream, mFlags, " [flags=", "]");
 #ifdef MOZ_DUMP_PAINTING
-  if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+  if (gfxPrefs::LayersDumpTexture() ||
+      profiler_feature_active(ProfilerFeature::LayersDump)) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     aStream << "\n" << pfx.get() << "Surface: ";
     RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
     if (dSurf) {
       aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
     }
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -620,17 +620,18 @@ TiledContentHost::RenderLayerBuffer(Tile
 }
 
 void
 TiledContentHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("TiledContentHost (0x%p)", this).get();
 
-  if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+  if (gfxPrefs::LayersDumpTexture() ||
+      profiler_feature_active(ProfilerFeature::LayersDump)) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
 
     Dump(aStream, pfx.get(), false);
   }
 }
 
 void
--- a/gfx/thebes/ContextStateTracker.cpp
+++ b/gfx/thebes/ContextStateTracker.cpp
@@ -9,17 +9,17 @@
 #include "ProfilerMarkerPayload.h"
 #endif
 
 namespace mozilla {
 
 void
 ContextStateTrackerOGL::PushOGLSection(GLContext* aGL, const char* aSectionName)
 {
-  if (!profiler_feature_active("gpu")) {
+  if (!profiler_feature_active(ProfilerFeature::GPU)) {
     return;
   }
 
   if (!aGL->IsSupported(gl::GLFeature::query_objects)) {
     return;
   }
 
   if (mSectionStack.Length() > 0) {
--- a/layout/base/RestyleTracker.cpp
+++ b/layout/base/RestyleTracker.cpp
@@ -251,17 +251,17 @@ RestyleTracker::DoProcessRestyles()
 
         if (isTimelineRecording) {
           timelines->AddMarkerForDocShell(docShell, Move(
             MakeUnique<RestyleTimelineMarker>(
               data->mRestyleHint, MarkerTracingType::START)));
         }
 
         Maybe<GeckoProfilerTracingRAII> profilerRAII;
-        if (profiler_feature_active("restyle")) {
+        if (profiler_feature_active(ProfilerFeature::Restyle)) {
           profilerRAII.emplace("Paint", "Styles", Move(data->mBacktrace));
         }
         ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint,
                           data->mRestyleHintData);
         AddRestyleRootsIfAwaitingRestyle(data->mDescendants);
 
         if (isTimelineRecording) {
           timelines->AddMarkerForDocShell(docShell, Move(
@@ -355,17 +355,17 @@ RestyleTracker::DoProcessRestyles()
              currentRestyle != lastRestyle;
              ++currentRestyle) {
           LOG_RESTYLE("processing pending restyle %s at index %d/%d",
                       FrameTagToString(currentRestyle->mElement).get(),
                       index++, count);
           LOG_RESTYLE_INDENT();
 
           Maybe<GeckoProfilerTracingRAII> profilerRAII;
-          if (profiler_feature_active("restyle")) {
+          if (profiler_feature_active(ProfilerFeature::Restyle)) {
             profilerRAII.emplace("Paint", "Styles", Move(currentRestyle->mBacktrace));
           }
           if (isTimelineRecording) {
             timelines->AddMarkerForDocShell(docShell, Move(
               MakeUnique<RestyleTimelineMarker>(
                 currentRestyle->mRestyleHint, MarkerTracingType::START)));
           }
 
--- a/layout/base/RestyleTracker.h
+++ b/layout/base/RestyleTracker.h
@@ -254,17 +254,17 @@ RestyleTracker::AddPendingRestyleToTable
                  "why are we getting eRestyle_SomeDescendants in an "
                  "animation-only restyle?");
     aElement->SetFlags(ConditionalDescendantsBit());
   }
 
   if (!existingData) {
     RestyleData* rd =
       new RestyleData(aRestyleHint, aMinChangeHint, aRestyleHintData);
-    if (profiler_feature_active("restyle")) {
+    if (profiler_feature_active(ProfilerFeature::Restyle)) {
       rd->mBacktrace = profiler_get_backtrace();
     }
     mPendingRestyles.Put(aElement, rd);
     return false;
   }
 
   bool hadRestyleLaterSiblings =
     (existingData->mRestyleHint & eRestyle_LaterSiblings) != 0;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3631,17 +3631,18 @@ nsLayoutUtils::PaintFrame(nsRenderingCon
         }
       }
     }
   }
 
   Telemetry::AccumulateTimeDelta(Telemetry::PAINT_BUILD_DISPLAYLIST_TIME,
                                  startBuildDisplayList);
 
-  bool profilerNeedsDisplayList = profiler_feature_active("displaylistdump");
+  bool profilerNeedsDisplayList =
+    profiler_feature_active(ProfilerFeature::DisplayListDump);
   bool consoleNeedsDisplayList = gfxUtils::DumpDisplayList() || gfxEnv::DumpPaint();
 #ifdef MOZ_DUMP_PAINTING
   FILE* savedDumpFile = gfxUtils::sDumpPaintFile;
 #endif
 
   UniquePtr<std::stringstream> ss;
   if (consoleNeedsDisplayList || profilerNeedsDisplayList) {
     ss = MakeUnique<std::stringstream>();
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -248,77 +248,57 @@ NewSamplerThread(PSLockRef aLock, uint32
 // profiler is active. When not instantiated, the profiler is inactive.
 //
 // Accesses to ActivePS are guarded by gPSMutex, in much the same fashion as
 // CorePS.
 //
 class ActivePS
 {
 private:
-  bool HasFeature(const char** aFeatures, uint32_t aFeatureCount,
-                  const char* aFeature)
+  static uint32_t AdjustFeatures(uint32_t aFeatures, uint32_t aFilterCount)
   {
-    for (size_t i = 0; i < aFeatureCount; i++) {
-      if (strcmp(aFeatures[i], aFeature) == 0) {
-        return true;
-      }
+    // Filter out any features unavailable in this platform/configuration.
+    aFeatures &= profiler_get_available_features();
+
+#if defined(PROFILE_JAVA)
+    if (!mozilla::jni::IsFennec()) {
+      aFeatures &= ~ProfilerFeature::Java;
     }
-    return false;
+#endif
+
+    // Always enable ProfilerFeature::Threads if we have a filter, because
+    // users sometimes ask to filter by a list of threads but forget to
+    // explicitly specify ProfilerFeature::Threads.
+    if (aFilterCount > 0) {
+      aFeatures |= ProfilerFeature::Threads;
+    }
+
+    return aFeatures;
   }
 
   ActivePS(PSLockRef aLock, int aEntries, double aInterval,
-           const char** aFeatures, uint32_t aFeatureCount,
-           const char** aFilters, uint32_t aFilterCount)
+           uint32_t aFeatures, const char** aFilters, uint32_t aFilterCount)
     : mGeneration(sNextGeneration++)
     , mEntries(aEntries)
     , mInterval(aInterval)
-#define HAS_FEATURE(feature) HasFeature(aFeatures, aFeatureCount, feature)
-    , mFeatureDisplayListDump(HAS_FEATURE("displaylistdump"))
-    , mFeatureGPU(HAS_FEATURE("gpu"))
-#if defined(PROFILE_JAVA)
-    , mFeatureJava(mozilla::jni::IsFennec() && HAS_FEATURE("java"))
-#else
-    , mFeatureJava(false)
-#endif
-    , mFeatureJS(HAS_FEATURE("js"))
-    , mFeatureLayersDump(HAS_FEATURE("layersdump"))
-    , mFeatureLeaf(HAS_FEATURE("leaf"))
-    , mFeatureMainThreadIO(HAS_FEATURE("mainthreadio"))
-    , mFeatureMemory(HAS_FEATURE("memory"))
-    , mFeaturePrivacy(HAS_FEATURE("privacy"))
-    , mFeatureRestyle(HAS_FEATURE("restyle"))
-    , mFeatureStackWalk(HAS_FEATURE("stackwalk"))
-#ifdef MOZ_TASK_TRACER
-    , mFeatureTaskTracer(HAS_FEATURE("tasktracer"))
-#else
-    , mFeatureTaskTracer(false)
-#endif
-      // Profile non-main threads if we have a filter, because users sometimes
-      // ask to filter by a list of threads but forget to explicitly request.
-    , mFeatureThreads(HAS_FEATURE("threads") || aFilterCount > 0)
-#undef HAS_FEATURE
+    , mFeatures(AdjustFeatures(aFeatures, aFilterCount))
     , mBuffer(new ProfileBuffer(aEntries))
       // The new sampler thread doesn't start sampling immediately because the
       // main loop within Run() is blocked until this function's caller unlocks
       // gPSMutex.
     , mSamplerThread(NewSamplerThread(aLock, mGeneration, aInterval))
-    , mInterposeObserver(mFeatureMainThreadIO
+    , mInterposeObserver(ProfilerFeature::HasMainThreadIO(aFeatures)
                          ? new mozilla::ProfilerIOInterposeObserver()
                          : nullptr)
+#undef HAS_FEATURE
     , mIsPaused(false)
 #if defined(GP_OS_linux)
     , mWasPaused(false)
 #endif
   {
-    // Deep copy aFeatures.
-    MOZ_ALWAYS_TRUE(mFeatures.resize(aFeatureCount));
-    for (uint32_t i = 0; i < aFeatureCount; ++i) {
-      mFeatures[i] = aFeatures[i];
-    }
-
     // Deep copy aFilters.
     MOZ_ALWAYS_TRUE(mFilters.resize(aFilterCount));
     for (uint32_t i = 0; i < aFilterCount; ++i) {
       mFilters[i] = aFilters[i];
     }
 
     if (mInterposeObserver) {
       mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll,
@@ -357,21 +337,21 @@ private:
       }
     }
 
     return false;
   }
 
 public:
   static void Create(PSLockRef aLock, int aEntries, double aInterval,
-                     const char** aFeatures, uint32_t aFeatureCount,
+                     uint32_t aFeatures,
                      const char** aFilters, uint32_t aFilterCount)
   {
     sInstance = new ActivePS(aLock, aEntries, aInterval, aFeatures,
-                             aFeatureCount, aFilters, aFilterCount);
+                             aFilters, aFilterCount);
   }
 
   static MOZ_MUST_USE SamplerThread* Destroy(PSLockRef aLock)
   {
     auto samplerThread = sInstance->mSamplerThread;
     delete sInstance;
     sInstance = nullptr;
 
@@ -390,44 +370,40 @@ public:
   }
 
   static bool ShouldProfileThread(PSLockRef aLock, ThreadInfo* aInfo)
   {
     // This function runs both on and off the main thread.
 
     MOZ_RELEASE_ASSERT(sInstance);
 
-    return ((aInfo->IsMainThread() || sInstance->mFeatureThreads) &&
+    return ((aInfo->IsMainThread() || FeatureThreads(aLock)) &&
             sInstance->ThreadSelected(aInfo->Name()));
   }
 
   PS_GET(uint32_t, Generation)
 
   PS_GET(int, Entries)
 
   PS_GET(double, Interval)
 
-  PS_GET(const Vector<std::string>&, Features)
+  PS_GET(uint32_t, Features)
+
+  #define PS_GET_FEATURE(n_, str_, Name_) \
+    static bool Feature##Name_(PSLockRef) \
+    { \
+      return ProfilerFeature::Has##Name_(sInstance->mFeatures); \
+    }
+
+  PROFILER_FOR_EACH_FEATURE(PS_GET_FEATURE)
+
+  #undef PS_GET_FEATURE
 
   PS_GET(const Vector<std::string>&, Filters)
 
-  PS_GET(bool, FeatureDisplayListDump)
-  PS_GET(bool, FeatureGPU)
-  PS_GET(bool, FeatureJava)
-  PS_GET(bool, FeatureJS)
-  PS_GET(bool, FeatureLayersDump)
-  PS_GET(bool, FeatureLeaf)
-  PS_GET(bool, FeatureMainThreadIO)
-  PS_GET(bool, FeatureMemory)
-  PS_GET(bool, FeaturePrivacy)
-  PS_GET(bool, FeatureRestyle)
-  PS_GET(bool, FeatureStackWalk)
-  PS_GET(bool, FeatureTaskTracer)
-  PS_GET(bool, FeatureThreads)
-
   static ProfileBuffer* Buffer(PSLockRef) { return sInstance->mBuffer.get(); }
 
   PS_GET_AND_SET(bool, IsPaused)
 
 #if defined(GP_OS_linux)
   PS_GET_AND_SET(bool, WasPaused)
 #endif
 
@@ -457,36 +433,21 @@ private:
 
   // The number of entries in mBuffer.
   const int mEntries;
 
   // The interval between samples, measured in milliseconds.
   const double mInterval;
 
   // The profile features that are enabled.
-  Vector<std::string> mFeatures;
+  const uint32_t mFeatures;
 
   // Substrings of names of threads we want to profile.
   Vector<std::string> mFilters;
 
-  // Configuration flags derived from mFeatures.
-  const bool mFeatureDisplayListDump;
-  const bool mFeatureGPU;
-  const bool mFeatureJava;
-  const bool mFeatureJS;
-  const bool mFeatureLayersDump;
-  const bool mFeatureLeaf;
-  const bool mFeatureMainThreadIO;
-  const bool mFeatureMemory;
-  const bool mFeaturePrivacy;
-  const bool mFeatureRestyle;
-  const bool mFeatureStackWalk;
-  const bool mFeatureTaskTracer;
-  const bool mFeatureThreads;
-
   // The buffer into which all samples are recorded. Always used in conjunction
   // with CorePS::m{Live,Dead}Threads.
   const UniquePtr<ProfileBuffer> mBuffer;
 
   // The current sampler thread. This class is not responsible for destroying
   // the SamplerThread object; the Destroy() method returns it so the caller
   // can destroy it.
   SamplerThread* const mSamplerThread;
@@ -1993,41 +1954,35 @@ locked_register_thread(PSLockRef aLock, 
       info->PollJSSampling();
     }
   }
 
   CorePS::LiveThreads(aLock).push_back(info);
 }
 
 static void
-NotifyProfilerStarted(const int aEntries, double aInterval,
-                      const char** aFeatures, uint32_t aFeatureCount,
+NotifyProfilerStarted(const int aEntries, double aInterval, uint32_t aFeatures,
                       const char** aFilters, uint32_t aFilterCount)
 {
   if (!CanNotifyObservers()) {
     return;
   }
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (!os) {
     return;
   }
 
-  nsTArray<nsCString> featuresArray;
-  for (size_t i = 0; i < aFeatureCount; ++i) {
-    featuresArray.AppendElement(aFeatures[i]);
-  }
-
   nsTArray<nsCString> filtersArray;
   for (size_t i = 0; i < aFilterCount; ++i) {
     filtersArray.AppendElement(aFilters[i]);
   }
 
   nsCOMPtr<nsIProfilerStartParams> params =
-    new nsProfilerStartParams(aEntries, aInterval, featuresArray, filtersArray);
+    new nsProfilerStartParams(aEntries, aInterval, aFeatures, filtersArray);
 
   os->NotifyObservers(params, "profiler-started", nullptr);
 }
 
 static void
 NotifyObservers(const char* aTopic)
 {
   if (!CanNotifyObservers()) {
@@ -2039,36 +1994,37 @@ NotifyObservers(const char* aTopic)
     return;
   }
 
   os->NotifyObservers(nullptr, aTopic, nullptr);
 }
 
 static void
 locked_profiler_start(PSLockRef aLock, const int aEntries, double aInterval,
-                      const char** aFeatures, uint32_t aFeatureCount,
+                      uint32_t aFeatures,
                       const char** aFilters, uint32_t aFilterCount);
 
 void
 profiler_init(void* aStackTop)
 {
   LOG("profiler_init");
 
   MOZ_RELEASE_ASSERT(!CorePS::Exists());
 
-  const char* features[] = { "js"
+  uint32_t features =
 #if defined(PROFILE_JAVA)
-                           , "java"
+                      ProfilerFeature::Java |
 #endif
-                           , "leaf"
+                      ProfilerFeature::JS |
+                      ProfilerFeature::Leaf |
 #if defined(HAVE_NATIVE_UNWIND)
-                           , "stackwalk"
+                      ProfilerFeature::StackWalk |
 #endif
-                           , "threads"
-                           };
+                      ProfilerFeature::Threads |
+                      0;
 
   const char* filters[] = { "GeckoMain", "Compositor" };
 
   if (getenv("MOZ_PROFILER_HELP")) {
     PrintUsageThenExit(0); // terminates execution
   }
 
   {
@@ -2124,26 +2080,24 @@ profiler_init(void* aStackTop)
       interval = strtol(startupInterval, nullptr, 10);
       if (errno == 0 && 1 <= interval && interval <= 1000) {
         LOG("- MOZ_PROFILER_STARTUP_INTERVAL = %d", interval);
       } else {
         PrintUsageThenExit(1);
       }
     }
 
-    locked_profiler_start(lock, entries, interval,
-                          features, MOZ_ARRAY_LENGTH(features),
+    locked_profiler_start(lock, entries, interval, features,
                           filters, MOZ_ARRAY_LENGTH(filters));
   }
 
   // We do this with gPSMutex unlocked. The comment in profiler_stop() explains
   // why.
   NotifyProfilerStarted(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                        features, MOZ_ARRAY_LENGTH(features),
-                        filters, MOZ_ARRAY_LENGTH(filters));
+                        features, filters, MOZ_ARRAY_LENGTH(filters));
 }
 
 static void
 locked_profiler_save_profile_to_file(PSLockRef aLock, const char* aFilename);
 
 static SamplerThread*
 locked_profiler_stop(PSLockRef aLock);
 
@@ -2211,46 +2165,40 @@ profiler_get_profile(double aSinceTime)
     b.EndArray();
   }
   b.End();
 
   return b.WriteFunc()->CopyData();
 }
 
 void
-profiler_get_start_params(int* aEntries, double* aInterval,
-                          mozilla::Vector<const char*>* aFeatures,
+profiler_get_start_params(int* aEntries, double* aInterval, uint32_t* aFeatures,
                           mozilla::Vector<const char*>* aFilters)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   if (NS_WARN_IF(!aEntries) || NS_WARN_IF(!aInterval) ||
       NS_WARN_IF(!aFeatures) || NS_WARN_IF(!aFilters)) {
     return;
   }
 
   PSAutoLock lock(gPSMutex);
 
   if (!ActivePS::Exists(lock)) {
     *aEntries = 0;
     *aInterval = 0;
-    aFeatures->clear();
+    *aFeatures = 0;
     aFilters->clear();
     return;
   }
 
   *aEntries = ActivePS::Entries(lock);
   *aInterval = ActivePS::Interval(lock);
-
-  const Vector<std::string>& features = ActivePS::Features(lock);
-  MOZ_ALWAYS_TRUE(aFeatures->resize(features.length()));
-  for (size_t i = 0; i < features.length(); ++i) {
-    (*aFeatures)[i] = features[i].c_str();
-  }
+  *aFeatures = ActivePS::Features(lock);
 
   const Vector<std::string>& filters = ActivePS::Filters(lock);
   MOZ_ALWAYS_TRUE(aFilters->resize(filters.length()));
   for (uint32_t i = 0; i < filters.length(); ++i) {
     (*aFilters)[i] = filters[i].c_str();
   }
 }
 
@@ -2293,56 +2241,41 @@ profiler_save_profile_to_file(const char
 
   if (!ActivePS::Exists(lock)) {
     return;
   }
 
   locked_profiler_save_profile_to_file(lock, aFilename);
 }
 
-const char**
+uint32_t
 profiler_get_available_features()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
-  static const char* features[] = {
-#if defined(HAVE_NATIVE_UNWIND)
-    // Walk the C++ stack.
-    "stackwalk",
+  uint32_t features = 0;
+
+  #define ADD_FEATURE(n_, str_, Name_) ProfilerFeature::Set##Name_(features);
+
+  // Add all the possible features.
+  PROFILER_FOR_EACH_FEATURE(ADD_FEATURE)
+
+  #undef ADD_FEATURE
+
+  // Now remove features not supported on this platform/configuration.
+#if !defined(PROFILE_JAVA)
+  ProfilerFeature::ClearJava(features);
 #endif
-    // Include the C++ leaf node if not stackwalking. DevTools
-    // profiler doesn't want the native addresses.
-    "leaf",
-    // Profile Java code (Android only).
-    "java",
-    // Tell the JS engine to emit pseudostack entries in the prologue/epilogue.
-    "js",
-    // GPU Profiling (may not be supported by the GL)
-    "gpu",
-    // Profile the registered secondary threads.
-    "threads",
-    // Do not include user-identifiable information
-    "privacy",
-    // Dump the layer tree with the textures.
-    "layersdump",
-    // Dump the display list with the textures.
-    "displaylistdump",
-    // Add main thread I/O to the profile
-    "mainthreadio",
-    // Add RSS collection
-    "memory",
-    // Restyle profiling.
-    "restyle",
-#ifdef MOZ_TASK_TRACER
-    // Start profiling with feature TaskTracer.
-    "tasktracer",
+#if !defined(HAVE_NATIVE_UNWIND)
+  ProfilerFeature::ClearStackWalk(features);
 #endif
-    nullptr
-  };
+#if !defined(MOZ_TASK_TRACER)
+  ProfilerFeature::ClearTaskTracer(features);
+#endif
 
   return features;
 }
 
 void
 profiler_get_buffer_info_helper(uint32_t* aCurrentPosition,
                                 uint32_t* aEntries,
                                 uint32_t* aGeneration)
@@ -2361,40 +2294,46 @@ profiler_get_buffer_info_helper(uint32_t
 
   *aCurrentPosition = ActivePS::Buffer(lock)->mWritePos;
   *aEntries = ActivePS::Entries(lock);
   *aGeneration = ActivePS::Buffer(lock)->mGeneration;
 }
 
 static void
 locked_profiler_start(PSLockRef aLock, int aEntries, double aInterval,
-                      const char** aFeatures, uint32_t aFeatureCount,
+                      uint32_t aFeatures,
                       const char** aFilters, uint32_t aFilterCount)
 {
   if (LOG_TEST) {
     LOG("locked_profiler_start");
     LOG("- entries  = %d", aEntries);
     LOG("- interval = %.2f", aInterval);
-    for (uint32_t i = 0; i < aFeatureCount; i++) {
-      LOG("- feature  = %s", aFeatures[i]);
-    }
+
+    #define LOG_FEATURE(n_, str_, Name_) \
+      if (ProfilerFeature::Has##Name_(aFeatures)) { \
+        LOG("- feature  = %s", str_); \
+      }
+
+    PROFILER_FOR_EACH_FEATURE(LOG_FEATURE)
+
+    #undef LOG_FEATURE
+
     for (uint32_t i = 0; i < aFilterCount; i++) {
       LOG("- threads  = %s", aFilters[i]);
     }
   }
 
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(CorePS::Exists() && !ActivePS::Exists(aLock));
 
   // Fall back to the default values if the passed-in values are unreasonable.
   int entries = aEntries > 0 ? aEntries : PROFILER_DEFAULT_ENTRIES;
   double interval = aInterval > 0 ? aInterval : PROFILER_DEFAULT_INTERVAL;
 
-  ActivePS::Create(aLock, entries, interval, aFeatures, aFeatureCount,
-                   aFilters, aFilterCount);
+  ActivePS::Create(aLock, entries, interval, aFeatures, aFilters, aFilterCount);
 
   // Set up profiling for each registered thread, if appropriate.
   Thread::tid_t tid = Thread::GetCurrentId();
   const CorePS::ThreadVector& liveThreads = CorePS::LiveThreads(aLock);
   for (uint32_t i = 0; i < liveThreads.size(); i++) {
     ThreadInfo* info = liveThreads.at(i);
 
     if (ActivePS::ShouldProfileThread(aLock, info)) {
@@ -2429,18 +2368,17 @@ locked_profiler_start(PSLockRef aLock, i
       javaInterval = 10;
     }
     mozilla::java::GeckoJavaSampler::Start(javaInterval, 1000);
   }
 #endif
 }
 
 void
-profiler_start(int aEntries, double aInterval,
-               const char** aFeatures, uint32_t aFeatureCount,
+profiler_start(int aEntries, double aInterval, uint32_t aFeatures,
                const char** aFilters, uint32_t aFilterCount)
 {
   LOG("profiler_start");
 
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   SamplerThread* samplerThread = nullptr;
   {
@@ -2451,27 +2389,27 @@ profiler_start(int aEntries, double aInt
       profiler_init(nullptr);
     }
 
     // Reset the current state if the profiler is running.
     if (ActivePS::Exists(lock)) {
       samplerThread = locked_profiler_stop(lock);
     }
 
-    locked_profiler_start(lock, aEntries, aInterval, aFeatures, aFeatureCount,
+    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) {
     NotifyObservers("profiler-stopped");
     delete samplerThread;
   }
-  NotifyProfilerStarted(aEntries, aInterval, aFeatures, aFeatureCount,
+  NotifyProfilerStarted(aEntries, aInterval, aFeatures,
                         aFilters, aFilterCount);
 }
 
 static MOZ_MUST_USE SamplerThread*
 locked_profiler_stop(PSLockRef aLock)
 {
   LOG("locked_profiler_stop");
 
@@ -2609,45 +2547,29 @@ profiler_resume()
     ActivePS::SetIsPaused(lock, false);
   }
 
   // gPSMutex must be unlocked when we notify, to avoid potential deadlocks.
   NotifyObservers("profiler-resumed");
 }
 
 bool
-profiler_feature_active(const char* aName)
+profiler_feature_active(uint32_t aFeature)
 {
   // This function runs both on and off the main thread.
 
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   PSAutoLock lock(gPSMutex);
 
   if (!ActivePS::Exists(lock)) {
     return false;
   }
 
-  if (strcmp(aName, "displaylistdump") == 0) {
-    return ActivePS::FeatureDisplayListDump(lock);
-  }
-
-  if (strcmp(aName, "gpu") == 0) {
-    return ActivePS::FeatureGPU(lock);
-  }
-
-  if (strcmp(aName, "layersdump") == 0) {
-    return ActivePS::FeatureLayersDump(lock);
-  }
-
-  if (strcmp(aName, "restyle") == 0) {
-    return ActivePS::FeatureRestyle(lock);
-  }
-
-  return false;
+  return !!(ActivePS::Features(lock) & aFeature);
 }
 
 bool
 profiler_is_active()
 {
   // This function runs both on and off the main thread.
 
   MOZ_RELEASE_ASSERT(CorePS::Exists());
--- a/tools/profiler/gecko/CrossProcessProfilerController.cpp
+++ b/tools/profiler/gecko/CrossProcessProfilerController.cpp
@@ -99,17 +99,17 @@ CrossProcessProfilerController::StartPro
     return;
   }
 
   ProfilerInitParams ipcParams;
 
   ipcParams.enabled() = true;
   aParams->GetEntries(&ipcParams.entries());
   aParams->GetInterval(&ipcParams.interval());
-  ipcParams.features() = aParams->GetFeatures();
+  aParams->GetFeatures(&ipcParams.features());
   ipcParams.filters() = aParams->GetFilters();
 
   mProcess->SendStartProfiler(ipcParams);
 }
 
 void
 CrossProcessProfilerController::Observe(nsISupports* aSubject,
                                         const char* aTopic)
--- a/tools/profiler/gecko/ProfilerTypes.ipdlh
+++ b/tools/profiler/gecko/ProfilerTypes.ipdlh
@@ -4,13 +4,13 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace mozilla {
 
 struct ProfilerInitParams {
   bool enabled;
   uint32_t entries;
   double interval;
+  uint32_t features;
   nsCString[] filters;
-  nsCString[] features;
 };
 
 } // namespace mozilla
--- a/tools/profiler/gecko/nsIProfiler.idl
+++ b/tools/profiler/gecko/nsIProfiler.idl
@@ -17,18 +17,18 @@ class nsCString;
  * which, unfortunately, means we need to implement nsISupports in order to
  * go through it.
  */
 [builtinclass, uuid(0a175ba7-8fcf-4ce9-9c4b-ccc6272f4425)]
 interface nsIProfilerStartParams : nsISupports
 {
   readonly attribute uint32_t entries;
   readonly attribute double interval;
+  readonly attribute uint32_t features;
 
-  [noscript, notxpcom, nostdcall] StringArrayRef getFeatures();
   [noscript, notxpcom, nostdcall] StringArrayRef getFilters();
 };
 
 [scriptable, uuid(ead3f75c-0e0e-4fbb-901c-1e5392ef5b2a)]
 interface nsIProfiler : nsISupports
 {
   boolean CanProfile();
   void StartProfiler(in uint32_t aEntries, in double aInterval,
--- a/tools/profiler/gecko/nsProfiler.cpp
+++ b/tools/profiler/gecko/nsProfiler.cpp
@@ -88,27 +88,48 @@ nsProfiler::Observe(nsISupports *aSubjec
 
 NS_IMETHODIMP
 nsProfiler::CanProfile(bool *aCanProfile)
 {
   *aCanProfile = !mLockedForPrivateBrowsing;
   return NS_OK;
 }
 
+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;
+}
+
 NS_IMETHODIMP
 nsProfiler::StartProfiler(uint32_t aEntries, double aInterval,
                           const char** aFeatures, uint32_t aFeatureCount,
                           const char** aFilters, uint32_t aFilterCount)
 {
   if (mLockedForPrivateBrowsing) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  profiler_start(aEntries, aInterval,
-                 aFeatures, aFeatureCount, aFilters, aFilterCount);
+  #define ADD_FEATURE_BIT(n_, str_, Name_) \
+    if (HasFeature(aFeatures, aFeatureCount, str_)) { \
+      features |= ProfilerFeature::Name_; \
+    }
+
+  // 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);
 
   // Do this after profiler_start().
   mGatherer = new ProfileGatherer();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -335,69 +356,71 @@ nsProfiler::GetElapsedTime(double* aElap
 NS_IMETHODIMP
 nsProfiler::IsActive(bool *aIsActive)
 {
   *aIsActive = profiler_is_active();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsProfiler::GetFeatures(uint32_t *aCount, char ***aFeatures)
+nsProfiler::GetFeatures(uint32_t* aCount, char*** aFeatureList)
 {
-  uint32_t len = 0;
+  uint32_t features = profiler_get_available_features();
 
-  const char** features = profiler_get_available_features();
-  if (!features) {
-    *aCount = 0;
-    *aFeatures = nullptr;
-    return NS_OK;
-  }
+  #define COUNT_IF_SET(n_, str_, Name_) \
+    if (ProfilerFeature::Has##Name_(features)) { \
+      len++; \
+    }
+
+  // Count the number of features in use.
+  uint32_t len = 0;
+  PROFILER_FOR_EACH_FEATURE(COUNT_IF_SET)
+
+  #undef COUNT_IF_SET
 
-  while (features[len]) {
-    len++;
-  }
-
-  char **featureList = static_cast<char **>
-                       (moz_xmalloc(len * sizeof(char*)));
+  auto featureList = static_cast<char**>(moz_xmalloc(len * sizeof(char*)));
 
-  for (size_t i = 0; i < len; i++) {
-    size_t strLen = strlen(features[i]);
-    featureList[i] = static_cast<char *>
-                         (nsMemory::Clone(features[i], (strLen + 1) * sizeof(char)));
-  }
+  #define DUP_IF_SET(n_, str_, Name_) \
+    if (ProfilerFeature::Has##Name_(features)) { \
+      size_t strLen = strlen(str_); \
+      featureList[i] = static_cast<char*>( \
+        nsMemory::Clone(str_, (strLen + 1) * sizeof(char))); \
+      i++; \
+    }
 
-  *aFeatures = featureList;
+  // Insert the strings for the features in use.
+  size_t i = 0;
+  PROFILER_FOR_EACH_FEATURE(DUP_IF_SET)
+
+  #undef STRDUP_IF_SET
+
+  *aFeatureList = featureList;
   *aCount = len;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::GetStartParams(nsIProfilerStartParams** aRetVal)
 {
   if (!profiler_is_active()) {
     *aRetVal = nullptr;
   } else {
     int entries = 0;
     double interval = 0;
+    uint32_t features = 0;
     mozilla::Vector<const char*> filters;
-    mozilla::Vector<const char*> features;
     profiler_get_start_params(&entries, &interval, &features, &filters);
 
-    nsTArray<nsCString> featuresArray;
-    for (size_t i = 0; i < features.length(); ++i) {
-      featuresArray.AppendElement(features[i]);
-    }
-
     nsTArray<nsCString> filtersArray;
     for (uint32_t i = 0; i < filters.length(); ++i) {
       filtersArray.AppendElement(filters[i]);
     }
 
     nsCOMPtr<nsIProfilerStartParams> startParams =
-      new nsProfilerStartParams(entries, interval, featuresArray, filtersArray);
+      new nsProfilerStartParams(entries, interval, features, filtersArray);
 
     startParams.forget(aRetVal);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::GetBufferInfo(uint32_t* aCurrentPosition, uint32_t* aTotalSize,
--- a/tools/profiler/gecko/nsProfilerStartParams.cpp
+++ b/tools/profiler/gecko/nsProfilerStartParams.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsProfilerStartParams.h"
 
 NS_IMPL_ISUPPORTS(nsProfilerStartParams, nsIProfilerStartParams)
 
 nsProfilerStartParams::nsProfilerStartParams(uint32_t aEntries,
                                              double aInterval,
-                                             const nsTArray<nsCString>& aFeatures,
+                                             uint32_t aFeatures,
                                              const nsTArray<nsCString>& aFilters) :
   mEntries(aEntries),
   mInterval(aInterval),
   mFeatures(aFeatures),
   mFilters(aFilters)
 {
 }
 
@@ -33,19 +33,21 @@ nsProfilerStartParams::GetEntries(uint32
 NS_IMETHODIMP
 nsProfilerStartParams::GetInterval(double* aInterval)
 {
   NS_ENSURE_ARG_POINTER(aInterval);
   *aInterval = mInterval;
   return NS_OK;
 }
 
-const nsTArray<nsCString>&
-nsProfilerStartParams::GetFeatures()
+NS_IMETHODIMP
+nsProfilerStartParams::GetFeatures(uint32_t* aFeatures)
 {
-  return mFeatures;
+  NS_ENSURE_ARG_POINTER(aFeatures);
+  *aFeatures = mFeatures;
+  return NS_OK;
 }
 
 const nsTArray<nsCString>&
 nsProfilerStartParams::GetFilters()
 {
   return mFilters;
 }
--- a/tools/profiler/gecko/nsProfilerStartParams.h
+++ b/tools/profiler/gecko/nsProfilerStartParams.h
@@ -13,20 +13,20 @@
 class nsProfilerStartParams : public nsIProfilerStartParams
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPROFILERSTARTPARAMS
 
   nsProfilerStartParams(uint32_t aEntries,
                         double aInterval,
-                        const nsTArray<nsCString>& aFeatures,
+                        uint32_t aFeatures,
                         const nsTArray<nsCString>& aFilters);
 
 private:
   virtual ~nsProfilerStartParams();
   uint32_t mEntries;
   double mInterval;
-  nsTArray<nsCString> mFeatures;
+  uint32_t mFeatures;
   nsTArray<nsCString> mFilters;
 };
 
 #endif
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -1,9 +1,10 @@
 /* -*- Mode: C++; tab-width: 2; 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/. */
 
 // The Gecko Profiler is an always-on profiler that takes fast and low overhead
 // samples of the program execution using only userspace functionality for
 // portability. The goal of this module is to provide performance data in a
 // generic cross-platform way without requiring custom tools or kernel support.
@@ -104,16 +105,74 @@ using UniqueProfilerBacktrace =
 #define PROFILER_MARKER(info) do {} while (0)
 #define PROFILER_MARKER_PAYLOAD(info, payload) \
   do { \
     mozilla::UniquePtr<ProfilerMarkerPayload> payloadDeletor(payload); \
   } while (0)
 
 #else   // defined(MOZ_GECKO_PROFILER)
 
+// Higher-order macro containing all the feature info in one place. Define
+// |macro| appropriately to extract the relevant parts. Note that the number
+// values are used internally only and so can be changed without consequence.
+#define PROFILER_FOR_EACH_FEATURE(macro) \
+  /* Dump the display list with the textures. */ \
+  macro(0, "displaylistdump", DisplayListDump) \
+  \
+  /* GPU Profiling (may not be supported by the GL). */ \
+  macro(1, "gpu", GPU) \
+  \
+  /* Profile Java code (Android only). */ \
+  macro(2, "java", Java) \
+  \
+  /* Get the JS engine to emit pseudostack entries in prologues/epilogues */ \
+  macro(3, "js", JS) \
+  \
+  /* Dump the layer tree with the textures. */ \
+  macro(4, "layersdump", LayersDump) \
+  \
+  /* Include the C++ leaf node if not stackwalking. */ \
+  /* The DevTools profiler doesn't want the native addresses. */ \
+  macro(5, "leaf", Leaf) \
+  \
+  /* Add main thread I/O to the profile. */ \
+  macro(6, "mainthreadio", MainThreadIO) \
+  \
+  /* Add memory measurements (e.g. RSS). */ \
+  macro(7, "memory", Memory) \
+  \
+  /* Do not include user-identifiable information. */ \
+  macro(8, "privacy", Privacy) \
+  \
+  /* Restyle profiling. */ \
+  macro(9, "restyle", Restyle) \
+  \
+  /* Walk the C++ stack. Not available on all platforms. */ \
+  macro(10, "stackwalk", StackWalk) \
+  \
+  /* Start profiling with feature TaskTracer. */ \
+  macro(11, "tasktracer", TaskTracer) \
+  \
+  /* Profile the registered secondary threads. */ \
+  macro(12, "threads", Threads)
+
+struct ProfilerFeature
+{
+  #define DECLARE(n_, str_, Name_) \
+    static const uint32_t Name_ = (1u << n_); \
+    static bool Has##Name_(uint32_t aFeatures) { return aFeatures & Name_; } \
+    static void Set##Name_(uint32_t& aFeatures) { aFeatures |= Name_; } \
+    static void Clear##Name_(uint32_t& aFeatures) { aFeatures &= ~Name_; }
+
+  // Define a bitfield constant, a getter, and two setters for each feature.
+  PROFILER_FOR_EACH_FEATURE(DECLARE)
+
+  #undef DECLARE
+};
+
 #if defined(__GNUC__) || defined(_MSC_VER)
 # define PROFILER_FUNCTION_NAME __FUNCTION__
 #else
   // From C99, supported by some C++ compilers. Just the raw function name.
 # define PROFILER_FUNCTION_NAME __func__
 #endif
 
 #define PROFILER_FUNC(decl, rv)  decl;
@@ -167,21 +226,21 @@ PROFILER_FUNC_VOID(profiler_init(void* s
 PROFILER_FUNC_VOID(profiler_shutdown())
 
 // Start the profiler -- initializing it first if necessary -- with the
 // selected options. Stops and restarts the profiler if it is already active.
 // After starting the profiler is "active". The samples will be recorded in a
 // circular buffer.
 //   "aEntries" is the number of entries in the profiler's circular buffer.
 //   "aInterval" the sampling interval, measured in millseconds.
+//   "aFeatures" is the feature set. Features unsupported by this
+//               platform/configuration are ignored.
 PROFILER_FUNC_VOID(profiler_start(int aEntries, double aInterval,
-                                  const char** aFeatures,
-                                  uint32_t aFeatureCount,
-                                  const char** aFilters,
-                                  uint32_t aFilterCount))
+                                  uint32_t aFeatures,
+                                  const char** aFilters, uint32_t aFilterCount))
 
 // Stop the profiler and discard the profile without saving it. A no-op if the
 // profiler is inactive. After stopping the profiler is "inactive".
 PROFILER_FUNC_VOID(profiler_stop())
 
 // Pause and resume the profiler. No-ops if the profiler is inactive. While
 // paused the profile will not take any samples and will not record any data
 // into its buffers. The profiler remains fully initialized in this state.
@@ -221,54 +280,54 @@ ProfilerBacktraceDestructor::operator()(
 // case the profiler_is_active() check is just an optimization -- it prevents
 // us calling CreateExpensiveData() unnecessarily in most cases, but the
 // expensive data will end up being created but not used if another thread
 // stops the profiler between the CreateExpensiveData() and PROFILER_OPERATION
 // calls.
 //
 PROFILER_FUNC(bool profiler_is_active(), false)
 
-// Check if a profiler feature is active. Returns false if the profiler is
-// inactive.
-//
-// Supported features: "displaylistdump", "gpu", "layersdump", "restyle".
-PROFILER_FUNC(bool profiler_feature_active(const char*), false)
+// Check if a profiler feature (specified via the ProfilerFeature type) is
+// active. Returns false if the profiler is inactive.
+PROFILER_FUNC(bool profiler_feature_active(uint32_t aFeature), false)
 
 // Get the profile encoded as a JSON string. A no-op (returning nullptr) if the
 // profiler is inactive.
 PROFILER_FUNC(mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0),
               nullptr)
 
 // Write the profile for this process (excluding subprocesses) into aWriter.
 // Returns false if the profiler is inactive.
 PROFILER_FUNC(bool profiler_stream_json_for_this_process(SpliceableJSONWriter& aWriter,
                                                          double aSinceTime = 0),
               false)
 
-// Get the params used to start the profiler. Returns 0 and empty vectors (via
-// outparams) if the profile is inactive.
+// Get the params used to start the profiler. Returns 0 and an empty vector
+// (via outparams) if the profile is inactive. It's possible that the features
+// returned may be slightly different to those requested due to requied
+// adjustments.
 PROFILER_FUNC_VOID(profiler_get_start_params(int* aEntrySize,
                                              double* aInterval,
-                                             mozilla::Vector<const char*>* aFeatures,
+                                             uint32_t* aFeatures,
                                              mozilla::Vector<const char*>* aFilters))
 
 // Get the profile and write it into a file. A no-op if the profile is
 // inactive.
 //
 // This function is 'extern "C"' so that it is easily callable from a debugger
 // in a build without debugging information (a workaround for
 // http://llvm.org/bugs/show_bug.cgi?id=22211).
 extern "C" {
 PROFILER_FUNC_VOID(profiler_save_profile_to_file(const char* aFilename))
 }
 
 // Get all the features supported by the profiler that are accepted by
-// profiler_start(). Returns a null terminated char* array. The result is the
-// same whether the profiler is active or not.
-PROFILER_FUNC(const char** profiler_get_available_features(), nullptr)
+// profiler_start(). The result is the same whether the profiler is active or
+// not.
+PROFILER_FUNC(uint32_t profiler_get_available_features(), 0)
 
 // Get information about the current buffer status. A no-op when the profiler
 // is inactive. Do not call this function; call profiler_get_buffer_info()
 // instead.
 PROFILER_FUNC_VOID(profiler_get_buffer_info_helper(uint32_t* aCurrentPosition,
                                                    uint32_t* aEntries,
                                                    uint32_t* aGeneration))
 
--- a/tools/profiler/tests/gtest/GeckoProfiler.cpp
+++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp
@@ -28,154 +28,137 @@ using namespace mozilla;
 
 typedef Vector<const char*> StrVec;
 
 void
 InactiveFeaturesAndParamsCheck()
 {
   int entries;
   double interval;
-  StrVec features;
+  uint32_t features;
   StrVec filters;
 
   ASSERT_TRUE(!profiler_is_active());
-  ASSERT_TRUE(!profiler_feature_active("gpu"));
-  ASSERT_TRUE(!profiler_feature_active("privacy"));
-  ASSERT_TRUE(!profiler_feature_active("restyle"));
+  ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::GPU));
+  ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Privacy));
+  ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Restyle));
 
   profiler_get_start_params(&entries, &interval, &features, &filters);
 
   ASSERT_TRUE(entries == 0);
   ASSERT_TRUE(interval == 0);
-  ASSERT_TRUE(features.empty());
+  ASSERT_TRUE(features == 0);
   ASSERT_TRUE(filters.empty());
 }
 
 void
-ActiveParamsCheck(int aEntries, double aInterval,
-                  const char** aFeatures, size_t aFeaturesLen,
+ActiveParamsCheck(int aEntries, double aInterval, uint32_t aFeatures,
                   const char** aFilters, size_t aFiltersLen)
 {
   int entries;
   double interval;
-  StrVec features;
+  uint32_t features;
   StrVec filters;
 
   profiler_get_start_params(&entries, &interval, &features, &filters);
 
   ASSERT_TRUE(entries == aEntries);
   ASSERT_TRUE(interval == aInterval);
-  ASSERT_TRUE(features.length() == aFeaturesLen);
-  for (size_t i = 0; i < aFeaturesLen; i++) {
-    ASSERT_TRUE(strcmp(features[i], aFeatures[i]) == 0);
-  }
+  ASSERT_TRUE(features == aFeatures);
   ASSERT_TRUE(filters.length() == aFiltersLen);
   for (size_t i = 0; i < aFiltersLen; i++) {
     ASSERT_TRUE(strcmp(filters[i], aFilters[i]) == 0);
   }
 }
 
 TEST(GeckoProfiler, FeaturesAndParams)
 {
   InactiveFeaturesAndParamsCheck();
 
   // Try a couple of features and filters.
   {
-    const char* features[] = { "js", "threads" };
+    uint32_t features = ProfilerFeature::JS | ProfilerFeature::Threads;
     const char* filters[] = { "GeckoMain", "Compositor" };
 
     profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                   features, MOZ_ARRAY_LENGTH(features),
-                   filters, MOZ_ARRAY_LENGTH(filters));
+                   features, filters, MOZ_ARRAY_LENGTH(filters));
 
     ASSERT_TRUE(profiler_is_active());
-    ASSERT_TRUE(!profiler_feature_active("gpu"));
-    ASSERT_TRUE(!profiler_feature_active("privacy"));
-    ASSERT_TRUE(!profiler_feature_active("restyle"));
+    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::GPU));
+    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Privacy));
+    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Restyle));
 
     ActiveParamsCheck(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                      features, MOZ_ARRAY_LENGTH(features),
-                      filters, MOZ_ARRAY_LENGTH(filters));
+                      features, filters, MOZ_ARRAY_LENGTH(filters));
 
     profiler_stop();
 
     InactiveFeaturesAndParamsCheck();
   }
 
   // Try some different features and filters.
   {
-    const char* features[] = { "gpu", "privacy", "no-such-feature" };
+    uint32_t features = ProfilerFeature::GPU | ProfilerFeature::Privacy;
     const char* filters[] = { "GeckoMain", "Foo", "Bar" };
 
     profiler_start(999999, 3,
-                   features, MOZ_ARRAY_LENGTH(features),
-                   filters, MOZ_ARRAY_LENGTH(filters));
+                   features, filters, MOZ_ARRAY_LENGTH(filters));
 
-    // Nb: we did specify "privacy", but profiler_feature_active() doesn't
-    // support querying "privacy".
     ASSERT_TRUE(profiler_is_active());
-    ASSERT_TRUE(profiler_feature_active("gpu"));
-    ASSERT_TRUE(!profiler_feature_active("privacy"));
-    ASSERT_TRUE(!profiler_feature_active("restyle"));
+    ASSERT_TRUE(profiler_feature_active(ProfilerFeature::GPU));
+    ASSERT_TRUE(profiler_feature_active(ProfilerFeature::Privacy));
+    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Restyle));
 
+    // Profiler::Threads is added because filters has multiple entries.
     ActiveParamsCheck(999999, 3,
-                      features, MOZ_ARRAY_LENGTH(features),
+                      features | ProfilerFeature::Threads,
                       filters, MOZ_ARRAY_LENGTH(filters));
 
     profiler_stop();
 
     InactiveFeaturesAndParamsCheck();
   }
 
   // Try all supported features, and filters that match all threads.
   {
-    const char** availableFeatures = profiler_get_available_features();
-    int n = 0;
-    while (availableFeatures[n]) {
-      n++;
-    }
-    // There are 11 features supported on all platforms, and 2 features
-    // supported on some platforms.
-    ASSERT_TRUE(11 <= n && n <= 13);
-
+    uint32_t availableFeatures = profiler_get_available_features();
     const char* filters[] = { "" };
 
     profiler_start(88888, 10,
-                   availableFeatures, n, filters, MOZ_ARRAY_LENGTH(filters));
+                   availableFeatures, filters, MOZ_ARRAY_LENGTH(filters));
 
     ASSERT_TRUE(profiler_is_active());
-    ASSERT_TRUE(profiler_feature_active("gpu"));
-    ASSERT_TRUE(!profiler_feature_active("privacy"));
-    ASSERT_TRUE(profiler_feature_active("restyle"));
+    ASSERT_TRUE(profiler_feature_active(ProfilerFeature::GPU));
+    ASSERT_TRUE(profiler_feature_active(ProfilerFeature::Privacy));
+    ASSERT_TRUE(profiler_feature_active(ProfilerFeature::Restyle));
 
     ActiveParamsCheck(88888, 10,
-                      availableFeatures, n, filters, MOZ_ARRAY_LENGTH(filters));
+                      availableFeatures, filters, MOZ_ARRAY_LENGTH(filters));
 
     // Don't call profiler_stop() here.
   }
 
   // Try no features, and filters that match no threads.
   {
-    const char* features[] = { "" };
+    uint32_t features = 0;
     const char* filters[] = { "NoThreadWillMatchThis" };
 
     // Second profiler_start() call in a row without an intervening
     // profiler_stop(); this will do an implicit profiler_stop() and restart.
     profiler_start(0, 0,
-                   features, 0,
-                   filters, MOZ_ARRAY_LENGTH(filters));
+                   features, filters, MOZ_ARRAY_LENGTH(filters));
 
     ASSERT_TRUE(profiler_is_active());
-    ASSERT_TRUE(!profiler_feature_active("gpu"));
-    ASSERT_TRUE(!profiler_feature_active("privacy"));
-    ASSERT_TRUE(!profiler_feature_active("restyle"));
+    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::GPU));
+    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Privacy));
+    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Restyle));
 
     // Entries and intervals go to defaults if 0 is specified.
     ActiveParamsCheck(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                      features, 0,
+                      features | ProfilerFeature::Threads,
                       filters, MOZ_ARRAY_LENGTH(filters));
 
     profiler_stop();
 
     InactiveFeaturesAndParamsCheck();
 
     // These calls are no-ops.
     profiler_stop();
@@ -185,22 +168,21 @@ TEST(GeckoProfiler, FeaturesAndParams)
   }
 }
 
 TEST(GeckoProfiler, GetBacktrace)
 {
   ASSERT_TRUE(!profiler_get_backtrace());
 
   {
-    const char* features[] = { "stackwalk" };
+    uint32_t features = ProfilerFeature::StackWalk;
     const char* filters[] = { "GeckoMain" };
 
     profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                   features, MOZ_ARRAY_LENGTH(features),
-                   filters, MOZ_ARRAY_LENGTH(filters));
+                   features, filters, MOZ_ARRAY_LENGTH(filters));
 
     // These will be destroyed while the profiler is active.
     static const int N = 100;
     {
       UniqueProfilerBacktrace u[N];
       for (int i = 0; i < N; i++) {
         u[i] = profiler_get_backtrace();
         ASSERT_TRUE(u[i]);
@@ -213,42 +195,40 @@ TEST(GeckoProfiler, GetBacktrace)
       u[i] = profiler_get_backtrace();
       ASSERT_TRUE(u[i]);
     }
 
     profiler_stop();
   }
 
   {
-    const char* features[] = { "privacy" };
+    uint32_t features = ProfilerFeature::Privacy;
     const char* filters[] = { "GeckoMain" };
 
     profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                   features, MOZ_ARRAY_LENGTH(features),
-                   filters, MOZ_ARRAY_LENGTH(filters));
+                   features, filters, MOZ_ARRAY_LENGTH(filters));
 
-    // No backtraces obtained when the "privacy" feature is set.
+    // No backtraces obtained when ProfilerFeature::Privacy is set.
     ASSERT_TRUE(!profiler_get_backtrace());
 
     profiler_stop();
   }
 
   ASSERT_TRUE(!profiler_get_backtrace());
 }
 
 TEST(GeckoProfiler, Pause)
 {
-  const char* features[] = { "stackwalk" };
+  uint32_t features = ProfilerFeature::StackWalk;
   const char* filters[] = { "GeckoMain" };
 
   ASSERT_TRUE(!profiler_is_paused());
 
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                 features, MOZ_ARRAY_LENGTH(features),
-                 filters, MOZ_ARRAY_LENGTH(filters));
+                 features, filters, MOZ_ARRAY_LENGTH(filters));
 
   ASSERT_TRUE(!profiler_is_paused());
 
   uint32_t currPos1, entries1, generation1;
   uint32_t currPos2, entries2, generation2;
 
   // Check that we are writing samples while not paused.
   profiler_get_buffer_info(&currPos1, &entries1, &generation1);
@@ -272,22 +252,21 @@ TEST(GeckoProfiler, Pause)
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_is_paused());
 }
 
 TEST(GeckoProfiler, Markers)
 {
-  const char* features[] = { "stackwalk" };
+  uint32_t features = ProfilerFeature::StackWalk;
   const char* filters[] = { "GeckoMain" };
 
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                 features, MOZ_ARRAY_LENGTH(features),
-                 filters, MOZ_ARRAY_LENGTH(filters));
+                 features, filters, MOZ_ARRAY_LENGTH(filters));
 
   profiler_tracing("A", "B", TRACING_EVENT);
   profiler_tracing("A", "C", TRACING_INTERVAL_START);
   profiler_tracing("A", "C", TRACING_INTERVAL_END);
 
   UniqueProfilerBacktrace bt = profiler_get_backtrace();
   profiler_tracing("B", "A", Move(bt), TRACING_EVENT);
 
@@ -305,98 +284,94 @@ TEST(GeckoProfiler, Markers)
     "M4", new ProfilerMarkerTracing("C", TRACING_EVENT,
                                     profiler_get_backtrace()));
 
   profiler_stop();
 }
 
 TEST(GeckoProfiler, Time)
 {
-  const char* features[] = { "stackwalk" };
+  uint32_t features = ProfilerFeature::StackWalk;
   const char* filters[] = { "GeckoMain" };
 
   double t1 = profiler_time();
   double t2 = profiler_time();
   ASSERT_TRUE(t1 <= t2);
 
   // profiler_start() restarts the timer used by profiler_time().
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                 features, MOZ_ARRAY_LENGTH(features),
-                 filters, MOZ_ARRAY_LENGTH(filters));
+                 features, filters, MOZ_ARRAY_LENGTH(filters));
 
   double t3 = profiler_time();
   double t4 = profiler_time();
   ASSERT_TRUE(t3 <= t4);
 
   profiler_stop();
 
   double t5 = profiler_time();
   double t6 = profiler_time();
   ASSERT_TRUE(t4 <= t5 && t1 <= t6);
 }
 
 TEST(GeckoProfiler, GetProfile)
 {
-  const char* features[] = { "stackwalk" };
+  uint32_t features = ProfilerFeature::StackWalk;
   const char* filters[] = { "GeckoMain" };
 
   ASSERT_TRUE(!profiler_get_profile());
 
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                 features, MOZ_ARRAY_LENGTH(features),
-                 filters, MOZ_ARRAY_LENGTH(filters));
+                 features, filters, MOZ_ARRAY_LENGTH(filters));
 
   UniquePtr<char[]> profile = profiler_get_profile();
   ASSERT_TRUE(profile && profile[0] == '{');
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_get_profile());
 }
 
 TEST(GeckoProfiler, StreamJSONForThisProcess)
 {
-  const char* features[] = { "stackwalk" };
+  uint32_t features = ProfilerFeature::StackWalk;
   const char* filters[] = { "GeckoMain" };
 
   SpliceableChunkedJSONWriter w;
   ASSERT_TRUE(!profiler_stream_json_for_this_process(w));
 
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                 features, MOZ_ARRAY_LENGTH(features),
-                 filters, MOZ_ARRAY_LENGTH(filters));
+                 features, filters, MOZ_ARRAY_LENGTH(filters));
 
   w.Start(SpliceableJSONWriter::SingleLineStyle);
   ASSERT_TRUE(profiler_stream_json_for_this_process(w));
   w.End();
 
   UniquePtr<char[]> profile = w.WriteFunc()->CopyData();
   ASSERT_TRUE(profile && profile[0] == '{');
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_stream_json_for_this_process(w));
 }
 
 TEST(GeckoProfiler, PseudoStack)
 {
-  const char* features[] = { "stackwalk" };
+  uint32_t features = ProfilerFeature::StackWalk;
   const char* filters[] = { "GeckoMain" };
 
   PROFILER_LABEL("A", "B", js::ProfileEntry::Category::OTHER);
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
 
   UniqueFreePtr<char> dynamic(strdup("dynamic"));
   {
     PROFILER_LABEL_DYNAMIC("A", "C", js::ProfileEntry::Category::JS,
                            dynamic.get());
 
     profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                   features, MOZ_ARRAY_LENGTH(features),
-                   filters, MOZ_ARRAY_LENGTH(filters));
+                   features, filters, MOZ_ARRAY_LENGTH(filters));
 
     ASSERT_TRUE(profiler_get_backtrace());
   }
 
 #if defined(MOZ_GECKO_PROFILER)
   SamplerStackFrameRAII raii1("A", js::ProfileEntry::Category::STORAGE, 888);
   SamplerStackFrameDynamicRAII raii2("A", js::ProfileEntry::Category::STORAGE,
                                      888, dynamic.get());
@@ -410,28 +385,27 @@ TEST(GeckoProfiler, PseudoStack)
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_get_profile());
 }
 
 TEST(GeckoProfiler, Bug1355807)
 {
-  const char* features[] = { "js" };
+  uint32_t features = ProfilerFeature::JS;
   const char* manyThreadsFilter[] = { "" };
   const char* fewThreadsFilter[] = { "GeckoMain" };
 
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                 features, MOZ_ARRAY_LENGTH(features),
+                 features,
                  manyThreadsFilter, MOZ_ARRAY_LENGTH(manyThreadsFilter));
 
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                 features, MOZ_ARRAY_LENGTH(features),
+                 features,
                  fewThreadsFilter, MOZ_ARRAY_LENGTH(fewThreadsFilter));
 
   // In bug 1355807 this caused an assertion failure in StopJSSampling().
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
-                 features, MOZ_ARRAY_LENGTH(features),
+                 features,
                  fewThreadsFilter, MOZ_ARRAY_LENGTH(fewThreadsFilter));
 
   profiler_stop();
 }
-
--- a/widget/android/ANRReporter.cpp
+++ b/widget/android/ANRReporter.cpp
@@ -12,39 +12,30 @@ namespace mozilla {
 
 bool
 ANRReporter::RequestNativeStack(bool aUnwind)
 {
     if (profiler_is_active()) {
         // Don't proceed if profiler is already running
         return false;
     }
+
     // WARNING: we are on the ANR reporter thread at this point and it is
     // generally unsafe to use the profiler from off the main thread. However,
     // the risk here is limited because for most users, the profiler is not run
     // elsewhere. See the discussion in Bug 863777, comment 13
-    const char *NATIVE_STACK_FEATURES[] =
-        {"leaf", "threads", "privacy"};
-    const char *NATIVE_STACK_UNWIND_FEATURES[] =
-        {"leaf", "threads", "privacy", "stackwalk"};
+    uint32_t features = ProfilerFeature::Leaf |
+                        ProfilerFeature::Privacy |
+                        (aUnwind ? ProfilerFeature::StackWalk : 0) |
+                        ProfilerFeature::Threads;
 
-    const char **features = NATIVE_STACK_FEATURES;
-    size_t features_size = sizeof(NATIVE_STACK_FEATURES);
-    if (aUnwind) {
-        features = NATIVE_STACK_UNWIND_FEATURES;
-        features_size = sizeof(NATIVE_STACK_UNWIND_FEATURES);
-        // We want the new unwinder if the unwind mode has not been set yet
-        putenv("MOZ_PROFILER_NEW=1");
-    }
+    const char *NATIVE_STACK_THREADS[] = {"GeckoMain", "Compositor"};
 
-    const char *NATIVE_STACK_THREADS[] =
-        {"GeckoMain", "Compositor"};
     // Buffer one sample and let the profiler wait a long time
-    profiler_start(/* entries */ 100, /* interval */ 10000,
-                   features, features_size / sizeof(char*),
+    profiler_start(/* entries */ 100, /* interval */ 10000, features,
                    NATIVE_STACK_THREADS,
                    sizeof(NATIVE_STACK_THREADS) / sizeof(char*));
     return true;
 }
 
 jni::String::LocalRef
 ANRReporter::GetNativeStack()
 {