Bug 1172186 - Make the profiler build standalone. r=mstange
authorBenoit Girard <b56girard@gmail.com>
Thu, 18 Jun 2015 01:05:42 -0400
changeset 280454 d1d45ce7cbf53564f5549cf8cf8ce0eca2976cfd
parent 280453 d8c58dd8dabc156c3948a532d4bba47ccacb9977
child 280455 54ad73e3a027524c74c2deff9bda18b65a9a536b
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1172186
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1172186 - Make the profiler build standalone. r=mstange
js/public/ProfilingFrameIterator.h
js/public/ProfilingStack.h
tools/profiler/GeckoProfiler.h
tools/profiler/GeckoProfilerFunc.h
tools/profiler/GeckoProfilerImpl.h
tools/profiler/ProfileEntry.cpp
tools/profiler/ProfileEntry.h
tools/profiler/ProfileJSONWriter.cpp
tools/profiler/ProfilerBacktrace.cpp
tools/profiler/ProfilerMarkers.cpp
tools/profiler/ProfilerMarkers.h
tools/profiler/PseudoStack.h
tools/profiler/SyncProfile.cpp
tools/profiler/TableTicker.cpp
tools/profiler/TableTicker.h
tools/profiler/nsProfiler.cpp
tools/profiler/platform-linux.cc
tools/profiler/platform-macos.cc
tools/profiler/platform-win32.cc
tools/profiler/platform.cpp
tools/profiler/platform.h
tools/profiler/shared-libraries.h
tools/profiler/tests/gtest/ThreadProfileTest.cpp
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -6,16 +6,17 @@
 
 #ifndef js_ProfilingFrameIterator_h
 #define js_ProfilingFrameIterator_h
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Maybe.h"
 
 #include "jsbytecode.h"
+#include "js/TypeDecls.h"
 #include "js/Utility.h"
 
 struct JSRuntime;
 class JSScript;
 
 namespace js {
     class Activation;
     class AsmJSProfilingFrameIterator;
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -4,16 +4,17 @@
  * 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 js_ProfilingStack_h
 #define js_ProfilingStack_h
 
 #include "jsbytecode.h"
 #include "jstypes.h"
+#include "js/TypeDecls.h"
 
 #include "js/Utility.h"
 
 struct JSRuntime;
 
 namespace js {
 
 // A call stack can be specified to the JS engine such that all JS entry/exits
--- a/tools/profiler/GeckoProfiler.h
+++ b/tools/profiler/GeckoProfiler.h
@@ -44,17 +44,19 @@
  *          application is responding to the event loop. Lower is better.
  * 't' - Elapse time since recording started.
  *
  */
 
 #ifndef SAMPLER_H
 #define SAMPLER_H
 
+#ifndef SPS_STANDALONE
 #include "js/TypeDecls.h"
+#endif
 #include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 class TimeStamp;
 
 namespace dom {
 class Promise;
 } // namespace dom
@@ -170,19 +172,21 @@ static inline mozilla::UniquePtr<char[]>
 }
 
 // Get the profile encoded as a JSON object.
 static inline JSObject* profiler_get_profile_jsobject(JSContext* aCx,
                                                       double aSinceTime = 0) {
   return nullptr;
 }
 
+#ifndef SPS_STANDALONE
 // Get the profile encoded as a JSON object.
 static inline void profiler_get_profile_jsobject_async(double aSinceTime = 0,
                                                        mozilla::dom::Promise* = 0) {}
+#endif
 
 // Get the profile and write it into a file
 static inline void profiler_save_profile_to_file(char* aFilename) { }
 
 // Get the features supported by the profiler that are accepted by profiler_init.
 // Returns a null terminated char* array.
 static inline char** profiler_get_features() { return nullptr; }
 
--- a/tools/profiler/GeckoProfilerFunc.h
+++ b/tools/profiler/GeckoProfilerFunc.h
@@ -1,17 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef PROFILER_FUNCS_H
 #define PROFILER_FUNCS_H
 
+#ifndef SPS_STANDALONE
 #include "js/TypeDecls.h"
+#endif
 #include "js/ProfilingStack.h"
 #include "mozilla/UniquePtr.h"
 #include <stdint.h>
 
 namespace mozilla {
 class TimeStamp;
 
 namespace dom {
@@ -56,19 +58,21 @@ void mozilla_sampler_responsiveness(cons
 void mozilla_sampler_frame_number(int frameNumber);
 
 const double* mozilla_sampler_get_responsiveness();
 
 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);
+#endif
 
 // Make this function easily callable from a debugger in a build without
 // debugging information (work around http://llvm.org/bugs/show_bug.cgi?id=22211)
 extern "C" {
   void mozilla_sampler_save_profile_to_file(const char* aFilename);
 }
 
 const char** mozilla_sampler_get_features();
--- a/tools/profiler/GeckoProfilerImpl.h
+++ b/tools/profiler/GeckoProfilerImpl.h
@@ -9,20 +9,22 @@
 
 #include <stdlib.h>
 #include <signal.h>
 #include <stdarg.h>
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/UniquePtr.h"
+#ifndef SPS_STANDALONE
 #include "nscore.h"
+#include "nsISupports.h"
+#endif
 #include "GeckoProfilerFunc.h"
 #include "PseudoStack.h"
-#include "nsISupports.h"
 #include "ProfilerBacktrace.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
 /* QT has a #define for the word "slots" and jsfriendapi.h has a struct with
  * this variable name, causing compilation problems. Alleviate this for now by
@@ -144,28 +146,30 @@ void profiler_set_frame_number(int frame
 }
 
 static inline
 mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0)
 {
   return mozilla_sampler_get_profile(aSinceTime);
 }
 
+#ifndef SPS_STANDALONE
 static inline
 JSObject* profiler_get_profile_jsobject(JSContext* aCx, double aSinceTime = 0)
 {
   return mozilla_sampler_get_profile_data(aCx, aSinceTime);
 }
 
 static inline
 void profiler_get_profile_jsobject_async(double aSinceTime = 0,
                                          mozilla::dom::Promise* aPromise = 0)
 {
   mozilla_sampler_get_profile_data_async(aSinceTime, aPromise);
 }
+#endif
 
 static inline
 void profiler_save_profile_to_file(const char* aFilename)
 {
   return mozilla_sampler_save_profile_to_file(aFilename);
 }
 
 static inline
@@ -212,26 +216,28 @@ void profiler_sleep_start()
 }
 
 static inline
 void profiler_sleep_end()
 {
   mozilla_sampler_sleep_end();
 }
 
+#ifndef SPS_STANDALONE
 static inline
 void profiler_js_operation_callback()
 {
   PseudoStack *stack = tlsPseudoStack.get();
   if (!stack) {
     return;
   }
 
   stack->jsOperationCallback();
 }
+#endif
 
 static inline
 double profiler_time()
 {
   return mozilla_sampler_time();
 }
 
 static inline
--- a/tools/profiler/ProfileEntry.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -1,23 +1,26 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <ostream>
 #include "platform.h"
+#include "mozilla/HashFunctions.h"
+
+#ifndef SPS_STANDALONE
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
-#include "mozilla/HashFunctions.h"
 
 // JS
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/TrackedOptimizationInfo.h"
+#endif
 
 // Self
 #include "ProfileEntry.h"
 
 #if defined(_MSC_VER) && _MSC_VER < 1900
  #define snprintf _snprintf
 #endif
 
@@ -200,16 +203,17 @@ public:
     mWriter.IntProperty(aName, mIndex++);
   }
 
   ~JSONSchemaWriter() {
     mWriter.EndObject();
   }
 };
 
+#ifndef SPS_STANDALONE
 class StreamOptimizationTypeInfoOp : public JS::ForEachTrackedOptimizationTypeInfoOp
 {
   JSONWriter& mWriter;
   UniqueJSONStrings& mUniqueStrings;
   bool mStartedTypeList;
 
 public:
   StreamOptimizationTypeInfoOp(JSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings)
@@ -301,25 +305,30 @@ public:
   }
 
   void operator()(const JS::ForEachProfiledFrameOp::FrameHandle& aFrameHandle) override {
     UniqueStacks::OnStackFrameKey frameKey(mReturnAddress, mDepth, aFrameHandle);
     mStack.AppendFrame(frameKey);
     mDepth++;
   }
 };
+#endif
 
 uint32_t UniqueJSONStrings::GetOrAddIndex(const char* aStr)
 {
   uint32_t index;
-  if (mStringToIndexMap.Get(aStr, &index)) {
-    return index;
+  StringKey key(aStr);
+
+  auto it = mStringToIndexMap.find(key);
+
+  if (it != mStringToIndexMap.end()) {
+    return it->second;
   }
-  index = mStringToIndexMap.Count();
-  mStringToIndexMap.Put(aStr, index);
+  index = mStringToIndexMap.size();
+  mStringToIndexMap[key] = index;
   mStringTableWriter.StringElement(aStr);
   return index;
 }
 
 bool UniqueStacks::FrameKey::operator==(const FrameKey& aOther) const
 {
   return mLocation == aOther.mLocation &&
          mLine == aOther.mLine &&
@@ -340,31 +349,29 @@ UniqueStacks::Stack::Stack(UniqueStacks&
 {
 }
 
 void UniqueStacks::Stack::AppendFrame(const OnStackFrameKey& aFrame)
 {
   // Compute the prefix hash and index before mutating mStack.
   uint32_t prefixHash = mStack.Hash();
   uint32_t prefix = mUniqueStacks.GetOrAddStackIndex(mStack);
-  mStack.mPrefixHash = Some(prefixHash);
-  mStack.mPrefix = Some(prefix);
-  mStack.mFrame = mUniqueStacks.GetOrAddFrameIndex(aFrame);
+  mStack.UpdateHash(prefixHash, prefix, mUniqueStacks.GetOrAddFrameIndex(aFrame));
 }
 
 uint32_t UniqueStacks::Stack::GetOrAddIndex() const
 {
   return mUniqueStacks.GetOrAddStackIndex(mStack);
 }
 
 uint32_t UniqueStacks::FrameKey::Hash() const
 {
   uint32_t hash = 0;
-  if (!mLocation.IsEmpty()) {
-    hash = mozilla::HashString(mLocation.get());
+  if (!mLocation.empty()) {
+    hash = mozilla::HashString(mLocation.c_str());
   }
   if (mLine.isSome()) {
     hash = mozilla::AddToHash(hash, *mLine);
   }
   if (mCategory.isSome()) {
     hash = mozilla::AddToHash(hash, *mCategory);
   }
   if (mJITAddress.isSome()) {
@@ -392,30 +399,65 @@ UniqueStacks::Stack UniqueStacks::BeginS
 UniqueStacks::UniqueStacks(JSRuntime* aRuntime)
  : mRuntime(aRuntime)
  , mFrameCount(0)
 {
   mFrameTableWriter.StartBareList();
   mStackTableWriter.StartBareList();
 }
 
+#ifdef SPS_STANDALONE
+uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack)
+{
+  uint32_t index;
+  auto it = mStackToIndexMap.find(aStack);
+
+  if (it != mStackToIndexMap.end()) {
+    return it->second;
+  }
+
+  index = mStackToIndexMap.size();
+  mStackToIndexMap[aStack] = index;
+  StreamStack(aStack);
+  return index;
+}
+#else
 uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack)
 {
   uint32_t index;
   if (mStackToIndexMap.Get(aStack, &index)) {
     MOZ_ASSERT(index < mStackToIndexMap.Count());
     return index;
   }
 
   index = mStackToIndexMap.Count();
   mStackToIndexMap.Put(aStack, index);
   StreamStack(aStack);
   return index;
 }
+#endif
 
+#ifdef SPS_STANDALONE
+uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame)
+{
+  uint32_t index;
+  auto it = mFrameToIndexMap.find(aFrame);
+  if (it != mFrameToIndexMap.end()) {
+    MOZ_ASSERT(it->second < mFrameCount);
+    return it->second;
+  }
+
+  // A manual count is used instead of mFrameToIndexMap.Count() due to
+  // forwarding of canonical JIT frames above.
+  index = mFrameCount++;
+  mFrameToIndexMap[aFrame] = index;
+  StreamFrame(aFrame);
+  return index;
+}
+#else
 uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame)
 {
   uint32_t index;
   if (mFrameToIndexMap.Get(aFrame, &index)) {
     MOZ_ASSERT(index < mFrameCount);
     return index;
   }
 
@@ -432,30 +474,34 @@ uint32_t UniqueStacks::GetOrAddFrameInde
 
   // A manual count is used instead of mFrameToIndexMap.Count() due to
   // forwarding of canonical JIT frames above.
   index = mFrameCount++;
   mFrameToIndexMap.Put(aFrame, index);
   StreamFrame(aFrame);
   return index;
 }
+#endif
 
 uint32_t UniqueStacks::LookupJITFrameDepth(void* aAddr)
 {
   uint32_t depth;
-  if (mJITFrameDepthMap.Get(aAddr, &depth)) {
+
+  auto it = mJITFrameDepthMap.find(aAddr);
+  if (it != mJITFrameDepthMap.end()) {
+    depth = it->second;
     MOZ_ASSERT(depth > 0);
     return depth;
   }
   return 0;
 }
 
 void UniqueStacks::AddJITFrameDepth(void* aAddr, unsigned depth)
 {
-  mJITFrameDepthMap.Put(aAddr, depth);
+  mJITFrameDepthMap[aAddr] = depth;
 }
 
 void UniqueStacks::SpliceFrameTableElements(SpliceableJSONWriter& aWriter)
 {
   mFrameTableWriter.EndBareList();
   aWriter.TakeAndSplice(mFrameTableWriter.WriteFunc());
 }
 
@@ -483,30 +529,36 @@ void UniqueStacks::StreamStack(const Sta
 }
 
 void UniqueStacks::StreamFrame(const OnStackFrameKey& aFrame)
 {
   // Schema:
   //   [location, implementation, optimizations, line, category]
 
   mFrameTableWriter.StartArrayElement();
+#ifndef SPS_STANDALONE
   if (!aFrame.mJITFrameHandle) {
-    mUniqueStrings.WriteElement(mFrameTableWriter, aFrame.mLocation.get());
+#else
+  {
+#endif
+    mUniqueStrings.WriteElement(mFrameTableWriter, aFrame.mLocation.c_str());
     if (aFrame.mLine.isSome()) {
       mFrameTableWriter.NullElement(); // implementation
       mFrameTableWriter.NullElement(); // optimizations
       mFrameTableWriter.IntElement(*aFrame.mLine);
     }
     if (aFrame.mCategory.isSome()) {
       if (aFrame.mLine.isNothing()) {
         mFrameTableWriter.NullElement(); // line
       }
       mFrameTableWriter.IntElement(*aFrame.mCategory);
     }
-  } else {
+  }
+#ifndef SPS_STANDALONE
+  else {
     const JS::ForEachProfiledFrameOp::FrameHandle& jitFrame = *aFrame.mJITFrameHandle;
 
     mUniqueStrings.WriteElement(mFrameTableWriter, jitFrame.label());
 
     JS::ProfilingFrameIterator::FrameKind frameKind = jitFrame.frameKind();
     MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion ||
                frameKind == JS::ProfilingFrameIterator::Frame_Baseline);
     mUniqueStrings.WriteElement(mFrameTableWriter,
@@ -552,16 +604,17 @@ void UniqueStacks::StreamFrame(const OnS
         unsigned line, column;
         line = JS_PCToLineNumber(script, pc, &column);
         mFrameTableWriter.IntProperty("line", line);
         mFrameTableWriter.IntProperty("column", column);
       }
       mFrameTableWriter.EndObject();
     }
   }
+#endif
   mFrameTableWriter.EndArray();
 }
 
 struct ProfileSample
 {
   uint32_t mStack;
   Maybe<double> mTime;
   Maybe<double> mResponsiveness;
@@ -738,30 +791,32 @@ void ProfileBuffer::StreamSamplesToJSON(
               }
               readAheadPos = (framePos + incBy) % mEntrySize;
               if (readAheadPos != mWritePos &&
                   mEntries[readAheadPos].mTagName == 'y') {
                 frameKey.mCategory = Some((unsigned) mEntries[readAheadPos].mTagInt);
                 incBy++;
               }
               stack.AppendFrame(frameKey);
+#ifndef SPS_STANDALONE
             } else if (frame.mTagName == 'J') {
               // A JIT frame may expand to multiple frames due to inlining.
               void* pc = frame.mTagPtr;
               unsigned depth = aUniqueStacks.LookupJITFrameDepth(pc);
               if (depth == 0) {
                 StreamJSFramesOp framesOp(pc, stack);
                 JS::ForEachProfiledFrame(aRuntime, pc, framesOp);
                 aUniqueStacks.AddJITFrameDepth(pc, framesOp.depth());
               } else {
                 for (unsigned i = 0; i < depth; i++) {
                   UniqueStacks::OnStackFrameKey inlineFrameKey(pc, i);
                   stack.AppendFrame(inlineFrameKey);
                 }
               }
+#endif
             }
             framePos = (framePos + incBy) % mEntrySize;
           }
 
           sample->mStack = stack.GetOrAddIndex();
           break;
         }
       }
@@ -849,22 +904,24 @@ void ProfileBuffer::DuplicateLastSample(
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN ThreadProfile
 
 ThreadProfile::ThreadProfile(ThreadInfo* aInfo, ProfileBuffer* aBuffer)
   : mThreadInfo(aInfo)
   , mBuffer(aBuffer)
   , mPseudoStack(aInfo->Stack())
-  , mMutex("ThreadProfile::mMutex")
+  , mMutex(OS::CreateMutex("ThreadProfile::mMutex"))
   , mThreadId(int(aInfo->ThreadId()))
   , mIsMainThread(aInfo->IsMainThread())
   , mPlatformData(aInfo->GetPlatformData())
   , mStackTop(aInfo->StackTop())
+#ifndef SPS_STANDALONE
   , mRespInfo(this)
+#endif
 #ifdef XP_LINUX
   , mRssMemory(0)
   , mUssMemory(0)
 #endif
 {
   MOZ_COUNT_CTOR(ThreadProfile);
   MOZ_ASSERT(aBuffer);
 
@@ -886,17 +943,21 @@ void ThreadProfile::addTag(const Profile
 void ThreadProfile::addStoredMarker(ProfilerMarker *aStoredMarker) {
   mBuffer->addStoredMarker(aStoredMarker);
 }
 
 void ThreadProfile::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
 {
   // mUniqueStacks may already be emplaced from FlushSamplesAndMarkers.
   if (!mUniqueStacks.isSome()) {
+#ifndef SPS_STANDALONE
     mUniqueStacks.emplace(mPseudoStack->mRuntime);
+#else
+    mUniqueStacks.emplace(nullptr);
+#endif
   }
 
   aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
   {
     StreamSamplesAndMarkers(aWriter, aSinceTime, *mUniqueStacks);
 
     aWriter.StartObjectProperty("stackTable");
     {
@@ -942,27 +1003,32 @@ void ThreadProfile::StreamJSON(Spliceabl
   aWriter.End();
 
   mUniqueStacks.reset();
 }
 
 void ThreadProfile::StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, double aSinceTime,
                                             UniqueStacks& aUniqueStacks)
 {
+#ifndef SPS_STANDALONE
   // Thread meta data
   if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
     // TODO Add the proper plugin name
     aWriter.StringProperty("name", "Plugin");
   } else if (XRE_GetProcessType() == GeckoProcessType_Content) {
     // This isn't going to really help once we have multiple content
     // processes, but it'll do for now.
     aWriter.StringProperty("name", "Content");
   } else {
     aWriter.StringProperty("name", Name());
   }
+#else
+  aWriter.StringProperty("name", Name());
+#endif
+
   aWriter.IntProperty("tid", static_cast<int>(mThreadId));
 
   aWriter.StartObjectProperty("samples");
   {
     {
       JSONSchemaWriter schema(aWriter);
       schema.WriteField("stack");
       schema.WriteField("time");
@@ -979,17 +1045,22 @@ void ThreadProfile::StreamSamplesAndMark
         // We would only have saved streamed samples during shutdown
         // streaming, which cares about dumping the entire buffer, and thus
         // should have passed in 0 for aSinceTime.
         MOZ_ASSERT(aSinceTime == 0);
         aWriter.Splice(mSavedStreamedSamples.get());
         mSavedStreamedSamples.reset();
       }
       mBuffer->StreamSamplesToJSON(aWriter, mThreadId, aSinceTime,
-                                   mPseudoStack->mRuntime, aUniqueStacks);
+#ifndef SPS_STANDALONE
+                                   mPseudoStack->mRuntime,
+#else
+                                   nullptr,
+#endif
+                                   aUniqueStacks);
     }
     aWriter.EndArray();
   }
   aWriter.EndObject();
 
   aWriter.StartObjectProperty("markers");
   {
     {
@@ -1021,24 +1092,33 @@ void ThreadProfile::FlushSamplesAndMarke
 
   // Unlike StreamJSObject, do not surround the samples in brackets by calling
   // aWriter.{Start,End}BareList. The result string will be a comma-separated
   // list of JSON object literals that will prepended by StreamJSObject into
   // an existing array.
   //
   // Note that the UniqueStacks instance is persisted so that the frame-index
   // mapping is stable across JS shutdown.
+#ifndef SPS_STANDALONE
   mUniqueStacks.emplace(mPseudoStack->mRuntime);
+#else
+  mUniqueStacks.emplace(nullptr);
+#endif
 
   {
     SpliceableChunkedJSONWriter b;
     b.StartBareList();
     {
       mBuffer->StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
-                                   mPseudoStack->mRuntime, *mUniqueStacks);
+#ifndef SPS_STANDALONE
+                                   mPseudoStack->mRuntime,
+#else
+                                   nullptr,
+#endif
+                                   *mUniqueStacks);
     }
     b.EndBareList();
     mSavedStreamedSamples = b.WriteFunc()->CopyData();
   }
 
   {
     SpliceableChunkedJSONWriter b;
     b.StartBareList();
@@ -1056,27 +1136,27 @@ void ThreadProfile::FlushSamplesAndMarke
 
 PseudoStack* ThreadProfile::GetPseudoStack()
 {
   return mPseudoStack;
 }
 
 void ThreadProfile::BeginUnwind()
 {
-  mMutex.Lock();
+  mMutex->Lock();
 }
 
 void ThreadProfile::EndUnwind()
 {
-  mMutex.Unlock();
+  mMutex->Unlock();
 }
 
-mozilla::Mutex* ThreadProfile::GetMutex()
+Mutex& ThreadProfile::GetMutex()
 {
-  return &mMutex;
+  return *mMutex.get();
 }
 
 void ThreadProfile::DuplicateLastSample()
 {
   mBuffer->DuplicateLastSample(mThreadId);
 }
 
 // END ThreadProfile
--- a/tools/profiler/ProfileEntry.h
+++ b/tools/profiler/ProfileEntry.h
@@ -7,33 +7,38 @@
 #ifndef MOZ_PROFILE_ENTRY_H
 #define MOZ_PROFILE_ENTRY_H
 
 #include <ostream>
 #include "GeckoProfiler.h"
 #include "platform.h"
 #include "ProfileJSONWriter.h"
 #include "ProfilerBacktrace.h"
-#include "nsRefPtr.h"
-#include "nsHashKeys.h"
-#include "nsDataHashtable.h"
+#include "mozilla/RefPtr.h"
+#include <string>
+#include <map>
+#ifndef SPS_STANDALONE
 #include "js/ProfilingFrameIterator.h"
 #include "js/TrackedOptimizationInfo.h"
+#include "nsHashKeys.h"
+#include "nsDataHashtable.h"
+#endif
 #include "mozilla/Maybe.h"
-#include "mozilla/Mutex.h"
 #include "mozilla/Vector.h"
+#ifndef SPS_STANDALONE
 #include "gtest/MozGtestFriend.h"
+#else
+#define FRIEND_TEST(a, b) // TODO Support standalone gtest
+#endif
+#include "mozilla/HashFunctions.h"
 #include "mozilla/UniquePtr.h"
 
 class ThreadProfile;
 
-// NB: Packing this structure has been shown to cause SIGBUS issues on ARM.
-#ifndef __arm__
 #pragma pack(push, 1)
-#endif
 
 class ProfileEntry
 {
 public:
   ProfileEntry();
 
   // aTagData must not need release (i.e. be a string from the text segment)
   ProfileEntry(char aTagName, const char *aTagData);
@@ -71,19 +76,17 @@ private:
     Address     mTagAddress;
     uintptr_t   mTagOffset;
     int         mTagInt;
     char        mTagChar;
   };
   char mTagName;
 };
 
-#ifndef __arm__
 #pragma pack(pop)
-#endif
 
 class UniqueJSONStrings
 {
 public:
   UniqueJSONStrings() {
     mStringTableWriter.StartBareList();
   }
 
@@ -96,89 +99,152 @@ public:
   }
 
   void WriteElement(mozilla::JSONWriter& aWriter, const char* aStr) {
     aWriter.IntElement(GetOrAddIndex(aStr));
   }
 
   uint32_t GetOrAddIndex(const char* aStr);
 
+  struct StringKey {
+
+    explicit StringKey(const char* aStr)
+     : mStr(strdup(aStr))
+    {
+      mHash = mozilla::HashString(mStr);
+    }
+
+    StringKey(const StringKey& aOther)
+      : mStr(strdup(aOther.mStr))
+    {
+      mHash = aOther.mHash;
+    }
+
+    ~StringKey() {
+      free(mStr);
+    }
+
+    uint32_t Hash() const;
+    bool operator==(const StringKey& aOther) const {
+      return strcmp(mStr, aOther.mStr) == 0;
+    }
+    bool operator<(const StringKey& aOther) const {
+      return mHash < aOther.mHash;
+    }
+
+  private:
+    uint32_t mHash;
+    char* mStr;
+  };
 private:
   SpliceableChunkedJSONWriter mStringTableWriter;
-  nsDataHashtable<nsCharPtrHashKey, uint32_t> mStringToIndexMap;
+  std::map<StringKey, uint32_t> mStringToIndexMap;
 };
 
 class UniqueStacks
 {
 public:
   struct FrameKey {
-    nsCString mLocation;
+    std::string mLocation;
     mozilla::Maybe<unsigned> mLine;
     mozilla::Maybe<unsigned> mCategory;
     mozilla::Maybe<void*> mJITAddress;
     mozilla::Maybe<uint32_t> mJITDepth;
 
     explicit FrameKey(const char* aLocation)
      : mLocation(aLocation)
-    { }
+    {
+      mHash = Hash();
+    }
 
     FrameKey(const FrameKey& aToCopy)
      : mLocation(aToCopy.mLocation)
      , mLine(aToCopy.mLine)
      , mCategory(aToCopy.mCategory)
      , mJITAddress(aToCopy.mJITAddress)
      , mJITDepth(aToCopy.mJITDepth)
-    { }
+    {
+      mHash = Hash();
+    }
 
     FrameKey(void* aJITAddress, uint32_t aJITDepth)
      : mJITAddress(mozilla::Some(aJITAddress))
      , mJITDepth(mozilla::Some(aJITDepth))
-    { }
+    {
+      mHash = Hash();
+    }
 
     uint32_t Hash() const;
     bool operator==(const FrameKey& aOther) const;
+    bool operator<(const FrameKey& aOther) const {
+      return mHash < aOther.mHash;
+    }
+
+  private:
+    uint32_t mHash;
   };
 
   // A FrameKey that holds a scoped reference to a JIT FrameHandle.
   struct MOZ_STACK_CLASS OnStackFrameKey : public FrameKey {
-    const JS::ForEachProfiledFrameOp::FrameHandle* mJITFrameHandle;
-
     explicit OnStackFrameKey(const char* aLocation)
       : FrameKey(aLocation)
+#ifndef SPS_STANDALONE
       , mJITFrameHandle(nullptr)
+#endif
     { }
 
     OnStackFrameKey(const OnStackFrameKey& aToCopy)
       : FrameKey(aToCopy)
+#ifndef SPS_STANDALONE
       , mJITFrameHandle(aToCopy.mJITFrameHandle)
+#endif
     { }
 
+#ifndef SPS_STANDALONE
+    const JS::ForEachProfiledFrameOp::FrameHandle* mJITFrameHandle;
+
     OnStackFrameKey(void* aJITAddress, unsigned aJITDepth)
       : FrameKey(aJITAddress, aJITDepth)
       , mJITFrameHandle(nullptr)
     { }
 
     OnStackFrameKey(void* aJITAddress, unsigned aJITDepth,
                     const JS::ForEachProfiledFrameOp::FrameHandle& aJITFrameHandle)
       : FrameKey(aJITAddress, aJITDepth)
       , mJITFrameHandle(&aJITFrameHandle)
     { }
+#endif
   };
 
   struct StackKey {
     mozilla::Maybe<uint32_t> mPrefixHash;
     mozilla::Maybe<uint32_t> mPrefix;
     uint32_t mFrame;
 
     explicit StackKey(uint32_t aFrame)
      : mFrame(aFrame)
-    { }
+    {
+      mHash = Hash();
+    }
 
     uint32_t Hash() const;
     bool operator==(const StackKey& aOther) const;
+    bool operator<(const StackKey& aOther) const {
+      return mHash < aOther.mHash;
+    }
+
+    void UpdateHash(uint32_t aPrefixHash, uint32_t aPrefix, uint32_t aFrame) {
+      mPrefixHash = mozilla::Some(aPrefixHash);
+      mPrefix = mozilla::Some(aPrefix);
+      mFrame = aFrame;
+      mHash = Hash();
+    }
+
+  private:
+    uint32_t mHash;
   };
 
   class Stack {
   public:
     Stack(UniqueStacks& aUniqueStacks, const OnStackFrameKey& aRoot);
 
     void AppendFrame(const OnStackFrameKey& aFrame);
     uint32_t GetOrAddIndex() const;
@@ -207,32 +273,46 @@ public:
 
 private:
   JSRuntime* mRuntime;
 
   // To avoid incurring JitcodeGlobalTable lookup costs for every JIT frame,
   // we cache the depth of frames keyed by JIT code address. If an address a
   // maps to a depth d, then frames keyed by a for depths 0 to d are
   // guaranteed to be in mFrameToIndexMap.
-  nsDataHashtable<nsVoidPtrHashKey, uint32_t> mJITFrameDepthMap;
+  std::map<void*, uint32_t> mJITFrameDepthMap;
 
   uint32_t mFrameCount;
   SpliceableChunkedJSONWriter mFrameTableWriter;
+#ifdef SPS_STANDALNOE
+  std::map<FrameKey, uint32_t> mFrameToIndexMap;
+#else
   nsDataHashtable<nsGenericHashKey<FrameKey>, uint32_t> mFrameToIndexMap;
+#endif
 
   SpliceableChunkedJSONWriter mStackTableWriter;
+
+  // This sucks but this is really performance critical, nsDataHashtable is way faster
+  // than map/unordered_map but nsDataHashtable is tied to xpcom so we ifdef
+  // until we can find a better solution.
+#ifdef SPS_STANDALONE
+  std::map<StackKey, uint32_t> mStackToIndexMap;
+#else
   nsDataHashtable<nsGenericHashKey<StackKey>, uint32_t> mStackToIndexMap;
+#endif
 };
 
-class ProfileBuffer {
+class ProfileBuffer : public mozilla::RefCounted<ProfileBuffer> {
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProfileBuffer)
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ProfileBuffer)
 
   explicit ProfileBuffer(int aEntrySize);
 
+  virtual ~ProfileBuffer();
+
   void addTag(const ProfileEntry& aTag);
   void StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
                            JSRuntime* rt, UniqueStacks& aUniqueStacks);
   void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
                            UniqueStacks& aUniqueStacks);
   void DuplicateLastSample(int aThreadId);
 
   void addStoredMarker(ProfilerMarker* aStoredMarker);
@@ -240,18 +320,16 @@ public:
   // The following two methods are not signal safe! They delete markers.
   void deleteExpiredStoredMarkers();
   void reset();
 
 protected:
   char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff);
   int FindLastSampleOfThread(int aThreadId);
 
-  ~ProfileBuffer();
-
 public:
   // Circular buffer 'Keep One Slot Open' implementation for simplicity
   mozilla::UniquePtr<ProfileEntry[]> mEntries;
 
   // Points to the next entry we will write to, which is also the one at which
   // we need to stop reading.
   int mWritePos;
 
@@ -370,17 +448,17 @@ public:
 
   /**
    * Track a marker which has been inserted into the ThreadProfile.
    * This marker can safely be deleted once the generation has
    * expired.
    */
   void addStoredMarker(ProfilerMarker *aStoredMarker);
   PseudoStack* GetPseudoStack();
-  mozilla::Mutex* GetMutex();
+  Mutex& GetMutex();
   void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime = 0);
 
   /**
    * Call this method when the JS entries inside the buffer are about to
    * become invalid, i.e., just before JS shutdown.
    */
   void FlushSamplesAndMarkers();
 
@@ -392,17 +470,19 @@ public:
   const char* Name() const { return mThreadInfo->Name(); }
   int ThreadId() const { return mThreadId; }
 
   PlatformData* GetPlatformData() const { return mPlatformData; }
   void* GetStackTop() const { return mStackTop; }
   void DuplicateLastSample();
 
   ThreadInfo* GetThreadInfo() const { return mThreadInfo; }
+#ifndef SPS_STANDALONE
   ThreadResponsiveness* GetThreadResponsiveness() { return &mRespInfo; }
+#endif
   void SetPendingDelete()
   {
     mPseudoStack = nullptr;
     mPlatformData = nullptr;
   }
 
   uint32_t bufferGeneration() const {
     return mBuffer->mGeneration;
@@ -415,33 +495,35 @@ protected:
 private:
   FRIEND_TEST(ThreadProfile, InsertOneTag);
   FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
   FRIEND_TEST(ThreadProfile, InsertTagsNoWrap);
   FRIEND_TEST(ThreadProfile, InsertTagsWrap);
   FRIEND_TEST(ThreadProfile, MemoryMeasure);
   ThreadInfo* mThreadInfo;
 
-  const nsRefPtr<ProfileBuffer> mBuffer;
+  const mozilla::RefPtr<ProfileBuffer> mBuffer;
 
   // JS frames in the buffer may require a live JSRuntime to stream (e.g.,
   // stringifying JIT frames). In the case of JSRuntime destruction,
   // FlushSamplesAndMarkers should be called to save them. These are spliced
   // into the final stream.
   mozilla::UniquePtr<char[]> mSavedStreamedSamples;
   mozilla::UniquePtr<char[]> mSavedStreamedMarkers;
   mozilla::Maybe<UniqueStacks> mUniqueStacks;
 
   PseudoStack*   mPseudoStack;
-  mozilla::Mutex mMutex;
+  mozilla::UniquePtr<Mutex>  mMutex;
   int            mThreadId;
   bool           mIsMainThread;
   PlatformData*  mPlatformData;  // Platform specific data.
   void* const    mStackTop;
+#ifndef SPS_STANDALONE
   ThreadResponsiveness mRespInfo;
+#endif
 
   // Only Linux is using a signal sender, instead of stopping the thread, so we
   // need some space to store the data which cannot be collected in the signal
   // handler code.
 #ifdef XP_LINUX
 public:
   int64_t        mRssMemory;
   int64_t        mUssMemory;
--- a/tools/profiler/ProfileJSONWriter.cpp
+++ b/tools/profiler/ProfileJSONWriter.cpp
@@ -7,37 +7,35 @@
 
 #include "ProfileJSONWriter.h"
 
 void
 ChunkedJSONWriteFunc::Write(const char* aStr)
 {
   MOZ_ASSERT(mChunkPtr >= mChunkList.back().get() && mChunkPtr <= mChunkEnd);
   MOZ_ASSERT(mChunkEnd >= mChunkList.back().get() + mChunkLengths.back());
-  MOZ_ASSERT(*mChunkPtr == '\0');
 
   size_t len = strlen(aStr);
 
   // Most strings to be written are small, but subprocess profiles (e.g.,
   // from the content process in e10s) may be huge. If the string is larger
   // than a chunk, allocate its own chunk.
   char* newPtr;
   if (len >= kChunkSize) {
-    AllocChunk(len + 1);
+    AllocChunk(len);
     newPtr = mChunkPtr + len;
   } else {
     newPtr = mChunkPtr + len;
-    if (newPtr >= mChunkEnd) {
+    if (newPtr > mChunkEnd) {
       AllocChunk(kChunkSize);
       newPtr = mChunkPtr + len;
     }
   }
 
   memcpy(mChunkPtr, aStr, len);
-  *newPtr = '\0';
   mChunkPtr = newPtr;
   mChunkLengths.back() += len;
 }
 
 mozilla::UniquePtr<char[]>
 ChunkedJSONWriteFunc::CopyData() const
 {
   MOZ_ASSERT(mChunkLengths.length() == mChunkList.length());
@@ -74,17 +72,16 @@ ChunkedJSONWriteFunc::Take(ChunkedJSONWr
 
 void
 ChunkedJSONWriteFunc::AllocChunk(size_t aChunkSize)
 {
   MOZ_ASSERT(mChunkLengths.length() == mChunkList.length());
   mozilla::UniquePtr<char[]> newChunk = mozilla::MakeUnique<char[]>(aChunkSize);
   mChunkPtr = newChunk.get();
   mChunkEnd = mChunkPtr + aChunkSize;
-  *mChunkPtr = '\0';
   MOZ_ALWAYS_TRUE(mChunkLengths.append(0));
   MOZ_ALWAYS_TRUE(mChunkList.append(mozilla::Move(newChunk)));
 }
 
 void
 SpliceableJSONWriter::TakeAndSplice(ChunkedJSONWriteFunc* aFunc)
 {
   Separator();
--- a/tools/profiler/ProfilerBacktrace.cpp
+++ b/tools/profiler/ProfilerBacktrace.cpp
@@ -23,11 +23,11 @@ ProfilerBacktrace::~ProfilerBacktrace()
     delete mProfile;
   }
 }
 
 void
 ProfilerBacktrace::StreamJSON(SpliceableJSONWriter& aWriter,
                               UniqueStacks& aUniqueStacks)
 {
-  mozilla::MutexAutoLock lock(*mProfile->GetMutex());
+  ::MutexAutoLock lock(mProfile->GetMutex());
   mProfile->StreamJSON(aWriter, aUniqueStacks);
 }
--- a/tools/profiler/ProfilerMarkers.cpp
+++ b/tools/profiler/ProfilerMarkers.cpp
@@ -1,20 +1,22 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GeckoProfiler.h"
 #include "ProfilerBacktrace.h"
 #include "ProfilerMarkers.h"
+#include "SyncProfile.h"
+#ifndef SPS_STANDALONE
 #include "gfxASurface.h"
-#include "SyncProfile.h"
 #include "Layers.h"
 #include "prprf.h"
+#endif
 
 ProfilerMarkerPayload::ProfilerMarkerPayload(ProfilerBacktrace* aStack)
   : mStack(aStack)
 {}
 
 ProfilerMarkerPayload::ProfilerMarkerPayload(const mozilla::TimeStamp& aStartTime,
                                              const mozilla::TimeStamp& aEndTime,
                                              ProfilerBacktrace* aStack)
@@ -82,16 +84,17 @@ ProfilerMarkerTracing::StreamPayload(Spl
     if (GetMetaData() == TRACING_INTERVAL_START) {
       aWriter.StringProperty("interval", "start");
     } else if (GetMetaData() == TRACING_INTERVAL_END) {
       aWriter.StringProperty("interval", "end");
     }
   }
 }
 
+#ifndef SPS_STANDALONE
 GPUMarkerPayload::GPUMarkerPayload(
   const mozilla::TimeStamp& aCpuTimeStart,
   const mozilla::TimeStamp& aCpuTimeEnd,
   uint64_t aGpuTimeStart,
   uint64_t aGpuTimeEnd)
 
   : ProfilerMarkerPayload(aCpuTimeStart, aCpuTimeEnd)
   , mCpuTimeStart(aCpuTimeStart)
@@ -199,8 +202,9 @@ VsyncPayload::VsyncPayload(mozilla::Time
 }
 
 void
 VsyncPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
 {
   aWriter.DoubleProperty("vsync", profiler_time(mVsyncTimestamp));
   aWriter.StringProperty("category", "VsyncTimestamp");
 }
+#endif
--- a/tools/profiler/ProfilerMarkers.h
+++ b/tools/profiler/ProfilerMarkers.h
@@ -3,18 +3,16 @@
  * 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 PROFILER_MARKERS_H
 #define PROFILER_MARKERS_H
 
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Attributes.h"
-#include "nsAutoPtr.h"
-#include "Units.h"    // For ScreenIntPoint
 
 namespace mozilla {
 namespace layers {
 class Layer;
 } // layers
 } // mozilla
 
 class SpliceableJSONWriter;
@@ -84,16 +82,17 @@ public:
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   const char *mCategory;
   TracingMetadata mMetaData;
 };
 
 
+#ifndef SPS_STANDALONE
 #include "gfxASurface.h"
 class ProfilerMarkerImagePayload : public ProfilerMarkerPayload
 {
 public:
   explicit ProfilerMarkerImagePayload(gfxASurface *aImg);
 
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
                              UniqueStacks& aUniqueStacks) override;
@@ -131,16 +130,18 @@ public:
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   mozilla::layers::Layer* mLayer;
   mozilla::gfx::Point mPoint;
 };
 
+#include "Units.h"    // For ScreenIntPoint
+
 /**
  * Tracks when touch events are processed by gecko, not when
  * the touch actually occured in gonk/android.
  */
 class TouchDataPayload : public ProfilerMarkerPayload
 {
 public:
   explicit TouchDataPayload(const mozilla::ScreenIntPoint& aPoint);
@@ -182,10 +183,11 @@ public:
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   mozilla::TimeStamp mCpuTimeStart;
   mozilla::TimeStamp mCpuTimeEnd;
   uint64_t mGpuTimeStart;
   uint64_t mGpuTimeEnd;
 };
+#endif
 
 #endif // PROFILER_MARKERS_H
--- a/tools/profiler/PseudoStack.h
+++ b/tools/profiler/PseudoStack.h
@@ -6,17 +6,19 @@
 #ifndef PROFILER_PSEUDO_STACK_H_
 #define PROFILER_PSEUDO_STACK_H_
 
 #include "mozilla/ArrayUtils.h"
 #include <stdint.h>
 #include "js/ProfilingStack.h"
 #include <stdlib.h>
 #include "mozilla/Atomics.h"
+#ifndef SPS_STANDALONE
 #include "nsISupportsImpl.h"
+#endif
 
 /* we duplicate this code here to avoid header dependencies
  * which make it more difficult to include in other places */
 #if defined(_M_X64) || defined(__x86_64__)
 #define V8_HOST_ARCH_X64 1
 #elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
 #define V8_HOST_ARCH_IA32 1
 #elif defined(__ARMEL__)
@@ -315,16 +317,17 @@ public:
     return mStackPointer == 0;
   }
   uint32_t stackSize() const
   {
     return sMin(mStackPointer, mozilla::sig_safe_t(mozilla::ArrayLength(mStack)));
   }
 
   void sampleRuntime(JSRuntime* runtime) {
+#ifndef SPS_STANDALONE
     if (mRuntime && !runtime) {
       // On JS shut down, flush the current buffer as stringifying JIT samples
       // requires a live JSRuntime.
       flushSamplerOnJSShutdown();
     }
 
     mRuntime = runtime;
 
@@ -335,17 +338,19 @@ public:
     static_assert(sizeof(mStack[0]) == sizeof(js::ProfileEntry),
                   "mStack must be binary compatible with js::ProfileEntry.");
     js::SetRuntimeProfilingStack(runtime,
                                  (js::ProfileEntry*) mStack,
                                  (uint32_t*) &mStackPointer,
                                  (uint32_t) mozilla::ArrayLength(mStack));
     if (mStartJSSampling)
       enableJSSampling();
+#endif
   }
+#ifndef SPS_STANDALONE
   void enableJSSampling() {
     if (mRuntime) {
       js::EnableRuntimeProfilingStack(mRuntime, true);
       js::RegisterRuntimeProfilingEventMarker(mRuntime, &ProfilerJSEventMarker);
       mStartJSSampling = false;
     } else {
       mStartJSSampling = true;
     }
@@ -354,29 +359,32 @@ public:
     if (mStartJSSampling)
       enableJSSampling();
   }
   void disableJSSampling() {
     mStartJSSampling = false;
     if (mRuntime)
       js::EnableRuntimeProfilingStack(mRuntime, false);
   }
+#endif
 
   // Keep a list of active checkpoints
   StackEntry volatile mStack[1024];
  private:
 
   // A PseudoStack can only be created via the "create" method.
   PseudoStack()
     : mStackPointer(0)
     , mSleepId(0)
     , mSleepIdObserved(0)
     , mSleeping(false)
     , mRefCnt(1)
+#ifndef SPS_STANDALONE
     , mRuntime(nullptr)
+#endif
     , mStartJSSampling(false)
     , mPrivacyMode(false)
   { }
 
   // A PseudoStack can only be deleted via deref.
   ~PseudoStack() {
     if (mStackPointer != 0) {
       // We're releasing the pseudostack while it's still in use.
@@ -406,18 +414,20 @@ public:
   // Keeps tack of whether the thread is sleeping or not (1 when sleeping 0 when awake)
   mozilla::Atomic<int> mSleeping;
   // This class is reference counted because it must be kept alive by
   // the ThreadInfo, by the reference from tlsPseudoStack, and by the
   // current thread when callbacks are in progress.
   mozilla::Atomic<int> mRefCnt;
 
  public:
+#ifndef SPS_STANDALONE
   // The runtime which is being sampled
   JSRuntime *mRuntime;
+#endif
   // Start JS Profiling when possible
   bool mStartJSSampling;
   bool mPrivacyMode;
 
   enum SleepState {NOT_SLEEPING, SLEEPING_FIRST, SLEEPING_AGAIN};
 
   // The first time this is called per sleep cycle we return SLEEPING_FIRST
   // and any other subsequent call within the same sleep cycle we return SLEEPING_AGAIN
--- a/tools/profiler/SyncProfile.cpp
+++ b/tools/profiler/SyncProfile.cpp
@@ -20,31 +20,28 @@ SyncProfile::~SyncProfile()
   // SyncProfile owns the ThreadInfo; see NewSyncProfile.
   ThreadInfo* info = GetThreadInfo();
   delete info;
 }
 
 bool
 SyncProfile::ShouldDestroy()
 {
-  GetMutex()->AssertNotCurrentThreadOwns();
-  mozilla::MutexAutoLock lock(*GetMutex());
+  ::MutexAutoLock lock(GetMutex());
   if (mOwnerState == OWNED) {
     mOwnerState = OWNER_DESTROYING;
     return true;
   }
   mOwnerState = ORPHANED;
   return false;
 }
 
 void
 SyncProfile::EndUnwind()
 {
-  // Mutex must be held when this is called
-  GetMutex()->AssertCurrentThreadOwns();
   if (mOwnerState != ORPHANED) {
     mOwnerState = OWNED;
   }
   // Save mOwnerState before we release the mutex
   OwnerState ownerState = mOwnerState;
   ThreadProfile::EndUnwind();
   if (ownerState == ORPHANED) {
     delete this;
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -4,52 +4,58 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <algorithm>
 #include <string>
 #include <stdio.h>
 #include <fstream>
 #include <sstream>
 #include "GeckoProfiler.h"
+#ifndef SPS_STANDALONE
 #include "SaveProfileTask.h"
+#include "nsThreadUtils.h"
+#include "prenv.h"
+#include "prtime.h"
+#include "nsXULAppAPI.h"
+#endif
 #include "ProfileEntry.h"
 #include "SyncProfile.h"
 #include "platform.h"
-#include "nsThreadUtils.h"
-#include "prenv.h"
-#include "prtime.h"
 #include "shared-libraries.h"
 #include "mozilla/StackWalk.h"
 #include "TableTicker.h"
-#include "nsXULAppAPI.h"
 
 // JSON
 #include "ProfileJSONWriter.h"
 
+#ifndef SPS_STANDALONE
 // Meta
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIXULRuntime.h"
 #include "nsIXULAppInfo.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "PlatformMacros.h"
 #include "nsTArray.h"
+#endif
 
 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
   #include "AndroidBridge.h"
 #endif
 
+#ifndef SPS_STANDALONE
 // JS
 #include "jsfriendapi.h"
 #include "js/ProfilingFrameIterator.h"
+#endif
 
 #if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
  #define USE_NS_STACKWALK
 #endif
 
 #if defined(XP_WIN)
 typedef CONTEXT tickcontext_t;
 #elif defined(LINUX)
@@ -65,21 +71,23 @@ pid_t gettid();
 #if defined(SPS_ARCH_arm) && defined(MOZ_WIDGET_GONK)
  // Should also work on other Android and ARM Linux, but not tested there yet.
  #define USE_EHABI_STACKWALK
 #endif
 #ifdef USE_EHABI_STACKWALK
  #include "EHABIStackWalk.h"
 #endif
 
+#ifndef SPS_STANDALONE
 #if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
 # define USE_LUL_STACKWALK
 # include "LulMain.h"
 # include "platform-linux-lul.h"
 #endif
+#endif
 
 using std::string;
 using namespace mozilla;
 
 #ifndef MAXPATHLEN
  #ifdef PATH_MAX
   #define MAXPATHLEN PATH_MAX
  #elif defined(MAX_PATH)
@@ -98,17 +106,71 @@ using namespace mozilla;
 #else
 # define VALGRIND_MAKE_MEM_DEFINED(_addr,_len)   ((void)0)
 #endif
 
 
 ///////////////////////////////////////////////////////////////////////
 // BEGIN SaveProfileTask et al
 
-std::string GetSharedLibraryInfoString();
+static void
+AddSharedLibraryInfoToStream(std::ostream& aStream, const SharedLibrary& aLib)
+{
+  aStream << "{";
+  aStream << "\"start\":" << aLib.GetStart();
+  aStream << ",\"end\":" << aLib.GetEnd();
+  aStream << ",\"offset\":" << aLib.GetOffset();
+  aStream << ",\"name\":\"" << aLib.GetName() << "\"";
+  const std::string &breakpadId = aLib.GetBreakpadId();
+  aStream << ",\"breakpadId\":\"" << breakpadId << "\"";
+#ifdef XP_WIN
+  // FIXME: remove this XP_WIN code when the profiler plugin has switched to
+  // using breakpadId.
+  std::string pdbSignature = breakpadId.substr(0, 32);
+  std::string pdbAgeStr = breakpadId.substr(32,  breakpadId.size() - 1);
+
+  std::stringstream stream;
+  stream << pdbAgeStr;
+
+  unsigned pdbAge;
+  stream << std::hex;
+  stream >> pdbAge;
+
+#ifdef DEBUG
+  std::ostringstream oStream;
+  oStream << pdbSignature << std::hex << std::uppercase << pdbAge;
+  MOZ_ASSERT(breakpadId == oStream.str());
+#endif
+
+  aStream << ",\"pdbSignature\":\"" << pdbSignature << "\"";
+  aStream << ",\"pdbAge\":" << pdbAge;
+  aStream << ",\"pdbName\":\"" << aLib.GetName() << "\"";
+#endif
+  aStream << "}";
+}
+
+std::string
+GetSharedLibraryInfoStringInternal()
+{
+  SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
+  if (info.GetSize() == 0)
+    return "[]";
+
+  std::ostringstream os;
+  os << "[";
+  AddSharedLibraryInfoToStream(os, info.GetEntry(0));
+
+  for (size_t i = 1; i < info.GetSize(); i++) {
+    os << ",";
+    AddSharedLibraryInfoToStream(os, info.GetEntry(i));
+  }
+
+  os << "]";
+  return os.str();
+}
 
 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;
@@ -155,17 +217,17 @@ TableTicker::TableTicker(double aInterva
   for (uint32_t i = 0; i < aFilterCount; ++i) {
     mThreadNameFilters[i] = aThreadNameFilters[i];
   }
 
   bool ignore;
   sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
 
   {
-    mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
+    ::MutexAutoLock lock(*sRegisteredThreadsMutex);
 
     // Create ThreadProfile for each registered thread
     for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
       ThreadInfo* info = sRegisteredThreads->at(i);
 
       RegisterThread(info);
     }
 
@@ -183,17 +245,17 @@ TableTicker::~TableTicker()
 {
   if (IsActive())
     Stop();
 
   SetActiveSampler(nullptr);
 
   // Destroy ThreadProfile for all threads
   {
-    mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
+    ::MutexAutoLock lock(*sRegisteredThreadsMutex);
 
     for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
       ThreadInfo* info = sRegisteredThreads->at(i);
       ThreadProfile* profile = info->Profile();
       if (profile) {
         delete profile;
         info->SetProfile(nullptr);
       }
@@ -212,20 +274,22 @@ TableTicker::~TableTicker()
 }
 
 void TableTicker::HandleSaveRequest()
 {
   if (!mSaveRequested)
     return;
   mSaveRequested = false;
 
+#ifndef SPS_STANDALONE
   // TODO: Use use the ipc/chromium Tasks here to support processes
   // without XPCOM.
   nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
   NS_DispatchToMainThread(runnable);
+#endif
 }
 
 void TableTicker::DeleteExpiredMarkers()
 {
   mBuffer->deleteExpiredStoredMarkers();
 }
 
 void TableTicker::StreamTaskTracer(SpliceableJSONWriter& aWriter)
@@ -234,17 +298,17 @@ void TableTicker::StreamTaskTracer(Splic
   aWriter.StartArrayProperty("data");
     nsAutoPtr<nsTArray<nsCString>> data(mozilla::tasktracer::GetLoggedData(sStartTime));
     for (uint32_t i = 0; i < data->Length(); ++i) {
       aWriter.StringElement((data->ElementAt(i)).get());
     }
   aWriter.EndArray();
 
   aWriter.StartArrayProperty("threads");
-    mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
+    ::MutexAutoLock lock(*sRegisteredThreadsMutex);
     for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
       // Thread meta data
       ThreadInfo* info = sRegisteredThreads->at(i);
       aWriter.StartObjectElement();
         if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
           // TODO Add the proper plugin name
           aWriter.StringProperty("name", "Plugin");
         } else {
@@ -260,21 +324,23 @@ void TableTicker::StreamTaskTracer(Splic
 }
 
 
 void TableTicker::StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter)
 {
   aWriter.IntProperty("version", 3);
   aWriter.DoubleProperty("interval", interval());
   aWriter.IntProperty("stackwalk", mUseStackWalk);
-  aWriter.IntProperty("processType", XRE_GetProcessType());
 
+#ifndef SPS_STANDALONE
   mozilla::TimeDuration delta = mozilla::TimeStamp::Now() - sStartTime;
   aWriter.DoubleProperty("startTime", static_cast<double>(PR_Now()/1000.0 - delta.ToMilliseconds()));
 
+  aWriter.IntProperty("processType", XRE_GetProcessType());
+
   nsresult res;
   nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
   if (!NS_FAILED(res)) {
     nsAutoCString string;
 
     res = http->GetPlatform(string);
     if (!NS_FAILED(res))
       aWriter.StringProperty("platform", string.Data());
@@ -304,25 +370,27 @@ void TableTicker::StreamMetaJSCustomObje
   nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
   if (appInfo) {
     nsAutoCString string;
 
     res = appInfo->GetName(string);
     if (!NS_FAILED(res))
       aWriter.StringProperty("product", string.Data());
   }
+#endif
 }
 
 void TableTicker::ToStreamAsJSON(std::ostream& stream, double aSinceTime)
 {
   SpliceableJSONWriter b(mozilla::MakeUnique<OStreamJSONWriteFunc>(stream));
   StreamJSON(b, aSinceTime);
 }
 
-JSObject* TableTicker::ToJSObject(JSContext* aCx, double aSinceTime)
+#ifndef SPS_STANDALONE
+JSObject* TableTicker::ToJSObject(JSContext *aCx, double aSinceTime)
 {
   JS::RootedValue val(aCx);
   {
     UniquePtr<char[]> buf = ToJSON(aSinceTime);
     NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
     bool rv = JS_ParseJSON(aCx, static_cast<const char16_t*>(js_string.get()),
                            js_string.Length(), &val);
     if (!rv) {
@@ -348,16 +416,17 @@ JSObject* TableTicker::ToJSObject(JSCont
           }
         }
       }
 #endif
     }
   }
   return &val.toObject();
 }
+#endif
 
 UniquePtr<char[]> TableTicker::ToJSON(double aSinceTime)
 {
   SpliceableChunkedJSONWriter b;
   StreamJSON(b, aSinceTime);
   return b.WriteFunc()->CopyData();
 }
 
@@ -395,17 +464,16 @@ void SubProcessCallback(const char* aPro
   closure->mWriter->StringElement(aProfile);
 }
 
 
 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
 static
 void BuildJavaThreadJSObject(SpliceableJSONWriter& aWriter)
 {
-  aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
   aWriter.StringProperty("name", "Java Main Thread");
 
   aWriter.StartArrayProperty("samples");
 
     // for each sample
     for (int sampleId = 0; true; sampleId++) {
       bool firstRun = true;
       // for each frame
@@ -440,26 +508,25 @@ void BuildJavaThreadJSObject(SpliceableJ
       }
       // if we found no frames for this sample, we are done
       if (firstRun) {
         break;
       }
     }
 
   aWriter.EndArray();
-  aWriter.End();
 }
 #endif
 
 void TableTicker::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
 {
   aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
   {
     // Put shared library info
-    aWriter.StringProperty("libs", GetSharedLibraryInfoString().c_str());
+    aWriter.StringProperty("libs", GetSharedLibraryInfoStringInternal().c_str());
 
     // Put meta data
     aWriter.StartObjectProperty("meta");
       StreamMetaJSCustomObject(aWriter);
     aWriter.EndObject();
 
     // Data of TaskTracer doesn't belong in the circular buffer.
     if (TaskTracer()) {
@@ -468,32 +535,33 @@ void TableTicker::StreamJSON(SpliceableJ
     }
 
     // Lists the samples for each ThreadProfile
     aWriter.StartArrayProperty("threads");
     {
       SetPaused(true);
 
       {
-        mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
+        ::MutexAutoLock lock(*sRegisteredThreadsMutex);
 
         for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
           // Thread not being profiled, skip it
           if (!sRegisteredThreads->at(i)->Profile())
             continue;
 
           // Note that we intentionally include ThreadProfile which
           // have been marked for pending delete.
 
-          MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
+          ::MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex());
 
           sRegisteredThreads->at(i)->Profile()->StreamJSON(aWriter, aSinceTime);
         }
       }
 
+#ifndef SPS_STANDALONE
       if (Sampler::CanNotifyObservers()) {
         // Send a event asking any subprocesses (plugins) to
         // give us their information
         SubprocessClosure closure(&aWriter);
         nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
         if (os) {
           nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure);
           os->NotifyObservers(pse, "profiler-subprocess", nullptr);
@@ -504,58 +572,63 @@ void TableTicker::StreamJSON(SpliceableJ
       if (ProfileJava()) {
         mozilla::widget::GeckoJavaSampler::PauseJavaProfiling();
 
         BuildJavaThreadJSObject(aWriter);
 
         mozilla::widget::GeckoJavaSampler::UnpauseJavaProfiling();
       }
   #endif
+#endif
 
       SetPaused(false);
     }
     aWriter.EndArray();
   }
   aWriter.End();
 }
 
 void TableTicker::FlushOnJSShutdown(JSRuntime* aRuntime)
 {
+#ifndef SPS_STANDALONE
   SetPaused(true);
 
   {
-    mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
+    ::MutexAutoLock lock(*sRegisteredThreadsMutex);
 
     for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
       // Thread not being profiled, skip it.
       if (!sRegisteredThreads->at(i)->Profile() ||
           sRegisteredThreads->at(i)->IsPendingDelete()) {
         continue;
       }
 
       // Thread not profiling the runtime that's going away, skip it.
       if (sRegisteredThreads->at(i)->Profile()->GetPseudoStack()->mRuntime != aRuntime) {
         continue;
       }
 
-      MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
+      ::MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex());
       sRegisteredThreads->at(i)->Profile()->FlushSamplesAndMarkers();
     }
   }
 
   SetPaused(false);
+#endif
 }
 
 void PseudoStack::flushSamplerOnJSShutdown()
 {
+#ifndef SPS_STANDALONE
   MOZ_ASSERT(mRuntime);
   TableTicker* t = tlsTicker.get();
   if (t) {
     t->FlushOnJSShutdown(mRuntime);
   }
+#endif
 }
 
 // END SaveProfileTask et al
 ////////////////////////////////////////////////////////////////////////
 
 static
 void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
 {
@@ -590,16 +663,17 @@ void addPseudoEntry(volatile StackEntry 
   // First entry has tagName 's' (start)
   // Check for magic pointer bit 1 to indicate copy
   const char* sampleLabel = entry.label();
   if (entry.isCopyLabel()) {
     // Store the string using 1 or more 'd' (dynamic) tags
     // that will happen to the preceding tag
 
     addDynamicTag(aProfile, 'c', sampleLabel);
+#ifndef SPS_STANDALONE
     if (entry.isJs()) {
       if (!entry.pc()) {
         // The JIT only allows the top-most entry to have a nullptr pc
         MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
         // If stack-walking was disabled, then that's just unfortunate
         if (lastpc) {
           jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
                                                 lastpc);
@@ -608,16 +682,17 @@ void addPseudoEntry(volatile StackEntry 
           }
         }
       } else {
         lineno = JS_PCToLineNumber(entry.script(), entry.pc());
       }
     } else {
       lineno = entry.line();
     }
+#endif
   } else {
     aProfile.addTag(ProfileEntry('c', sampleLabel));
 
     // XXX: Bug 1010578. Don't assume a CPP entry and try to get the
     // line for js entries as well.
     if (entry.isCpp()) {
       lineno = entry.line();
     }
@@ -676,16 +751,17 @@ void mergeStacksIntoProfile(ThreadProfil
   // entries.
   uint32_t startBufferGen;
   if (aSample->isSamplingCurrentThread) {
     startBufferGen = UINT32_MAX;
   } else {
     startBufferGen = aProfile.bufferGeneration();
   }
   uint32_t jsCount = 0;
+#ifndef SPS_STANDALONE
   JS::ProfilingFrameIterator::Frame jsFrames[1000];
   // Only walk jit stack if profiling frame iterator is turned on.
   if (pseudoStack->mRuntime && JS::IsProfilingEnabledForRuntime(pseudoStack->mRuntime)) {
     AutoWalkJSStack autoWalkJSStack;
     const uint32_t maxFrames = mozilla::ArrayLength(jsFrames);
 
     if (aSample && autoWalkJSStack.walkAllowed) {
       JS::ProfilingFrameIterator::RegisterState registerState;
@@ -709,16 +785,17 @@ void mergeStacksIntoProfile(ThreadProfil
           mozilla::Maybe<JS::ProfilingFrameIterator::Frame> frame =
             jsIter.getPhysicalFrameWithoutLabel();
           if (frame.isSome())
             jsFrames[jsCount++] = frame.value();
         }
       }
     }
   }
+#endif
 
   // Start the sample with a root entry.
   aProfile.addTag(ProfileEntry('s', "(root)"));
 
   // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
   // native arrays are ordered youngest-to-oldest. We must add frames to
   // aProfile oldest-to-youngest. Thus, iterate over the pseudo-stack forwards
   // and JS and native arrays backwards. Note: this means the terminating
@@ -738,34 +815,38 @@ void mergeStacksIntoProfile(ThreadProfil
     uint8_t *nativeStackAddr = nullptr;
 
     if (pseudoIndex != pseudoCount) {
       volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
 
       if (pseudoFrame.isCpp())
         lastPseudoCppStackAddr = (uint8_t *) pseudoFrame.stackAddress();
 
+#ifndef SPS_STANDALONE
       // Skip any pseudo-stack JS frames which are marked isOSR
       // Pseudostack frames are marked isOSR when the JS interpreter
       // enters a jit frame on a loop edge (via on-stack-replacement,
       // or OSR).  To avoid both the pseudoframe and jit frame being
       // recorded (and showing up twice), the interpreter marks the
       // interpreter pseudostack entry with the OSR flag to ensure that
       // it doesn't get counted.
       if (pseudoFrame.isJs() && pseudoFrame.isOSR()) {
           pseudoIndex++;
           continue;
       }
+#endif
 
       MOZ_ASSERT(lastPseudoCppStackAddr);
       pseudoStackAddr = lastPseudoCppStackAddr;
     }
 
+#ifndef SPS_STANDALONE
     if (jsIndex >= 0)
       jsStackAddr = (uint8_t *) jsFrames[jsIndex].stackAddress;
+#endif
 
     if (nativeIndex >= 0)
       nativeStackAddr = (uint8_t *) aNativeStack.sp_array[nativeIndex];
 
     // If there's a native stack entry which has the same SP as a
     // pseudo stack entry, pretend we didn't see the native stack
     // entry.  Ditto for a native stack entry which has the same SP as
     // a JS stack entry.  In effect this means pseudo or JS entries
@@ -788,16 +869,17 @@ void mergeStacksIntoProfile(ThreadProfil
     if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
       MOZ_ASSERT(pseudoIndex < pseudoCount);
       volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
       addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr);
       pseudoIndex++;
       continue;
     }
 
+#ifndef SPS_STANDALONE
     // Check to see if JS jit stack frame is top-most
     if (jsStackAddr > nativeStackAddr) {
       MOZ_ASSERT(jsIndex >= 0);
       const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];
 
       // Stringifying non-asm.js JIT frames is delayed until streaming
       // time. To re-lookup the entry in the JitcodeGlobalTable, we need to
       // store the JIT code address ('J') in the circular buffer.
@@ -818,40 +900,43 @@ void mergeStacksIntoProfile(ThreadProfil
         MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
                    jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
         aProfile.addTag(ProfileEntry('J', jsFrames[jsIndex].returnAddress));
       }
 
       jsIndex--;
       continue;
     }
+#endif
 
     // If we reach here, there must be a native stack entry and it must be the
     // greatest entry.
     if (nativeStackAddr) {
       MOZ_ASSERT(nativeIndex >= 0);
       aProfile
         .addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex]));
     }
     if (nativeIndex >= 0) {
       nativeIndex--;
     }
   }
 
+#ifndef SPS_STANDALONE
   // Update the JS runtime with the current profile sample buffer generation.
   //
   // Do not do this for synchronous sampling, which create their own
   // ProfileBuffers.
   if (!aSample->isSamplingCurrentThread && pseudoStack->mRuntime) {
     MOZ_ASSERT(aProfile.bufferGeneration() >= startBufferGen);
     uint32_t lapCount = aProfile.bufferGeneration() - startBufferGen;
     JS::UpdateJSRuntimeProfilerSampleBufferGen(pseudoStack->mRuntime,
                                                aProfile.bufferGeneration(),
                                                lapCount);
   }
+#endif
 }
 
 #ifdef USE_NS_STACKWALK
 static
 void StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP,
                        void* aClosure)
 {
   NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
@@ -1138,20 +1223,22 @@ void TableTicker::InplaceTick(TickSample
     ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
     while (pendingMarkersList && pendingMarkersList->peek()) {
       ProfilerMarker* marker = pendingMarkersList->popHead();
       currThreadProfile.addStoredMarker(marker);
       currThreadProfile.addTag(ProfileEntry('m', marker));
     }
   }
 
+#ifndef SPS_STANDALONE
   if (sample && currThreadProfile.GetThreadResponsiveness()->HasData()) {
     mozilla::TimeDuration delta = currThreadProfile.GetThreadResponsiveness()->GetUnresponsiveDuration(sample->timestamp);
     currThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
   }
+#endif
 
   // rssMemory is equal to 0 when we are not recording.
   if (sample && sample->rssMemory != 0) {
     currThreadProfile.addTag(ProfileEntry('R', static_cast<double>(sample->rssMemory)));
   }
 
   // ussMemory is equal to 0 when we are not recording.
   if (sample && sample->ussMemory != 0) {
@@ -1177,17 +1264,17 @@ SyncProfile* NewSyncProfile()
 {
   PseudoStack* stack = tlsPseudoStack.get();
   if (!stack) {
     MOZ_ASSERT(stack);
     return nullptr;
   }
   Thread::tid_t tid = Thread::GetCurrentId();
 
-  ThreadInfo* info = new ThreadInfo("SyncProfile", tid, NS_IsMainThread(), stack, nullptr);
+  ThreadInfo* info = new ThreadInfo("SyncProfile", tid, false, stack, nullptr);
   SyncProfile* profile = new SyncProfile(info, GET_BACKTRACE_DEFAULT_ENTRY);
   return profile;
 }
 
 } // anonymous namespace
 
 SyncProfile* TableTicker::GetBacktrace()
 {
--- a/tools/profiler/TableTicker.h
+++ b/tools/profiler/TableTicker.h
@@ -3,19 +3,20 @@
  * 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 TableTicker_h
 #define TableTicker_h
 
 #include "platform.h"
 #include "ProfileEntry.h"
-#include "mozilla/Mutex.h"
 #include "mozilla/Vector.h"
+#ifndef SPS_STANDALONE
 #include "IntelPowerGadget.h"
+#endif
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
 namespace mozilla {
 class ProfileGatherer;
 } // namespace mozilla
 
@@ -78,32 +79,34 @@ class TableTicker: public Sampler {
   }
 
   virtual void HandleSaveRequest() override;
   virtual void DeleteExpiredMarkers() override;
 
   ThreadProfile* GetPrimaryThreadProfile()
   {
     if (!mPrimaryThreadProfile) {
-      mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
+      ::MutexAutoLock lock(*sRegisteredThreadsMutex);
 
       for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
         ThreadInfo* info = sRegisteredThreads->at(i);
         if (info->IsMainThread() && !info->IsPendingDelete()) {
           mPrimaryThreadProfile = info->Profile();
           break;
         }
       }
     }
 
     return mPrimaryThreadProfile;
   }
 
   void ToStreamAsJSON(std::ostream& stream, double aSinceTime = 0);
-  virtual JSObject *ToJSObject(JSContext* aCx, double aSinceTime = 0);
+#ifndef SPS_STANDALONE
+  virtual JSObject *ToJSObject(JSContext *aCx, double aSinceTime = 0);
+#endif
   mozilla::UniquePtr<char[]> ToJSON(double aSinceTime = 0);
   virtual void ToJSObjectAsync(double aSinceTime = 0, mozilla::dom::Promise* aPromise = 0);
   void StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter);
   void StreamTaskTracer(SpliceableJSONWriter& aWriter);
   void FlushOnJSShutdown(JSRuntime* aRuntime);
   bool ProfileJS() const { return mProfileJS; }
   bool ProfileJava() const { return mProfileJava; }
   bool ProfileGPU() const { return mProfileGPU; }
@@ -127,17 +130,17 @@ protected:
 
   // Not implemented on platforms which do not support backtracing
   void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
 
   void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime);
 
   // This represent the application's main thread (SAMPLER_INIT)
   ThreadProfile* mPrimaryThreadProfile;
-  nsRefPtr<ProfileBuffer> mBuffer;
+  mozilla::RefPtr<ProfileBuffer> mBuffer;
   bool mSaveRequested;
   bool mAddLeafAddresses;
   bool mUseStackWalk;
   bool mProfileJS;
   bool mProfileGPU;
   bool mProfileThreads;
   bool mProfileJava;
   bool mProfilePower;
--- a/tools/profiler/nsProfiler.cpp
+++ b/tools/profiler/nsProfiler.cpp
@@ -129,70 +129,22 @@ nsProfiler::GetProfile(double aSinceTime
     char *profileStr = static_cast<char *>
                          (nsMemory::Clone(profile.get(), (len + 1) * sizeof(char)));
     profileStr[len] = '\0';
     *aProfile = profileStr;
   }
   return NS_OK;
 }
 
-static void
-AddSharedLibraryInfoToStream(std::ostream& aStream, const SharedLibrary& aLib)
-{
-  aStream << "{";
-  aStream << "\"start\":" << aLib.GetStart();
-  aStream << ",\"end\":" << aLib.GetEnd();
-  aStream << ",\"offset\":" << aLib.GetOffset();
-  aStream << ",\"name\":\"" << aLib.GetName() << "\"";
-  const std::string &breakpadId = aLib.GetBreakpadId();
-  aStream << ",\"breakpadId\":\"" << breakpadId << "\"";
-#ifdef XP_WIN
-  // FIXME: remove this XP_WIN code when the profiler plugin has switched to
-  // using breakpadId.
-  std::string pdbSignature = breakpadId.substr(0, 32);
-  std::string pdbAgeStr = breakpadId.substr(32,  breakpadId.size() - 1);
-
-  std::stringstream stream;
-  stream << pdbAgeStr;
-
-  unsigned pdbAge;
-  stream << std::hex;
-  stream >> pdbAge;
-
-#ifdef DEBUG
-  std::ostringstream oStream;
-  oStream << pdbSignature << std::hex << std::uppercase << pdbAge;
-  MOZ_ASSERT(breakpadId == oStream.str());
-#endif
-
-  aStream << ",\"pdbSignature\":\"" << pdbSignature << "\"";
-  aStream << ",\"pdbAge\":" << pdbAge;
-  aStream << ",\"pdbName\":\"" << aLib.GetName() << "\"";
-#endif
-  aStream << "}";
-}
+std::string GetSharedLibraryInfoStringInternal();
 
 std::string
 GetSharedLibraryInfoString()
 {
-  SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
-  if (info.GetSize() == 0)
-    return "[]";
-
-  std::ostringstream os;
-  os << "[";
-  AddSharedLibraryInfoToStream(os, info.GetEntry(0));
-
-  for (size_t i = 1; i < info.GetSize(); i++) {
-    os << ",";
-    AddSharedLibraryInfoToStream(os, info.GetEntry(i));
-  }
-
-  os << "]";
-  return os.str();
+  return GetSharedLibraryInfoStringInternal();
 }
 
 NS_IMETHODIMP
 nsProfiler::GetSharedLibraryInformation(nsAString& aOutString)
 {
   aOutString.Assign(NS_ConvertUTF8toUTF16(GetSharedLibraryInfoString().c_str()));
   return NS_OK;
 }
--- a/tools/profiler/platform-linux.cc
+++ b/tools/profiler/platform-linux.cc
@@ -315,17 +315,17 @@ static void* SignalSender(void* arg) {
   TimeDuration lastSleepOverhead = 0;
   TimeStamp sampleStart = TimeStamp::Now();
   while (SamplerRegistry::sampler->IsActive()) {
 
     SamplerRegistry::sampler->HandleSaveRequest();
     SamplerRegistry::sampler->DeleteExpiredMarkers();
 
     if (!SamplerRegistry::sampler->IsPaused()) {
-      mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+      ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
       std::vector<ThreadInfo*> threads =
         SamplerRegistry::sampler->GetRegisteredThreads();
 
       bool isFirstProfiledThread = true;
       for (uint32_t i = 0; i < threads.size(); i++) {
         ThreadInfo* info = threads[i];
 
         // This will be null if we're not interested in profiling this thread.
@@ -508,17 +508,17 @@ UpdateThreadId(void* aThreadInfo) {
 
 bool Sampler::RegisterCurrentThread(const char* aName,
                                     PseudoStack* aPseudoStack,
                                     bool aIsMainThread, void* stackTop)
 {
   if (!Sampler::sRegisteredThreadsMutex)
     return false;
 
-  mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+  ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   int id = gettid();
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id && !info->IsPendingDelete()) {
       // Thread already registered. This means the first unregister will be
       // too early.
       ASSERT(false);
@@ -556,17 +556,17 @@ bool Sampler::RegisterCurrentThread(cons
 
 void Sampler::UnregisterCurrentThread()
 {
   if (!Sampler::sRegisteredThreadsMutex)
     return;
 
   tlsStackTop.set(nullptr);
 
-  mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+  ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   int id = gettid();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id && !info->IsPendingDelete()) {
       if (profiler_is_active()) {
         // We still want to show the results of this thread if you
--- a/tools/profiler/platform-macos.cc
+++ b/tools/profiler/platform-macos.cc
@@ -24,25 +24,30 @@
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <math.h>
 
+#ifndef SPS_STANDALONE
 #include "ThreadResponsiveness.h"
 #include "nsThreadUtils.h"
 
+// Memory profile
+#include "nsMemoryReporterManager.h"
+#endif
+
 #include "platform.h"
 #include "TableTicker.h"
 #include "mozilla/TimeStamp.h"
 
-// Memory profile
-#include "nsMemoryReporterManager.h"
+using mozilla::TimeStamp;
+using mozilla::TimeDuration;
 
 // this port is based off of v8 svn revision 9837
 
 // XXX: this is a very stubbed out implementation
 // that only supports a single Sampler
 struct SamplerRegistry {
   static void AddActiveSampler(Sampler *sampler) {
     ASSERT(!SamplerRegistry::sampler);
@@ -200,34 +205,36 @@ class SamplerThread : public Thread {
 
   // Implement Thread::Run().
   virtual void Run() {
     TimeDuration lastSleepOverhead = 0;
     TimeStamp sampleStart = TimeStamp::Now();
     while (SamplerRegistry::sampler->IsActive()) {
       SamplerRegistry::sampler->DeleteExpiredMarkers();
       if (!SamplerRegistry::sampler->IsPaused()) {
-        mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+        ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
         std::vector<ThreadInfo*> threads =
           SamplerRegistry::sampler->GetRegisteredThreads();
         bool isFirstProfiledThread = true;
         for (uint32_t i = 0; i < threads.size(); i++) {
           ThreadInfo* info = threads[i];
 
           // This will be null if we're not interested in profiling this thread.
           if (!info->Profile() || info->IsPendingDelete())
             continue;
 
           PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
           if (sleeping == PseudoStack::SLEEPING_AGAIN) {
             info->Profile()->DuplicateLastSample();
             continue;
           }
 
+#ifndef SPS_STANDALONE
           info->Profile()->GetThreadResponsiveness()->Update();
+#endif
 
           ThreadProfile* thread_profile = info->Profile();
 
           SampleContext(SamplerRegistry::sampler, thread_profile,
                         isFirstProfiledThread);
           isFirstProfiledThread = false;
         }
       }
@@ -246,24 +253,25 @@ class SamplerThread : public Thread {
                      bool isFirstProfiledThread)
   {
     thread_act_t profiled_thread =
       thread_profile->GetPlatformData()->profiled_thread();
 
     TickSample sample_obj;
     TickSample* sample = &sample_obj;
 
+    // Unique Set Size is not supported on Mac.
+    sample->ussMemory = 0;
+    sample->rssMemory = 0;
+
+#ifndef SPS_STANDALONE
     if (isFirstProfiledThread && Sampler::GetActiveSampler()->ProfileMemory()) {
       sample->rssMemory = nsMemoryReporterManager::ResidentFast();
-    } else {
-      sample->rssMemory = 0;
     }
-
-    // Unique Set Size is not supported on Mac.
-    sample->ussMemory = 0;
+#endif
 
     // We're using thread_suspend on OS X because pthread_kill (which is what
     // we're using on Linux) has less consistent performance and causes
     // strange crashes, see bug 1166778 and bug 1166808.
 
     if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
 
 #if V8_HOST_ARCH_X64
@@ -365,17 +373,17 @@ Thread::GetCurrentId()
 bool Sampler::RegisterCurrentThread(const char* aName,
                                     PseudoStack* aPseudoStack,
                                     bool aIsMainThread, void* stackTop)
 {
   if (!Sampler::sRegisteredThreadsMutex)
     return false;
 
 
-  mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+  ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   int id = gettid();
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id && !info->IsPendingDelete()) {
       // Thread already registered. This means the first unregister will be
       // too early.
       ASSERT(false);
@@ -399,17 +407,17 @@ bool Sampler::RegisterCurrentThread(cons
 
 void Sampler::UnregisterCurrentThread()
 {
   if (!Sampler::sRegisteredThreadsMutex)
     return;
 
   tlsStackTop.set(nullptr);
 
-  mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+  ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   int id = gettid();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id && !info->IsPendingDelete()) {
       if (profiler_is_active()) {
         // We still want to show the results of this thread if you
--- a/tools/profiler/platform-win32.cc
+++ b/tools/profiler/platform-win32.cc
@@ -117,17 +117,17 @@ class SamplerThread : public Thread {
     // adjust the resolution to match.
     if (interval_ < 10)
         ::timeBeginPeriod(interval_);
 
     while (sampler_->IsActive()) {
       sampler_->DeleteExpiredMarkers();
 
       if (!sampler_->IsPaused()) {
-        mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+        ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
         std::vector<ThreadInfo*> threads =
           sampler_->GetRegisteredThreads();
         bool isFirstProfiledThread = true;
         for (uint32_t i = 0; i < threads.size(); i++) {
           ThreadInfo* info = threads[i];
 
           // This will be null if we're not interested in profiling this thread.
           if (!info->Profile() || info->IsPendingDelete())
@@ -310,17 +310,17 @@ void OS::Sleep(int milliseconds) {
 bool Sampler::RegisterCurrentThread(const char* aName,
                                     PseudoStack* aPseudoStack,
                                     bool aIsMainThread, void* stackTop)
 {
   if (!Sampler::sRegisteredThreadsMutex)
     return false;
 
 
-  mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+  ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   int id = GetCurrentThreadId();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id && !info->IsPendingDelete()) {
       // Thread already registered. This means the first unregister will be
       // too early.
@@ -345,17 +345,17 @@ bool Sampler::RegisterCurrentThread(cons
 
 void Sampler::UnregisterCurrentThread()
 {
   if (!Sampler::sRegisteredThreadsMutex)
     return;
 
   tlsStackTop.set(nullptr);
 
-  mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+  ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   int id = GetCurrentThreadId();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id && !info->IsPendingDelete()) {
       if (profiler_is_active()) {
         // We still want to show the results of this thread if you
--- a/tools/profiler/platform.cpp
+++ b/tools/profiler/platform.cpp
@@ -2,44 +2,51 @@
  * 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 <ostream>
 #include <fstream>
 #include <sstream>
 #include <errno.h>
 
-#include "ProfilerIOInterposeObserver.h"
 #include "platform.h"
 #include "PlatformMacros.h"
-#include "prenv.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "GeckoProfiler.h"
+#ifndef SPS_STANDALONE
+#include "ProfilerIOInterposeObserver.h"
 #include "mozilla/StaticPtr.h"
+#endif
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/TimeStamp.h"
 #include "PseudoStack.h"
 #include "TableTicker.h"
+#ifndef SPS_STANDALONE
 #include "nsIObserverService.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
+#include "nsXULAppAPI.h"
 #include "nsProfilerStartParams.h"
 #include "mozilla/Services.h"
 #include "nsThreadUtils.h"
+#endif
 #include "ProfilerMarkers.h"
-#include "nsXULAppAPI.h"
 
 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
   #include "AndroidBridge.h"
 #endif
 
+#ifndef SPS_STANDALONE
 #if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
 # define USE_LUL_STACKWALK
 # include "LulMain.h"
 # include "platform-linux-lul.h"
 #endif
+#endif
 
 mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
 mozilla::ThreadLocal<TableTicker *> tlsTicker;
 mozilla::ThreadLocal<void *> tlsStackTop;
 // We need to track whether we've been initialized otherwise
 // we end up using tlsStack without initializing it.
 // Because tlsStack is totally opaque to us we can't reuse
 // it as the flag itself.
@@ -69,46 +76,48 @@ const char* PROFILER_FEATURES = "MOZ_PRO
  * to know are associated with different events */
 
 // Values harvested from env vars, that control the profiler.
 static int sUnwindInterval;   /* in milliseconds */
 static int sUnwindStackScan;  /* max # of dubious frames allowed */
 static int sProfileEntries;   /* how many entries do we store? */
 
 std::vector<ThreadInfo*>* Sampler::sRegisteredThreads = nullptr;
-mozilla::Mutex* Sampler::sRegisteredThreadsMutex = nullptr;
+mozilla::UniquePtr< ::Mutex> Sampler::sRegisteredThreadsMutex;
 
 TableTicker* Sampler::sActiveSampler;
 
+#ifndef SPS_STANDALONE
 static mozilla::StaticAutoPtr<mozilla::ProfilerIOInterposeObserver>
                                                             sInterposeObserver;
+#endif
 
 // The name that identifies the gecko thread for calls to
 // profiler_register_thread.
 static const char * gGeckoThreadName = "GeckoMain";
 
 void Sampler::Startup() {
   sRegisteredThreads = new std::vector<ThreadInfo*>();
-  sRegisteredThreadsMutex = new mozilla::Mutex("sRegisteredThreads mutex");
+  sRegisteredThreadsMutex = OS::CreateMutex("sRegisteredThreads mutex");
 
   // We could create the sLUL object and read unwind info into it at
   // this point.  That would match the lifetime implied by destruction
   // of it in Sampler::Shutdown just below.  However, that gives a big
   // delay on startup, even if no profiling is actually to be done.
   // So, instead, sLUL is created on demand at the first call to
   // Sampler::Start.
 }
 
 void Sampler::Shutdown() {
   while (sRegisteredThreads->size() > 0) {
     delete sRegisteredThreads->back();
     sRegisteredThreads->pop_back();
   }
 
-  delete sRegisteredThreadsMutex;
+  sRegisteredThreadsMutex = nullptr;
   delete sRegisteredThreads;
 
   // UnregisterThread can be called after shutdown in XPCShell. Thus
   // we need to point to null to ignore such a call after shutdown.
   sRegisteredThreadsMutex = nullptr;
   sRegisteredThreads = nullptr;
 
 #if defined(USE_LUL_STACKWALK)
@@ -127,17 +136,19 @@ ThreadInfo::ThreadInfo(const char* aName
   , mThreadId(aThreadId)
   , mIsMainThread(aIsMainThread)
   , mPseudoStack(aPseudoStack)
   , mPlatformData(Sampler::AllocPlatformData(aThreadId))
   , mProfile(nullptr)
   , mStackTop(aStackTop)
   , mPendingDelete(false)
 {
+#ifndef SPS_STANDALONE
   mThread = NS_GetCurrentThread();
+#endif
 }
 
 ThreadInfo::~ThreadInfo() {
   free(mName);
 
   if (mProfile)
     delete mProfile;
 
@@ -240,17 +251,17 @@ void ProfilerMarker::StreamJSON(Spliceab
 enum class ProfilerVerbosity : int8_t { UNCHECKED, NOTVERBOSE, VERBOSE };
 
 // Raced on, potentially
 static ProfilerVerbosity profiler_verbosity = ProfilerVerbosity::UNCHECKED;
 
 bool moz_profiler_verbose()
 {
   if (profiler_verbosity == ProfilerVerbosity::UNCHECKED) {
-    if (PR_GetEnv("MOZ_PROFILER_VERBOSE") != nullptr)
+    if (getenv("MOZ_PROFILER_VERBOSE") != nullptr)
       profiler_verbosity = ProfilerVerbosity::VERBOSE;
     else
       profiler_verbosity = ProfilerVerbosity::NOTVERBOSE;
   }
 
   return profiler_verbosity == ProfilerVerbosity::VERBOSE;
 }
 
@@ -315,21 +326,21 @@ bool is_native_unwinding_avail() {
 // Read env vars at startup, so as to set:
 //   sUnwindInterval, sProfileEntries, sUnwindStackScan.
 void read_profiler_env_vars()
 {
   /* Set defaults */
   sUnwindInterval = 0;  /* We'll have to look elsewhere */
   sProfileEntries = 0;
 
-  const char* interval = PR_GetEnv(PROFILER_INTERVAL);
-  const char* entries = PR_GetEnv(PROFILER_ENTRIES);
-  const char* scanCount = PR_GetEnv(PROFILER_STACK);
+  const char* interval = getenv(PROFILER_INTERVAL);
+  const char* entries = getenv(PROFILER_ENTRIES);
+  const char* scanCount = getenv(PROFILER_STACK);
 
-  if (PR_GetEnv(PROFILER_HELP)) {
+  if (getenv(PROFILER_HELP)) {
      // Enable verbose output
      moz_profiler_set_verbosity(ProfilerVerbosity::VERBOSE);
      profiler_usage();
      // Now force the next enquiry of moz_profiler_verbose to re-query
      // env var MOZ_PROFILER_VERBOSE.
      moz_profiler_set_verbosity(ProfilerVerbosity::UNCHECKED);
   }
 
@@ -408,16 +419,17 @@ void set_tls_stack_top(void* stackTop)
 
 bool is_main_thread_name(const char* aName) {
   if (!aName) {
     return false;
   }
   return strcmp(aName, gGeckoThreadName) == 0;
 }
 
+#ifndef SPS_STANDALONE
 #ifdef HAVE_VA_COPY
 #define VARARGS_ASSIGN(foo, bar)        VA_COPY(foo,bar)
 #elif defined(HAVE_VA_LIST_AS_ARRAY)
 #define VARARGS_ASSIGN(foo, bar)     foo[0] = bar[0]
 #else
 #define VARARGS_ASSIGN(foo, bar)     (foo) = (bar)
 #endif
 
@@ -446,27 +458,32 @@ mozilla_sampler_log(const char *fmt, va_
       // EVENT_BACKTRACE could be used to get a source
       // for all log events. This could be a runtime
       // flag later.
       profiler_tracing("log", heapBuf, TRACING_EVENT);
       delete[] heapBuf;
     }
   }
 }
+#endif
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN externally visible functions
 
 void mozilla_sampler_init(void* stackTop)
 {
   sInitCount++;
 
   if (stack_key_initialized)
     return;
 
+#ifdef SPS_STANDALONE
+  mozilla::TimeStamp::Startup();
+#endif
+
   LOG("BEGIN mozilla_sampler_init");
   if (!tlsPseudoStack.init() || !tlsTicker.init() || !tlsStackTop.init()) {
     LOG("Failed to init.");
     return;
   }
   bool ignore;
   sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
 
@@ -484,22 +501,24 @@ void mozilla_sampler_init(void* stackTop
 
   // Read interval settings from MOZ_PROFILER_INTERVAL and stack-scan
   // threshhold from MOZ_PROFILER_STACK_SCAN.
   read_profiler_env_vars();
 
   // platform specific initialization
   OS::Startup();
 
+#ifndef SPS_STANDALONE
   set_stderr_callback(mozilla_sampler_log);
+#endif
 
   // We can't open pref so we use an environment variable
   // to know if we should trigger the profiler on startup
   // NOTE: Default
-  const char *val = PR_GetEnv("MOZ_PROFILER_STARTUP");
+  const char *val = getenv("MOZ_PROFILER_STARTUP");
   if (!val || !*val) {
     return;
   }
 
   const char* features[] = {"js"
                          , "leaf"
                          , "threads"
 #if defined(XP_WIN) || defined(XP_MACOSX) \
@@ -525,33 +544,39 @@ void mozilla_sampler_shutdown()
   sInitCount--;
 
   if (sInitCount > 0)
     return;
 
   // Save the profile on shutdown if requested.
   TableTicker *t = tlsTicker.get();
   if (t) {
-    const char *val = PR_GetEnv("MOZ_PROFILER_SHUTDOWN");
+    const char *val = getenv("MOZ_PROFILER_SHUTDOWN");
     if (val) {
       std::ofstream stream;
       stream.open(val);
       if (stream.is_open()) {
         t->ToStreamAsJSON(stream);
         stream.close();
       }
     }
   }
 
   profiler_stop();
 
+#ifndef SPS_STANDALONE
   set_stderr_callback(nullptr);
+#endif
 
   Sampler::Shutdown();
 
+#ifdef SPS_STANDALONE
+  mozilla::TimeStamp::Shutdown();
+#endif
+
   PseudoStack *stack = tlsPseudoStack.get();
   stack->deref();
   tlsPseudoStack.set(nullptr);
 }
 
 void mozilla_sampler_save()
 {
   TableTicker *t = tlsTicker.get();
@@ -570,17 +595,18 @@ mozilla::UniquePtr<char[]> mozilla_sampl
   TableTicker *t = tlsTicker.get();
   if (!t) {
     return nullptr;
   }
 
   return t->ToJSON(aSinceTime);
 }
 
-JSObject* mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime)
+#ifndef SPS_STANDALONE
+JSObject *mozilla_sampler_get_profile_data(JSContext *aCx, double aSinceTime)
 {
   TableTicker *t = tlsTicker.get();
   if (!t) {
     return nullptr;
   }
 
   return t->ToJSObject(aCx, aSinceTime);
 }
@@ -590,16 +616,17 @@ void mozilla_sampler_get_profile_data_as
 {
   TableTicker *t = tlsTicker.get();
   if (NS_WARN_IF(!t)) {
     return;
   }
 
   t->ToJSObjectAsync(aSinceTime, aPromise);
 }
+#endif
 
 void mozilla_sampler_save_profile_to_file(const char* aFilename)
 {
   TableTicker *t = tlsTicker.get();
   if (!t) {
     return;
   }
 
@@ -709,59 +736,64 @@ void mozilla_sampler_start(int aProfileE
   t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
                       aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
                       aFeatures, aFeatureCount,
                       aThreadNameFilters, aFilterCount);
 
   tlsTicker.set(t);
   t->Start();
   if (t->ProfileJS() || t->InPrivacyMode()) {
-      mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+      ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
       std::vector<ThreadInfo*> threads = t->GetRegisteredThreads();
 
       for (uint32_t i = 0; i < threads.size(); i++) {
         ThreadInfo* info = threads[i];
         if (info->IsPendingDelete()) {
           continue;
         }
         ThreadProfile* thread_profile = info->Profile();
         if (!thread_profile) {
           continue;
         }
         thread_profile->GetPseudoStack()->reinitializeOnResume();
+#ifndef SPS_STANDALONE
         if (t->ProfileJS()) {
           thread_profile->GetPseudoStack()->enableJSSampling();
         }
         if (t->InPrivacyMode()) {
           thread_profile->GetPseudoStack()->mPrivacyMode = true;
         }
+#endif
       }
   }
 
 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
   if (t->ProfileJava()) {
     int javaInterval = aInterval;
     // Java sampling doesn't accuratly keep up with 1ms sampling
     if (javaInterval < 10) {
       aInterval = 10;
     }
     mozilla::widget::GeckoJavaSampler::StartJavaProfiling(javaInterval, 1000);
   }
 #endif
 
+#ifndef SPS_STANDALONE
   if (t->AddMainThreadIO()) {
     if (!sInterposeObserver) {
       // Lazily create IO interposer observer
       sInterposeObserver = new mozilla::ProfilerIOInterposeObserver();
     }
     mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll,
                                     sInterposeObserver);
   }
+#endif
 
   sIsProfiling = true;
+#ifndef SPS_STANDALONE
   sIsGPUProfiling = t->ProfileGPU();
   sIsLayersDump = t->LayersDump();
   sIsDisplayListDump = t->DisplayListDump();
   sIsRestyleProfiling = t->ProfileRestyle();
 
   if (Sampler::CanNotifyObservers()) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
@@ -778,16 +810,17 @@ void mozilla_sampler_start(int aProfileE
 
       nsCOMPtr<nsIProfilerStartParams> params =
         new nsProfilerStartParams(aProfileEntries, aInterval, featuresArray,
                                   threadNameFiltersArray);
 
       os->NotifyObservers(params, "profiler-started", nullptr);
     }
   }
+#endif
 
   LOG("END   mozilla_sampler_start");
 }
 
 void mozilla_sampler_stop()
 {
   LOG("BEGIN mozilla_sampler_stop");
 
@@ -801,37 +834,41 @@ void mozilla_sampler_stop()
   }
 
   bool disableJS = t->ProfileJS();
 
   t->Stop();
   delete t;
   tlsTicker.set(nullptr);
 
+#ifndef SPS_STANDALONE
   if (disableJS) {
     PseudoStack *stack = tlsPseudoStack.get();
     ASSERT(stack != nullptr);
     stack->disableJSSampling();
   }
 
   mozilla::IOInterposer::Unregister(mozilla::IOInterposeObserver::OpAll,
                                     sInterposeObserver);
   sInterposeObserver = nullptr;
+#endif
 
   sIsProfiling = false;
+#ifndef SPS_STANDALONE
   sIsGPUProfiling = false;
   sIsLayersDump = false;
   sIsDisplayListDump = false;
   sIsRestyleProfiling = false;
 
   if (Sampler::CanNotifyObservers()) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os)
       os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
   }
+#endif
 
   LOG("END   mozilla_sampler_stop");
 }
 
 bool mozilla_sampler_is_paused() {
   if (Sampler::GetActiveSampler()) {
     return Sampler::GetActiveSampler()->IsPaused();
   } else {
@@ -889,26 +926,30 @@ void mozilla_sampler_responsiveness(cons
 void mozilla_sampler_frame_number(int frameNumber)
 {
   sFrameNumber = frameNumber;
 }
 
 void mozilla_sampler_lock()
 {
   profiler_stop();
+#ifndef SPS_STANDALONE
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os)
     os->NotifyObservers(nullptr, "profiler-locked", nullptr);
+#endif
 }
 
 void mozilla_sampler_unlock()
 {
+#ifndef SPS_STANDALONE
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os)
     os->NotifyObservers(nullptr, "profiler-unlocked", nullptr);
+#endif
 }
 
 bool mozilla_sampler_register_thread(const char* aName, void* stackTop)
 {
   if (sInitCount == 0) {
     return false;
   }
 
@@ -1021,17 +1062,17 @@ void mozilla_sampler_tracing(const char*
 {
   mozilla_sampler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData, aCause));
 }
 
 void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload)
 {
   // Note that aPayload may be allocated by the caller, so we need to make sure
   // that we free it at some point.
-  nsAutoPtr<ProfilerMarkerPayload> payload(aPayload);
+  mozilla::UniquePtr<ProfilerMarkerPayload> payload(aPayload);
 
   if (!stack_key_initialized)
     return;
 
   // Don't insert a marker if we're not profiling to avoid
   // the heap copy (malloc).
   if (!profiler_is_active()) {
     return;
@@ -1045,15 +1086,76 @@ void mozilla_sampler_add_marker(const ch
   PseudoStack *stack = tlsPseudoStack.get();
   if (!stack) {
     return;
   }
 
   mozilla::TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull()) ?
                      aPayload->GetStartTime() : mozilla::TimeStamp::Now();
   mozilla::TimeDuration delta = origin - sStartTime;
-  stack->addMarker(aMarker, payload.forget(), delta.ToMilliseconds());
+  stack->addMarker(aMarker, payload.release(), delta.ToMilliseconds());
+}
+
+#ifndef SPS_STANDALONE
+#include "mozilla/Mutex.h"
+
+class GeckoMutex : public ::Mutex {
+ public:
+  explicit GeckoMutex(const char* aDesc) :
+    mMutex(aDesc)
+  {}
+
+  virtual ~GeckoMutex() {}
+
+  virtual int Lock() {
+    mMutex.Lock();
+    return 0;
+  }
+
+  virtual int Unlock() {
+    mMutex.Unlock();
+    return 0;
+  }
+
+ private:
+  mozilla::Mutex mMutex;
+};
+
+mozilla::UniquePtr< ::Mutex> OS::CreateMutex(const char* aDesc) {
+  return mozilla::MakeUnique<GeckoMutex>(aDesc);
 }
 
+#else
+// Otherwise use c++11 Mutex
+#include <mutex>
+
+class OSXMutex : public ::Mutex {
+ public:
+  OSXMutex(const char* aDesc) :
+    mMutex()
+  {}
+
+  virtual ~OSXMutex() {}
+
+  virtual int Lock() {
+    mMutex.lock();
+    return 0;
+  }
+
+  virtual int Unlock() {
+    mMutex.unlock();
+    return 0;
+  }
+
+ private:
+  std::mutex mMutex;
+};
+
+mozilla::UniquePtr< ::Mutex> OS::CreateMutex(const char* aDesc) {
+  return mozilla::MakeUnique<GeckoMutex>(aDesc);
+}
+
+#endif
+
 // END externally visible functions
 ////////////////////////////////////////////////////////////////////////
 
 
--- a/tools/profiler/platform.h
+++ b/tools/profiler/platform.h
@@ -24,34 +24,42 @@
 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 // SUCH DAMAGE.
 
 #ifndef TOOLS_PLATFORM_H_
 #define TOOLS_PLATFORM_H_
 
+#ifdef SPS_STANDALONE
+#define MOZ_COUNT_CTOR(name)
+#define MOZ_COUNT_DTOR(name)
+#endif
+
 #ifdef ANDROID
 #include <android/log.h>
 #else
 #define __android_log_print(a, ...)
 #endif
 
 #ifdef XP_UNIX
 #include <pthread.h>
 #endif
 
 #include <stdint.h>
 #include <math.h>
+#ifndef SPS_STANDALONE
 #include "MainThreadUtils.h"
-#include "mozilla/unused.h"
+#include "mozilla/Mutex.h"
+#include "ThreadResponsiveness.h"
+#endif
 #include "mozilla/TimeStamp.h"
-#include "mozilla/Mutex.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/unused.h"
 #include "PlatformMacros.h"
-#include "ThreadResponsiveness.h"
 #include "v8-support.h"
 #include <vector>
 
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 #define ASSERT(a) MOZ_ASSERT(a)
@@ -106,20 +114,32 @@ class Mutex {
   // locked and owned by the calling thread, and immediately. If the mutex
   // is already locked by another thread, suspends the calling thread until
   // the mutex is unlocked.
   virtual int Lock() = 0;
 
   // Unlocks the given mutex. The mutex is assumed to be locked and owned by
   // the calling thread on entrance.
   virtual int Unlock() = 0;
+};
 
-  // Tries to lock the given mutex. Returns whether the mutex was
-  // successfully locked.
-  virtual bool TryLock() = 0;
+class MutexAutoLock {
+ public:
+  explicit MutexAutoLock(::Mutex& aMutex)
+    : mMutex(&aMutex)
+  {
+    mMutex->Lock();
+  }
+
+  ~MutexAutoLock() {
+    mMutex->Unlock();
+  }
+
+ private:
+  Mutex* mMutex;
 };
 
 // ----------------------------------------------------------------------------
 // OS
 //
 // This class has static methods for the different platform specific
 // functions. Add methods here to cope with differences between the
 // supported platforms.
@@ -131,16 +151,18 @@ class OS {
   static void Sleep(const int milliseconds);
 
   // Sleep for a number of microseconds.
   static void SleepMicro(const int microseconds);
 
   // Called on startup to initialize platform specific things
   static void Startup();
 
+  static mozilla::UniquePtr< ::Mutex> CreateMutex(const char* aDesc);
+
  private:
   static const int msPerSecond = 1000;
 
 };
 
 
 
 
@@ -346,17 +368,17 @@ class Sampler {
 
   static void Startup();
   // Should only be called on shutdown
   static void Shutdown();
 
   static TableTicker* GetActiveSampler() { return sActiveSampler; }
   static void SetActiveSampler(TableTicker* sampler) { sActiveSampler = sampler; }
 
-  static mozilla::Mutex* sRegisteredThreadsMutex;
+  static mozilla::UniquePtr<Mutex> sRegisteredThreadsMutex;
 
   static bool CanNotifyObservers() {
 #ifdef MOZ_WIDGET_GONK
     // We use profile.sh on b2g to manually select threads and options per process.
     return false;
 #elif defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
     // Android ANR reporter uses the profiler off the main thread
     return NS_IsMainThread();
@@ -409,29 +431,33 @@ class ThreadInfo {
 
   virtual void SetPendingDelete();
   bool IsPendingDelete() const { return mPendingDelete; }
 
 #ifdef MOZ_NUWA_PROCESS
   void SetThreadId(int aThreadId) { mThreadId = aThreadId; }
 #endif
 
+#ifndef SPS_STANDALONE
   /**
    * May be null for the main thread if the profiler was started during startup
    */
   nsIThread* GetThread() const { return mThread.get(); }
+#endif
  private:
   char* mName;
   int mThreadId;
   const bool mIsMainThread;
   PseudoStack* mPseudoStack;
   PlatformData* mPlatformData;
   ThreadProfile* mProfile;
   void* const mStackTop;
+#ifndef SPS_STANDALONE
   nsCOMPtr<nsIThread> mThread;
+#endif
   bool mPendingDelete;
 };
 
 // Just like ThreadInfo, but owns a reference to the PseudoStack.
 class StackOwningThreadInfo : public ThreadInfo {
  public:
   StackOwningThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack, void* aStackTop);
   virtual ~StackOwningThreadInfo();
--- a/tools/profiler/shared-libraries.h
+++ b/tools/profiler/shared-libraries.h
@@ -11,17 +11,19 @@
 #error This header does not have a useful implementation on your platform!
 #endif
 
 #include <algorithm>
 #include <vector>
 #include <string>
 #include <stdlib.h>
 #include <stdint.h>
+#ifndef SPS_STANDALONE
 #include <nsID.h>
+#endif
 
 class SharedLibrary {
 public:
 
   SharedLibrary(uintptr_t aStart,
                 uintptr_t aEnd,
                 uintptr_t aOffset,
                 const std::string& aBreakpadId,
--- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp
+++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp
@@ -7,38 +7,38 @@
 
 #include "ProfileEntry.h"
 
 // Make sure we can initialize our ThreadProfile
 TEST(ThreadProfile, Initialization) {
   PseudoStack* stack = PseudoStack::create();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  nsRefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
+  mozilla::RefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
   ThreadProfile tp(&info, pb);
 }
 
 // Make sure we can record one tag and read it
 TEST(ThreadProfile, InsertOneTag) {
   PseudoStack* stack = PseudoStack::create();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  nsRefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
+  mozilla::RefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
   pb->addTag(ProfileEntry('t', 123.1));
   ASSERT_TRUE(pb->mEntries != nullptr);
   ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagName == 't');
   ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagDouble == 123.1);
 }
 
 // See if we can insert some tags
 TEST(ThreadProfile, InsertTagsNoWrap) {
   PseudoStack* stack = PseudoStack::create();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  nsRefPtr<ProfileBuffer> pb = new ProfileBuffer(100);
+  mozilla::RefPtr<ProfileBuffer> pb = new ProfileBuffer(100);
   int test_size = 50;
   for (int i = 0; i < test_size; i++) {
     pb->addTag(ProfileEntry('t', i));
   }
   ASSERT_TRUE(pb->mEntries != nullptr);
   int readPos = pb->mReadPos;
   while (readPos != pb->mWritePos) {
     ASSERT_TRUE(pb->mEntries[readPos].mTagName == 't');
@@ -50,17 +50,17 @@ TEST(ThreadProfile, InsertTagsNoWrap) {
 // See if wrapping works as it should in the basic case
 TEST(ThreadProfile, InsertTagsWrap) {
   PseudoStack* stack = PseudoStack::create();
   Thread::tid_t tid = 1000;
   // we can fit only 24 tags in this buffer because of the empty slot
   int tags = 24;
   int buffer_size = tags + 1;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  nsRefPtr<ProfileBuffer> pb = new ProfileBuffer(buffer_size);
+  mozilla::RefPtr<ProfileBuffer> pb = new ProfileBuffer(buffer_size);
   int test_size = 43;
   for (int i = 0; i < test_size; i++) {
     pb->addTag(ProfileEntry('t', i));
   }
   ASSERT_TRUE(pb->mEntries != nullptr);
   int readPos = pb->mReadPos;
   int ctr = 0;
   while (readPos != pb->mWritePos) {