Bug 851748 - Merge TableTicker1+2. r=jseward
authorBenoit Girard <b56girard@gmail.com>
Mon, 25 Mar 2013 17:57:28 -0400
changeset 126168 f23a97b6dea106ea1aad187db9b04a747ffb2bdf
parent 126167 50beb409e8d0f8d071588e7baac154a538d2e549
child 126169 a90c916de9d3dcbbbc19beafd146828fee164168
push id25318
push userb56girard@gmail.com
push dateMon, 25 Mar 2013 21:58:16 +0000
treeherdermozilla-inbound@f23a97b6dea1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjseward
bugs851748
milestone22.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 851748 - Merge TableTicker1+2. r=jseward
tools/profiler/BreakpadSampler.cpp
tools/profiler/GeckoProfilerFunc.h
tools/profiler/GeckoProfilerImpl.h
tools/profiler/Makefile.in
tools/profiler/ProfileEntry.cpp
tools/profiler/ProfileEntry.h
tools/profiler/ProfileEntry2.cpp
tools/profiler/ProfileEntry2.h
tools/profiler/SaveProfileTask.h
tools/profiler/TableTicker.cpp
tools/profiler/TableTicker.h
tools/profiler/TableTicker2.cpp
tools/profiler/UnwinderThread2.cpp
tools/profiler/UnwinderThread2.h
tools/profiler/platform.cpp
tools/profiler/platform.h
rename from tools/profiler/TableTicker2.cpp
rename to tools/profiler/BreakpadSampler.cpp
--- a/tools/profiler/TableTicker2.cpp
+++ b/tools/profiler/BreakpadSampler.cpp
@@ -18,18 +18,20 @@
 #include "PlatformMacros.h"
 #include "GeckoProfilerImpl.h"
 #include "platform.h"
 #include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "shared-libraries.h"
 #include "mozilla/StackWalk.h"
-#include "ProfileEntry2.h"
+#include "ProfileEntry.h"
+#include "SaveProfileTask.h"
 #include "UnwinderThread2.h"
+#include "TableTicker.h"
 
 // JSON
 #include "JSObjectBuilder.h"
 #include "nsIJSRuntimeService.h"
 
 // Meta
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
@@ -42,403 +44,67 @@
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 
 // JS
 #include "jsdbgapi.h"
 
 // This file's exports are listed in GeckoProfilerImpl.h.
 
-// Pseudo backtraces are available on all platforms.  Native
-// backtraces are available only on selected platforms.  Breakpad is
-// the only supported native unwinder.  HAVE_NATIVE_UNWIND is set at
-// build time to indicate whether native unwinding is possible on this
-// platform.  The actual unwind mode currently in use is stored in
-// sUnwindMode.
-
-#undef HAVE_NATIVE_UNWIND
-#if defined(MOZ_PROFILING) \
-    && (defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
-        || defined(SPS_PLAT_x86_linux))
-# define HAVE_NATIVE_UNWIND
-#endif
-
-#if 0 && defined(MOZ_PROFILING) && !defined(SPS_PLAT_x86_windows)
-# warning MOZ_PROFILING
-#endif
-
-#if 0 && defined(HAVE_NATIVE_UNWIND)
-# warning HAVE_NATIVE_UNWIND
-#endif
-
-typedef  enum { UnwINVALID, UnwNATIVE, UnwPSEUDO, UnwCOMBINED }  UnwMode;
-
 /* These will be set to something sensible before we take the first
    sample. */
-static UnwMode sUnwindMode     = UnwINVALID;
-static int     sUnwindInterval = 0;
-
+UnwMode sUnwindMode     = UnwINVALID;
+int     sUnwindInterval = 0;
 
 using std::string;
 using namespace mozilla;
 
-#ifdef XP_WIN
- #include <windows.h>
- #define getpid GetCurrentProcessId
-#else
- #include <unistd.h>
-#endif
-
-#ifndef MAXPATHLEN
- #ifdef PATH_MAX
-  #define MAXPATHLEN PATH_MAX
- #elif defined(MAX_PATH)
-  #define MAXPATHLEN MAX_PATH
- #elif defined(_MAX_PATH)
-  #define MAXPATHLEN _MAX_PATH
- #elif defined(CCHMAXPATH)
-  #define MAXPATHLEN CCHMAXPATH
- #else
-  #define MAXPATHLEN 1024
- #endif
-#endif
-
 #if _MSC_VER
  #define snprintf _snprintf
 #endif
 
-class TableTicker2;
-
-mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
-static mozilla::ThreadLocal<TableTicker2 *> tlsTicker;
-// 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.
-bool stack_key_initialized;
-
-static TimeStamp sLastTracerEvent; // is raced on
-static int       sFrameNumber = 0;
-static int       sLastFrameNumber = 0;
-
-
-static bool
-hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature)
-{
-  for (size_t i = 0; i < aFeatureCount; i++) {
-    if (0) LOGF("hasFeature %s: %lu %s", aFeature, (unsigned long)i, aFeatures[i]);
-    if (strcmp(aFeatures[i], aFeature) == 0)
-      return true;
-  }
-  return false;
-}
-
-class TableTicker2: public Sampler {
- public:
-  TableTicker2(int aInterval, int aEntrySize, PseudoStack *aStack,
-              const char** aFeatures, uint32_t aFeatureCount)
-    : Sampler(aInterval, true)
-    , mPrimaryThreadProfile(aEntrySize, aStack)
-    , mStartTime(TimeStamp::Now())
-    , mSaveRequested(false)
-  {
-    mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
-
-    //XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
-    mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
-    mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
-    mPrimaryThreadProfile.addTag(ProfileEntry2('m', "Start"));
-  }
-
-  ~TableTicker2() { if (IsActive()) Stop(); }
-
-  virtual void SampleStack(TickSample* sample) {}
-
-  // Called within a signal. This function must be reentrant
-  virtual void Tick(TickSample* sample);
-
-  // Called within a signal. This function must be reentrant
-  virtual void RequestSave()
-  {
-    mSaveRequested = true;
-  }
-
-  virtual void HandleSaveRequest();
-
-  ThreadProfile2* GetPrimaryThreadProfile()
-  {
-    return &mPrimaryThreadProfile;
-  }
-
-  JSObject *ToJSObject(JSContext *aCx);
-  JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b);
-
-  const bool ProfileJS() { return mProfileJS; }
-
-private:
-  // Unwind, using a "native" backtrace scheme (non-PseudoStack).
-  // Not implemented on platforms which do not support native backtraces
-  void doNativeBacktrace(ThreadProfile2 &aProfile, TickSample* aSample);
-
-private:
-  // This represent the application's main thread (SAMPLER_INIT)
-  ThreadProfile2 mPrimaryThreadProfile;
-  TimeStamp mStartTime;
-  bool mSaveRequested;
-  bool mUseStackWalk;
-  bool mJankOnly;
-  bool mProfileJS;
-};
-
-
-////////////////////////////////////////////////////////////////////////
-// BEGIN SaveProfileTask et al
-
-std::string GetSharedLibraryInfoString();
-
-static JSBool
-WriteCallback(const jschar *buf, uint32_t len, void *data)
-{
-  std::ofstream& stream = *static_cast<std::ofstream*>(data);
-  nsAutoCString profile = NS_ConvertUTF16toUTF8(buf, len);
-  stream << profile.Data();
-  return JS_TRUE;
-}
-
-/**
- * This is an event used to save the profile on the main thread
- * to be sure that it is not being modified while saving.
- */
-class SaveProfileTask : public nsRunnable {
-public:
-  SaveProfileTask() {}
-
-  NS_IMETHOD Run() {
-    TableTicker2 *t = tlsTicker.get();
-
-    // Pause the profiler during saving.
-    // This will prevent us from recording sampling
-    // regarding profile saving. This will also
-    // prevent bugs caused by the circular buffer not
-    // being thread safe. Bug 750989.
-    t->SetPaused(true);
-
-    // Get file path
-#   if defined(SPS_PLAT_arm_android)
-    nsCString tmpPath;
-    tmpPath.AppendPrintf("/sdcard/profile_%i_%i.txt", XRE_GetProcessType(), getpid());
-#   else
-    nsCOMPtr<nsIFile> tmpFile;
-    nsAutoCString tmpPath;
-    if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)))) {
-      LOG("Failed to find temporary directory.");
-      return NS_ERROR_FAILURE;
-    }
-    tmpPath.AppendPrintf("profile_%i_%i.txt", XRE_GetProcessType(), getpid());
-
-    nsresult rv = tmpFile->AppendNative(tmpPath);
-    if (NS_FAILED(rv))
-      return rv;
-
-    rv = tmpFile->GetNativePath(tmpPath);
-    if (NS_FAILED(rv))
-      return rv;
-#   endif
-
-    // Create a JSContext to run a JSObjectBuilder :(
-    // Based on XPCShellEnvironment
-    JSRuntime *rt;
-    JSContext *cx;
-    nsCOMPtr<nsIJSRuntimeService> rtsvc 
-      = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
-    if (!rtsvc || NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
-      LOG("failed to get RuntimeService");
-      return NS_ERROR_FAILURE;;
-    }
-
-    cx = JS_NewContext(rt, 8192);
-    if (!cx) {
-      LOG("Failed to get context");
-      return NS_ERROR_FAILURE;
-    }
-
-    {
-      JSAutoRequest ar(cx);
-      static JSClass c = {
-          "global", JSCLASS_GLOBAL_FLAGS,
-          JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-          JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
-      };
-      JSObject *obj = JS_NewGlobalObject(cx, &c, NULL);
-
-      std::ofstream stream;
-      stream.open(tmpPath.get());
-      // Pause the profiler during saving.
-      // This will prevent us from recording sampling
-      // regarding profile saving. This will also
-      // prevent bugs caused by the circular buffer not
-      // being thread safe. Bug 750989.
-      t->SetPaused(true);
-      if (stream.is_open()) {
-        JSAutoCompartment autoComp(cx, obj);
-        JSObject* profileObj = mozilla_sampler_get_profile_data2(cx);
-        jsval val = OBJECT_TO_JSVAL(profileObj);
-        JS_Stringify(cx, &val, nullptr, JSVAL_NULL, WriteCallback, &stream);
-        stream.close();
-        LOGF("Saved to %s", tmpPath.get());
-      } else {
-        LOG("Fail to open profile log file.");
-      }
-    }
-    JS_EndRequest(cx);
-    JS_DestroyContext(cx);
-
-    t->SetPaused(false);
-
-    return NS_OK;
-  }
-};
-
-void TableTicker2::HandleSaveRequest()
-{
-  if (!mSaveRequested)
-    return;
-  mSaveRequested = false;
-
-  // TODO: Use use the ipc/chromium Tasks here to support processes
-  // without XPCOM.
-  nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
-  NS_DispatchToMainThread(runnable);
-}
-
-JSCustomObject* TableTicker2::GetMetaJSCustomObject(JSAObjectBuilder& b)
-{
-  JSCustomObject *meta = b.CreateObject();
-
-  b.DefineProperty(meta, "version", 2);
-  b.DefineProperty(meta, "interval", interval());
-  b.DefineProperty(meta, "stackwalk", mUseStackWalk);
-  b.DefineProperty(meta, "jank", mJankOnly);
-  b.DefineProperty(meta, "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))
-      b.DefineProperty(meta, "platform", string.Data());
-
-    res = http->GetOscpu(string);
-    if (!NS_FAILED(res))
-      b.DefineProperty(meta, "oscpu", string.Data());
-
-    res = http->GetMisc(string);
-    if (!NS_FAILED(res))
-      b.DefineProperty(meta, "misc", string.Data());
-  }
-
-  nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
-  if (runtime) {
-    nsAutoCString string;
-
-    res = runtime->GetXPCOMABI(string);
-    if (!NS_FAILED(res))
-      b.DefineProperty(meta, "abi", string.Data());
-
-    res = runtime->GetWidgetToolkit(string);
-    if (!NS_FAILED(res))
-      b.DefineProperty(meta, "toolkit", string.Data());
-  }
-
-  nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
-  if (appInfo) {
-    nsAutoCString string;
-
-    res = appInfo->GetName(string);
-    if (!NS_FAILED(res))
-      b.DefineProperty(meta, "product", string.Data());
-  }
-
-  return meta;
-}
-
-JSObject* TableTicker2::ToJSObject(JSContext *aCx)
-{
-  JSObjectBuilder b(aCx);
-
-  JSCustomObject *profile = b.CreateObject();
-
-  // Put shared library info
-  b.DefineProperty(profile, "libs", GetSharedLibraryInfoString().c_str());
-
-  // Put meta data
-  JSCustomObject *meta = GetMetaJSCustomObject(b);
-  b.DefineProperty(profile, "meta", meta);
-
-  // Lists the samples for each ThreadProfile2
-  JSCustomArray *threads = b.CreateArray();
-  b.DefineProperty(profile, "threads", threads);
-
-  // For now we only have one thread
-  SetPaused(true);
-  ThreadProfile2* prof = GetPrimaryThreadProfile();
-  prof->GetMutex()->Lock();
-  JSCustomObject* threadSamples = prof->ToJSObject(aCx);
-  b.ArrayPush(threads, threadSamples);
-  prof->GetMutex()->Unlock();
-  SetPaused(false);
-
-  return b.GetJSObject(profile);
-}
-
-// END SaveProfileTask et al
-////////////////////////////////////////////////////////////////////////
-
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN take samples.
 // Everything in this section RUNS IN SIGHANDLER CONTEXT
 
 // RUNS IN SIGHANDLER CONTEXT
 static
-void genProfileEntry2(/*MODIFIED*/UnwinderThreadBuffer* utb,
+void genProfileEntry(/*MODIFIED*/UnwinderThreadBuffer* utb,
                      volatile StackEntry &entry,
                      PseudoStack *stack, void *lastpc)
 {
   int lineno = -1;
 
   // Add a pseudostack-entry start label
-  utb__addEntry( utb, ProfileEntry2('h', 'P') );
+  utb__addEntry( utb, ProfileEntry('h', 'P') );
   // And the SP value, if it is non-zero
   if (entry.stackAddress() != 0) {
-    utb__addEntry( utb, ProfileEntry2('S', entry.stackAddress()) );
+    utb__addEntry( utb, ProfileEntry('S', entry.stackAddress()) );
   }
 
   // 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
 
-    utb__addEntry( utb, ProfileEntry2('c', "") );
+    utb__addEntry( utb, ProfileEntry('c', "") );
     // Add one to store the null termination
     size_t strLen = strlen(sampleLabel) + 1;
     for (size_t j = 0; j < strLen;) {
       // Store as many characters in the void* as the platform allows
       char text[sizeof(void*)];
       for (size_t pos = 0; pos < sizeof(void*) && j+pos < strLen; pos++) {
         text[pos] = sampleLabel[j+pos];
       }
       j += sizeof(void*)/sizeof(char);
       // Cast to *((void**) to pass the text data to a void*
-      utb__addEntry( utb, ProfileEntry2('d', *((void**)(&text[0]))) );
+      utb__addEntry( utb, ProfileEntry('d', *((void**)(&text[0]))) );
     }
     if (entry.js()) {
       if (!entry.pc()) {
         // The JIT only allows the top-most entry to have a NULL 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(),
@@ -449,64 +115,52 @@ void genProfileEntry2(/*MODIFIED*/Unwind
         }
       } else {
         lineno = JS_PCToLineNumber(NULL, entry.script(), entry.pc());
       }
     } else {
       lineno = entry.line();
     }
   } else {
-    utb__addEntry( utb, ProfileEntry2('c', sampleLabel) );
+    utb__addEntry( utb, ProfileEntry('c', sampleLabel) );
     lineno = entry.line();
   }
   if (lineno != -1) {
-    utb__addEntry( utb, ProfileEntry2('n', lineno) );
+    utb__addEntry( utb, ProfileEntry('n', lineno) );
   }
 
   // Add a pseudostack-entry end label
-  utb__addEntry( utb, ProfileEntry2('h', 'Q') );
+  utb__addEntry( utb, ProfileEntry('h', 'Q') );
 }
 
 // RUNS IN SIGHANDLER CONTEXT
 // Generate pseudo-backtrace entries and put them in |utb|, with
 // the order outermost frame first.
 void genPseudoBacktraceEntries(/*MODIFIED*/UnwinderThreadBuffer* utb,
                                PseudoStack *aStack, TickSample *sample)
 {
-  // Call genProfileEntry2 to generate tags for each profile
+  // Call genProfileEntry to generate tags for each profile
   // entry.  Each entry will be bounded by a 'h' 'P' tag to
   // mark the start and a 'h' 'Q' tag to mark the end.
   uint32_t nInStack = aStack->stackSize();
   for (uint32_t i = 0; i < nInStack; i++) {
-    genProfileEntry2(utb, aStack->mStack[i], aStack, nullptr);
+    genProfileEntry(utb, aStack->mStack[i], aStack, nullptr);
   }
 # ifdef ENABLE_SPS_LEAF_DATA
   if (sample) {
-    utb__addEntry( utb, ProfileEntry2('l', (void*)sample->pc) );
+    utb__addEntry( utb, ProfileEntry('l', (void*)sample->pc) );
 #   ifdef ENABLE_ARM_LR_SAVING
-    utb__addEntry( utb, ProfileEntry2('L', (void*)sample->lr) );
+    utb__addEntry( utb, ProfileEntry('L', (void*)sample->lr) );
 #   endif
   }
 # endif
 }
 
-/* used to keep track of the last event that we sampled during */
-static unsigned int sLastSampledEventGeneration = 0;
-
-/* a counter that's incremented everytime we get responsiveness event
- * note: it might also be worth tracking everytime we go around
- * the event loop */
-static unsigned int sCurrentEventGeneration = 0;
-/* we don't need to worry about overflow because we only treat the
- * case of them being the same as special. i.e. we only run into
- * a problem if 2^32 events happen between samples that we need
- * to know are associated with different events */
-
 // RUNS IN SIGHANDLER CONTEXT
-void TableTicker2::Tick(TickSample* sample)
+void BreakpadSampler::Tick(TickSample* sample)
 {
   /* Get hold of an empty inter-thread buffer into which to park
      the ProfileEntries for this sample. */
   UnwinderThreadBuffer* utb = uwt__acquire_empty_buffer();
 
   /* This could fail, if no buffers are currently available, in which
      case we must give up right away.  We cannot wait for a buffer to
      become available, as that risks deadlock. */
@@ -514,17 +168,17 @@ void TableTicker2::Tick(TickSample* samp
     return;
 
   /* Manufacture the ProfileEntries that we will give to the unwinder
      thread, and park them in |utb|. */
 
   // Marker(s) come before the sample
   PseudoStack* stack = mPrimaryThreadProfile.GetPseudoStack();
   for (int i = 0; stack->getMarker(i) != NULL; i++) {
-    utb__addEntry( utb, ProfileEntry2('m', stack->getMarker(i)) );
+    utb__addEntry( utb, ProfileEntry('m', stack->getMarker(i)) );
   }
   stack->mQueueClearMarker = true;
 
   bool recordSample = true;
   if (mJankOnly) {
     // if we are on a different event we can discard any temporary samples
     // we've kept around
     if (sLastSampledEventGeneration != sCurrentEventGeneration) {
@@ -551,49 +205,49 @@ void TableTicker2::Tick(TickSample* samp
   // settings in sUnwindMode and sUnwindInterval.
   // Add a native-backtrace request, or add pseudo backtrace entries,
   // or both.
   switch (sUnwindMode) {
     case UnwNATIVE: /* Native only */
       // add a "do native stack trace now" hint.  This will be actioned
       // by the unwinder thread as it processes the entries in this
       // sample.
-      utb__addEntry( utb, ProfileEntry2('h'/*hint*/, 'N'/*native-trace*/) );
+      utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'N'/*native-trace*/) );
       break;
     case UnwPSEUDO: /* Pseudo only */
       /* Add into |utb|, the pseudo backtrace entries */
       genPseudoBacktraceEntries(utb, stack, sample);
       break;
     case UnwCOMBINED: /* Both Native and Pseudo */
-      utb__addEntry( utb, ProfileEntry2('h'/*hint*/, 'N'/*native-trace*/) );
+      utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'N'/*native-trace*/) );
       genPseudoBacktraceEntries(utb, stack, sample);
       break;
     case UnwINVALID:
     default:
       MOZ_CRASH();
   }
 
   if (recordSample) {    
     // add a "flush now" hint
-    utb__addEntry( utb, ProfileEntry2('h'/*hint*/, 'F'/*flush*/) );
+    utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'F'/*flush*/) );
   }
 
   // Add any extras
   if (!sLastTracerEvent.IsNull() && sample) {
     TimeDuration delta = sample->timestamp - sLastTracerEvent;
-    utb__addEntry( utb, ProfileEntry2('r', delta.ToMilliseconds()) );
+    utb__addEntry( utb, ProfileEntry('r', delta.ToMilliseconds()) );
   }
 
   if (sample) {
     TimeDuration delta = sample->timestamp - mStartTime;
-    utb__addEntry( utb, ProfileEntry2('t', delta.ToMilliseconds()) );
+    utb__addEntry( utb, ProfileEntry('t', delta.ToMilliseconds()) );
   }
 
   if (sLastFrameNumber != sFrameNumber) {
-    utb__addEntry( utb, ProfileEntry2('f', sFrameNumber) );
+    utb__addEntry( utb, ProfileEntry('f', sFrameNumber) );
     sLastFrameNumber = sFrameNumber;
   }
 
   /* So now we have, in |utb|, the complete set of entries we want to
      push into the circular buffer.  This may also include a 'h' 'F'
      entry, which is "flush now" hint, and/or a 'h' 'N' entry, which
      is a "generate a native backtrace and add it to the buffer right
      now" hint.  Hand them off to the helper thread, together with
@@ -642,366 +296,8 @@ void TableTicker2::Tick(TickSample* samp
   } else {
     uwt__release_full_buffer(&mPrimaryThreadProfile, utb, NULL);
   }
 }
 
 // END take samples
 ////////////////////////////////////////////////////////////////////////
 
-
-std::ostream& operator<<(std::ostream& stream, const ThreadProfile2& profile)
-{
-  int readPos = profile.mReadPos;
-  while (readPos != profile.mLastFlushPos) {
-    stream << profile.mEntries[readPos];
-    readPos = (readPos + 1) % profile.mEntrySize;
-  }
-  return stream;
-}
-
-std::ostream& operator<<(std::ostream& stream, const ProfileEntry2& entry)
-{
-  if (entry.mTagName == 'r' || entry.mTagName == 't') {
-    stream << entry.mTagName << "-" << std::fixed << entry.mTagFloat << "\n";
-  } else if (entry.mTagName == 'l' || entry.mTagName == 'L') {
-    // Bug 739800 - Force l-tag addresses to have a "0x" prefix on all platforms
-    // Additionally, stringstream seemed to be ignoring formatter flags.
-    char tagBuff[1024];
-    unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
-    snprintf(tagBuff, 1024, "%c-%#llx\n", entry.mTagName, pc);
-    stream << tagBuff;
-  } else if (entry.mTagName == 'd') {
-    // TODO implement 'd' tag for text profile
-  } else {
-    stream << entry.mTagName << "-" << entry.mTagData << "\n";
-  }
-  return stream;
-}
-
-static inline const char* name_UnwMode(UnwMode m)
-{
-  switch (m) {
-    case UnwINVALID:  return "invalid";
-    case UnwNATIVE:   return "native";
-    case UnwPSEUDO:   return "pseudo";
-    case UnwCOMBINED: return "combined";
-    default:          return "??name_UnwMode??";
-  }
-}
-
-// Read env vars at startup, so as to set sUnwindMode and sInterval.
-static void read_env_vars()
-{
-  bool nativeAvail = false;
-# if defined(HAVE_NATIVE_UNWIND)
-  nativeAvail = true;
-# endif
-
-  MOZ_ASSERT(sUnwindMode     == UnwINVALID);
-  MOZ_ASSERT(sUnwindInterval == 0);
-
-  /* Set defaults */
-  sUnwindMode     = nativeAvail ? UnwCOMBINED : UnwPSEUDO;
-  sUnwindInterval = 0;  /* We'll have to look elsewhere */
-
-  const char* strM = PR_GetEnv("MOZ_PROFILER_MODE");
-  const char* strI = PR_GetEnv("MOZ_PROFILER_INTERVAL");
-
-  if (strM) {
-    if (0 == strcmp(strM, "pseudo"))
-      sUnwindMode = UnwPSEUDO;
-    else if (0 == strcmp(strM, "native") && nativeAvail)
-      sUnwindMode = UnwNATIVE;
-    else if (0 == strcmp(strM, "combined") && nativeAvail)
-      sUnwindMode = UnwCOMBINED;
-    else goto usage;
-  }
-
-  if (strI) {
-    errno = 0;
-    long int n = strtol(strI, (char**)NULL, 10);
-    if (errno == 0 && n >= 1 && n <= 1000) {
-      sUnwindInterval = n;
-    }
-    else goto usage;
-  }
-
-  goto out;
-
- usage:
-  LOG( "SPS: ");
-  LOG( "SPS: Environment variable usage:");
-  LOG( "SPS: ");
-  LOG( "SPS:   MOZ_PROFILER_MODE=native    for native unwind only");
-  LOG( "SPS:   MOZ_PROFILER_MODE=pseudo    for pseudo unwind only");
-  LOG( "SPS:   MOZ_PROFILER_MODE=combined  for combined native & pseudo unwind");
-  LOG( "SPS:   If unset, default is 'combined' on native-capable");
-  LOG( "SPS:     platforms, 'pseudo' on others.");
-  LOG( "SPS: ");
-  LOG( "SPS:   MOZ_PROFILER_INTERVAL=<number>   (milliseconds, 1 to 1000)");
-  LOG( "SPS:   If unset, platform default is used.");
-  LOG( "SPS: ");
-  LOGF("SPS:   This platform %s native unwinding.",
-       nativeAvail ? "supports" : "does not support");
-  LOG( "SPS: ");
-  /* Re-set defaults */
-  sUnwindMode       = nativeAvail ? UnwCOMBINED : UnwPSEUDO;
-  sUnwindInterval   = 0;  /* We'll have to look elsewhere */
-
- out:
-  LOG( "SPS:");
-  LOGF("SPS: Unwind mode       = %s", name_UnwMode(sUnwindMode));
-  LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")",
-       (int)sUnwindInterval);
-  LOG( "SPS: Use env var MOZ_PROFILER_MODE=help for further information.");
-  LOG( "SPS:");
-
-  return;
-}
-
-
-////////////////////////////////////////////////////////////////////////
-// BEGIN externally visible functions
-
-void mozilla_sampler_init2()
-{
-  if (stack_key_initialized)
-    return;
-
-  LOG("BEGIN mozilla_sampler_init2");
-  if (!tlsPseudoStack.init() || !tlsTicker.init()) {
-    LOG("Failed to init.");
-    return;
-  }
-  stack_key_initialized = true;
-
-  PseudoStack *stack = new PseudoStack();
-  tlsPseudoStack.set(stack);
-
-  // Read mode settings from MOZ_PROFILER_MODE and interval
-  // settings from MOZ_PROFILER_INTERVAL.
-  read_env_vars();
-
-  // Create the unwinder thread.  ATM there is only one.
-  uwt__init();
-
-# if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
-     || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android) \
-     || defined(SPS_PLAT_x86_windows) || defined(SPS_PLAT_amd64_windows) /* no idea if windows is correct */
-  // On Linuxes, register this thread (temporarily) for profiling
-  int aLocal;
-  uwt__register_thread_for_profiling( &aLocal );
-# elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin)
-  // Registration is done in platform-macos.cc
-# else
-#   error "Unknown plat"
-# endif
-
-  // Allow the profiler to be started using signals
-  OS::RegisterStartHandler();
-
-  // 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");
-  if (!val || !*val) {
-    return;
-  }
-
-  const char* features[] = {"js"
-#if defined(XP_WIN) || defined(XP_MACOSX)
-                         , "stackwalk"
-#endif
-                         };
-  mozilla_sampler_start2(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
-                         features, sizeof(features)/sizeof(const char*));
-  LOG("END   mozilla_sampler_init2");
-}
-
-void mozilla_sampler_shutdown2()
-{
-  // Shut down and reap the unwinder thread.  We have to do this
-  // before stopping the sampler, so as to guarantee that the unwinder
-  // thread doesn't try to access memory that the subsequent call to
-  // mozilla_sampler_stop2 causes to be freed.
-  uwt__deinit();
-
-  mozilla_sampler_stop2();
-  // We can't delete the Stack because we can be between a
-  // sampler call_enter/call_exit point.
-  // TODO Need to find a safe time to delete Stack
-}
-
-void mozilla_sampler_save2()
-{
-  TableTicker2 *t = tlsTicker.get();
-  if (!t) {
-    return;
-  }
-
-  t->RequestSave();
-  // We're on the main thread already so we don't
-  // have to wait to handle the save request.
-  t->HandleSaveRequest();
-}
-
-char* mozilla_sampler_get_profile2()
-{
-  TableTicker2 *t = tlsTicker.get();
-  if (!t) {
-    return NULL;
-  }
-
-  std::stringstream profile;
-  t->SetPaused(true);
-  profile << *(t->GetPrimaryThreadProfile());
-  t->SetPaused(false);
-
-  std::string profileString = profile.str();
-  char *rtn = (char*)malloc( (profileString.length() + 1) * sizeof(char) );
-  strcpy(rtn, profileString.c_str());
-  return rtn;
-}
-
-JSObject *mozilla_sampler_get_profile_data2(JSContext *aCx)
-{
-  TableTicker2 *t = tlsTicker.get();
-  if (!t) {
-    return NULL;
-  }
-
-  return t->ToJSObject(aCx);
-}
-
-
-const char** mozilla_sampler_get_features2()
-{
-  static const char* features[] = {
-#if defined(MOZ_PROFILING) && defined(HAVE_NATIVE_UNWIND)
-    "stackwalk",
-#endif
-    "jank",
-    "js",
-    NULL
-  };
-
-  return features;
-}
-
-// Values are only honored on the first start
-void mozilla_sampler_start2(int aProfileEntries, int aInterval,
-                            const char** aFeatures, uint32_t aFeatureCount)
-{
-  if (!stack_key_initialized)
-    mozilla_sampler_init2();
-
-  /* If the sampling interval was set using env vars, use that
-     in preference to anything else. */
-  if (sUnwindInterval > 0)
-    aInterval = sUnwindInterval;
-
-  PseudoStack *stack = tlsPseudoStack.get();
-  if (!stack) {
-    ASSERT(false);
-    return;
-  }
-
-  mozilla_sampler_stop2();
-  TableTicker2 *t
-    = new TableTicker2(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
-                      aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
-                      stack, aFeatures, aFeatureCount);
-  tlsTicker.set(t);
-  t->Start();
-  if (t->ProfileJS())
-      stack->enableJSSampling();
-
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os)
-    os->NotifyObservers(nullptr, "profiler-started", nullptr);
-}
-
-void mozilla_sampler_stop2()
-{
-  if (!stack_key_initialized)
-    mozilla_sampler_init2();
-
-  TableTicker2 *t = tlsTicker.get();
-  if (!t) {
-    return;
-  }
-
-  bool disableJS = t->ProfileJS();
-
-  t->Stop();
-  delete t;
-  tlsTicker.set(NULL);
-  PseudoStack *stack = tlsPseudoStack.get();
-  ASSERT(stack != NULL);
-
-  if (disableJS)
-    stack->disableJSSampling();
-
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os)
-    os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
-}
-
-bool mozilla_sampler_is_active2()
-{
-  if (!stack_key_initialized)
-    mozilla_sampler_init2();
-
-  TableTicker2 *t = tlsTicker.get();
-  if (!t) {
-    return false;
-  }
-
-  return t->IsActive();
-}
-
-static double sResponsivenessTimes[100];
-static unsigned int sResponsivenessLoc = 0;
-void mozilla_sampler_responsiveness2(const TimeStamp& aTime)
-{
-  if (!sLastTracerEvent.IsNull()) {
-    if (sResponsivenessLoc == 100) {
-      for(size_t i = 0; i < 100-1; i++) {
-        sResponsivenessTimes[i] = sResponsivenessTimes[i+1];
-      }
-      sResponsivenessLoc--;
-    }
-    TimeDuration delta = aTime - sLastTracerEvent;
-    sResponsivenessTimes[sResponsivenessLoc++] = delta.ToMilliseconds();
-  }
-  sCurrentEventGeneration++;
-
-  sLastTracerEvent = aTime;
-}
-
-const double* mozilla_sampler_get_responsiveness2()
-{
-  return sResponsivenessTimes;
-}
-
-void mozilla_sampler_frame_number2(int frameNumber)
-{
-  sFrameNumber = frameNumber;
-}
-
-void mozilla_sampler_print_location2()
-{
-  // FIXME
-}
-
-void mozilla_sampler_lock2()
-{
-  // FIXME!
-}
-
-void mozilla_sampler_unlock2()
-{
-  // FIXME!
-}
-
-// END externally visible functions
-////////////////////////////////////////////////////////////////////////
--- a/tools/profiler/GeckoProfilerFunc.h
+++ b/tools/profiler/GeckoProfilerFunc.h
@@ -16,64 +16,49 @@ using mozilla::TimeDuration;
 
 // Returns a handle to pass on exit. This can check that we are popping the
 // correct callstack.
 inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress = NULL,
                                         bool aCopy = false, uint32_t line = 0);
 inline void  mozilla_sampler_call_exit(void* handle);
 inline void  mozilla_sampler_add_marker(const char *aInfo);
 
-void mozilla_sampler_start1(int aEntries, int aInterval, const char** aFeatures,
-                            uint32_t aFeatureCount);
-void mozilla_sampler_start2(int aEntries, int aInterval, const char** aFeatures,
+void mozilla_sampler_start(int aEntries, int aInterval, const char** aFeatures,
                             uint32_t aFeatureCount);
 
-void mozilla_sampler_stop1();
-void mozilla_sampler_stop2();
+void mozilla_sampler_stop();
 
-bool mozilla_sampler_is_active1();
-bool mozilla_sampler_is_active2();
+bool mozilla_sampler_is_active();
 
-void mozilla_sampler_responsiveness1(const TimeStamp& time);
-void mozilla_sampler_responsiveness2(const TimeStamp& time);
+void mozilla_sampler_responsiveness(const TimeStamp& time);
 
-void mozilla_sampler_frame_number1(int frameNumber);
-void mozilla_sampler_frame_number2(int frameNumber);
+void mozilla_sampler_frame_number(int frameNumber);
 
-const double* mozilla_sampler_get_responsiveness1();
-const double* mozilla_sampler_get_responsiveness2();
+const double* mozilla_sampler_get_responsiveness();
 
-void mozilla_sampler_save1();
-void mozilla_sampler_save2();
+void mozilla_sampler_save();
 
-char* mozilla_sampler_get_profile1();
-char* mozilla_sampler_get_profile2();
+char* mozilla_sampler_get_profile();
 
-JSObject *mozilla_sampler_get_profile_data1(JSContext *aCx);
-JSObject *mozilla_sampler_get_profile_data2(JSContext *aCx);
+JSObject *mozilla_sampler_get_profile_data(JSContext *aCx);
 
-const char** mozilla_sampler_get_features1();
-const char** mozilla_sampler_get_features2();
+const char** mozilla_sampler_get_features();
 
-void mozilla_sampler_init1();
-void mozilla_sampler_init2();
+void mozilla_sampler_init();
 
-void mozilla_sampler_shutdown1();
-void mozilla_sampler_shutdown2();
+void mozilla_sampler_shutdown();
 
 void mozilla_sampler_print_location1();
 void mozilla_sampler_print_location2();
 
 // Lock the profiler. When locked the profiler is (1) stopped,
 // (2) profile data is cleared, (3) profiler-locked is fired.
 // This is used to lock down the profiler during private browsing
-void mozilla_sampler_lock1();
-void mozilla_sampler_lock2();
+void mozilla_sampler_lock();
 
 // Unlock the profiler, leaving it stopped and fires profiler-unlocked.
-void mozilla_sampler_unlock1();
-void mozilla_sampler_unlock2();
+void mozilla_sampler_unlock();
 
 /* Returns true if env var SPS_NEW is set to anything, else false. */
 extern bool sps_version2();
 
 #endif
 
--- a/tools/profiler/GeckoProfilerImpl.h
+++ b/tools/profiler/GeckoProfilerImpl.h
@@ -33,167 +33,116 @@
 #undef min
 #endif
 
 struct PseudoStack;
 class TableTicker;
 class JSCustomObject;
 
 extern mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
+extern mozilla::ThreadLocal<TableTicker *> tlsTicker;
 extern bool stack_key_initialized;
 
 #ifndef SAMPLE_FUNCTION_NAME
 # ifdef __GNUC__
 #  define SAMPLE_FUNCTION_NAME __FUNCTION__
 # elif defined(_MSC_VER)
 #  define SAMPLE_FUNCTION_NAME __FUNCTION__
 # else
 #  define SAMPLE_FUNCTION_NAME __func__  // defined in C99, supported in various C++ compilers. Just raw function name.
 # endif
 #endif
 
 static inline
 void profiler_init()
 {
-  if (!sps_version2()) {
-    mozilla_sampler_init1();
-  } else {
-    mozilla_sampler_init2();
-  }
+  mozilla_sampler_init();
 }
 
 static inline
 void profiler_shutdown()
 {
-  if (!sps_version2()) {
-    mozilla_sampler_shutdown1();
-  } else {
-    mozilla_sampler_shutdown2();
-  }
+  mozilla_sampler_shutdown();
 }
 
 static inline
 void profiler_start(int aProfileEntries, int aInterval,
                        const char** aFeatures, uint32_t aFeatureCount)
 {
-  if (!sps_version2()) {
-    mozilla_sampler_start1(aProfileEntries, aInterval, aFeatures, aFeatureCount);
-  } else {
-    mozilla_sampler_start2(aProfileEntries, aInterval, aFeatures, aFeatureCount);
-  }
+  mozilla_sampler_start(aProfileEntries, aInterval, aFeatures, aFeatureCount);
 }
 
 static inline
 void profiler_stop()
 {
-  if (!sps_version2()) {
-    mozilla_sampler_stop1();
-  } else {
-    mozilla_sampler_stop2();
-  }
+  mozilla_sampler_stop();
 }
 
 static inline
 bool profiler_is_active()
 {
-  if (!sps_version2()) {
-    return mozilla_sampler_is_active1();
-  } else {
-    return mozilla_sampler_is_active2();
-  }
+  return mozilla_sampler_is_active();
 }
 
 static inline
 void profiler_responsiveness(const TimeStamp& aTime)
 {
-  if (!sps_version2()) {
-    mozilla_sampler_responsiveness1(aTime);
-  } else {
-    mozilla_sampler_responsiveness2(aTime);
-  }
+  mozilla_sampler_responsiveness(aTime);
 }
 
 static inline
 const double* profiler_get_responsiveness()
 {
-  if (!sps_version2()) {
-    return mozilla_sampler_get_responsiveness1();
-  } else {
-    return mozilla_sampler_get_responsiveness2();
-  }
+  return mozilla_sampler_get_responsiveness();
 }
 
 static inline
 void profiler_set_frame_number(int frameNumber)
 {
-  if (!sps_version2()) {
-    return mozilla_sampler_frame_number1(frameNumber);
-  } else {
-    return mozilla_sampler_frame_number2(frameNumber);
-  }
+  return mozilla_sampler_frame_number(frameNumber);
 }
 
 static inline
 char* profiler_get_profile()
 {
-  if (!sps_version2()) {
-    return mozilla_sampler_get_profile1();
-  } else {
-    return mozilla_sampler_get_profile2();
-  }
+  return mozilla_sampler_get_profile();
 }
 
 static inline
 JSObject* profiler_get_profile_jsobject(JSContext* aCx)
 {
-  if (!sps_version2()) {
-    return mozilla_sampler_get_profile_data1(aCx);
-  } else {
-    return mozilla_sampler_get_profile_data2(aCx);
-  }
+  return mozilla_sampler_get_profile_data(aCx);
 }
 
 static inline
 const char** profiler_get_features()
 {
-  if (!sps_version2()) {
-    return mozilla_sampler_get_features1();
-  } else {
-    return mozilla_sampler_get_features2();
-  }
+  return mozilla_sampler_get_features();
 }
 
 static inline
 void profiler_print_location()
 {
   if (!sps_version2()) {
     return mozilla_sampler_print_location1();
   } else {
     return mozilla_sampler_print_location2();
   }
 }
 
 static inline
 void profiler_lock()
 {
-  if (!sps_version2()) {
-    return mozilla_sampler_lock1();
-  } else {
-    return mozilla_sampler_lock2();
-  }
+  return mozilla_sampler_lock();
 }
 
 static inline
 void profiler_unlock()
 {
-  if (!sps_version2()) {
-    return mozilla_sampler_unlock1();
-  } else {
-    return mozilla_sampler_unlock2();
-  }
+  return mozilla_sampler_unlock();
 }
 
 // we want the class and function name but can't easily get that using preprocessor macros
 // __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
 
 #define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line
 #define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line)
 #define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__)
--- a/tools/profiler/Makefile.in
+++ b/tools/profiler/Makefile.in
@@ -39,22 +39,23 @@ LIBRARY_NAME    = profiler
 EXPORT_LIBRARY  = 1
 LIBXUL_LIBRARY  = 1
 IS_COMPONENT    = 1
 ifndef _MSC_VER
 FAIL_ON_WARNINGS = 1
 endif # !_MSC_VER
 
 CPPSRCS		= \
+  platform.cpp \
   nsProfilerFactory.cpp \
   nsProfiler.cpp \
   TableTicker.cpp \
-  TableTicker2.cpp \
+  BreakpadSampler.cpp \
   UnwinderThread2.cpp \
-  ProfileEntry2.cpp \
+  ProfileEntry.cpp \
   local_debug_info_symbolizer.cc \
   JSObjectBuilder.cpp \
   JSCustomObjectBuilder.cpp \
   $(NULL)
 
 EXTRA_JS_MODULES = \
   Profiler.jsm \
   $(NULL)
rename from tools/profiler/ProfileEntry2.cpp
rename to tools/profiler/ProfileEntry.cpp
--- a/tools/profiler/ProfileEntry2.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -5,86 +5,87 @@
 
 #include <iostream>
 #include "GeckoProfilerImpl.h"
 #include "platform.h"
 #include "nsThreadUtils.h"
 
 // JSON
 #include "JSObjectBuilder.h"
+#include "JSCustomObjectBuilder.h"
 
 // Self
-#include "ProfileEntry2.h"
+#include "ProfileEntry.h"
 
 #if _MSC_VER
  #define snprintf _snprintf
 #endif
 
 ////////////////////////////////////////////////////////////////////////
-// BEGIN ProfileEntry2
+// BEGIN ProfileEntry
 
-ProfileEntry2::ProfileEntry2()
+ProfileEntry::ProfileEntry()
   : mTagData(NULL)
   , mTagName(0)
 { }
 
 // aTagData must not need release (i.e. be a string from the text segment)
-ProfileEntry2::ProfileEntry2(char aTagName, const char *aTagData)
+ProfileEntry::ProfileEntry(char aTagName, const char *aTagData)
   : mTagData(aTagData)
   , mTagName(aTagName)
 { }
 
-ProfileEntry2::ProfileEntry2(char aTagName, void *aTagPtr)
+ProfileEntry::ProfileEntry(char aTagName, void *aTagPtr)
   : mTagPtr(aTagPtr)
   , mTagName(aTagName)
 { }
 
-ProfileEntry2::ProfileEntry2(char aTagName, double aTagFloat)
+ProfileEntry::ProfileEntry(char aTagName, double aTagFloat)
   : mTagFloat(aTagFloat)
   , mTagName(aTagName)
 { }
 
-ProfileEntry2::ProfileEntry2(char aTagName, uintptr_t aTagOffset)
+ProfileEntry::ProfileEntry(char aTagName, uintptr_t aTagOffset)
   : mTagOffset(aTagOffset)
   , mTagName(aTagName)
 { }
 
-ProfileEntry2::ProfileEntry2(char aTagName, Address aTagAddress)
+ProfileEntry::ProfileEntry(char aTagName, Address aTagAddress)
   : mTagAddress(aTagAddress)
   , mTagName(aTagName)
 { }
 
-ProfileEntry2::ProfileEntry2(char aTagName, int aTagLine)
+ProfileEntry::ProfileEntry(char aTagName, int aTagLine)
   : mTagLine(aTagLine)
   , mTagName(aTagName)
 { }
 
-ProfileEntry2::ProfileEntry2(char aTagName, char aTagChar)
+ProfileEntry::ProfileEntry(char aTagName, char aTagChar)
   : mTagChar(aTagChar)
   , mTagName(aTagName)
 { }
 
-bool ProfileEntry2::is_ent_hint(char hintChar) {
+bool ProfileEntry::is_ent_hint(char hintChar) {
   return mTagName == 'h' && mTagChar == hintChar;
 }
 
-bool ProfileEntry2::is_ent_hint() {
+bool ProfileEntry::is_ent_hint() {
   return mTagName == 'h';
 }
 
-bool ProfileEntry2::is_ent(char tagChar) {
+bool ProfileEntry::is_ent(char tagChar) {
   return mTagName == tagChar;
 }
 
-void* ProfileEntry2::get_tagPtr() {
+void* ProfileEntry::get_tagPtr() {
   // No consistency checking.  Oh well.
   return mTagPtr;
 }
 
-void ProfileEntry2::log()
+void ProfileEntry::log()
 {
   // There is no compiler enforced mapping between tag chars
   // and union variant fields, so the following was derived
   // by looking through all the use points of TableTicker.cpp.
   //   mTagData   (const char*)  m,c,s
   //   mTagPtr    (void*)        d,l,L, S(start-of-stack)
   //   mTagLine   (int)          n,f
   //   mTagChar   (char)         h
@@ -100,61 +101,79 @@ void ProfileEntry2::log()
       LOGF("%c \'%c\'", mTagName, mTagChar); break;
     case 'r': case 't':
       LOGF("%c %f", mTagName, mTagFloat); break;
     default:
       LOGF("'%c' unknown_tag", mTagName); break;
   }
 }
 
-// END ProfileEntry2
+std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry)
+{
+  if (entry.mTagName == 'r' || entry.mTagName == 't') {
+    stream << entry.mTagName << "-" << std::fixed << entry.mTagFloat << "\n";
+  } else if (entry.mTagName == 'l' || entry.mTagName == 'L') {
+    // Bug 739800 - Force l-tag addresses to have a "0x" prefix on all platforms
+    // Additionally, stringstream seemed to be ignoring formatter flags.
+    char tagBuff[1024];
+    unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
+    snprintf(tagBuff, 1024, "%c-%#llx\n", entry.mTagName, pc);
+    stream << tagBuff;
+  } else if (entry.mTagName == 'd') {
+    // TODO implement 'd' tag for text profile
+  } else {
+    stream << entry.mTagName << "-" << entry.mTagData << "\n";
+  }
+  return stream;
+}
+
+// END ProfileEntry
 ////////////////////////////////////////////////////////////////////////
 
 
 ////////////////////////////////////////////////////////////////////////
-// BEGIN ThreadProfile2
+// BEGIN ThreadProfile
 
-#define PROFILE_MAX_ENTRY  100000
 #define DYNAMIC_MAX_STRING 512
 
-ThreadProfile2::ThreadProfile2(int aEntrySize, PseudoStack *aStack)
+ThreadProfile::ThreadProfile(int aEntrySize, PseudoStack *aStack)
   : mWritePos(0)
   , mLastFlushPos(0)
   , mReadPos(0)
   , mEntrySize(aEntrySize)
   , mPseudoStack(aStack)
-  , mMutex("ThreadProfile2::mMutex")
+  , mMutex("ThreadProfile::mMutex")
 {
-  mEntries = new ProfileEntry2[mEntrySize];
+  mEntries = new ProfileEntry[mEntrySize];
 }
 
-ThreadProfile2::~ThreadProfile2()
+ThreadProfile::~ThreadProfile()
 {
   delete[] mEntries;
 }
 
-void ThreadProfile2::addTag(ProfileEntry2 aTag)
+void ThreadProfile::addTag(ProfileEntry aTag)
 {
   // Called from signal, call only reentrant functions
   mEntries[mWritePos] = aTag;
   mWritePos = (mWritePos + 1) % mEntrySize;
   if (mWritePos == mReadPos) {
     // Keep one slot open
-    mEntries[mReadPos] = ProfileEntry2();
+    mEntries[mReadPos] = ProfileEntry();
     mReadPos = (mReadPos + 1) % mEntrySize;
   }
   // we also need to move the flush pos to ensure we
   // do not pass it
   if (mWritePos == mLastFlushPos) {
     mLastFlushPos = (mLastFlushPos + 1) % mEntrySize;
   }
 }
 
 // flush the new entries
-void ThreadProfile2::flush()
+void ThreadProfile::flush()
 {
   mLastFlushPos = mWritePos;
 }
 
 // discards all of the entries since the last flush()
 // NOTE: that if mWritePos happens to wrap around past
 // mLastFlushPos we actually only discard mWritePos - mLastFlushPos entries
 //
@@ -199,62 +218,104 @@ void ThreadProfile2::flush()
 // |-----------------------------|
 // |ABCDEFdefghijklmnopqrstuvwxyz|
 // |-----------------------------|
 //        r       w
 // |-----------------------------|
 // |ABCDEFdefghijklmnopqrstuvwxyz| -> 'defghijkl'
 // |-----------------------------|
 
-void ThreadProfile2::erase()
+void ThreadProfile::erase()
 {
   mWritePos = mLastFlushPos;
 }
 
-char* ThreadProfile2::processDynamicTag(int readPos,
+char* ThreadProfile::processDynamicTag(int readPos,
                                        int* tagsConsumed, char* tagBuff)
 {
   int readAheadPos = (readPos + 1) % mEntrySize;
   int tagBuffPos = 0;
 
   // Read the string stored in mTagData until the null character is seen
   bool seenNullByte = false;
   while (readAheadPos != mLastFlushPos && !seenNullByte) {
     (*tagsConsumed)++;
-    ProfileEntry2 readAheadEntry = mEntries[readAheadPos];
+    ProfileEntry readAheadEntry = mEntries[readAheadPos];
     for (size_t pos = 0; pos < sizeof(void*); pos++) {
       tagBuff[tagBuffPos] = readAheadEntry.mTagChars[pos];
       if (tagBuff[tagBuffPos] == '\0' || tagBuffPos == DYNAMIC_MAX_STRING-2) {
         seenNullByte = true;
         break;
       }
       tagBuffPos++;
     }
     if (!seenNullByte)
       readAheadPos = (readAheadPos + 1) % mEntrySize;
   }
   return tagBuff;
 }
 
-JSCustomObject* ThreadProfile2::ToJSObject(JSContext *aCx)
+void ThreadProfile::IterateTags(IterateTagsCallback aCallback)
 {
-  JSObjectBuilder b(aCx);
-
-  JSCustomObject *profile = b.CreateObject();
-  JSCustomArray *samples = b.CreateArray();
-  b.DefineProperty(profile, "samples", samples);
-
-  JSCustomObject *sample = NULL;
-  JSCustomArray *frames = NULL;
+  MOZ_ASSERT(aCallback);
 
   int readPos = mReadPos;
   while (readPos != mLastFlushPos) {
     // Number of tag consumed
     int incBy = 1;
-    ProfileEntry2 entry = mEntries[readPos];
+    const ProfileEntry& entry = mEntries[readPos];
+
+    // Read ahead to the next tag, if it's a 'd' tag process it now
+    const char* tagStringData = entry.mTagData;
+    int readAheadPos = (readPos + 1) % mEntrySize;
+    char tagBuff[DYNAMIC_MAX_STRING];
+    // Make sure the string is always null terminated if it fills up DYNAMIC_MAX_STRING-2
+    tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
+
+    if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
+      tagStringData = processDynamicTag(readPos, &incBy, tagBuff);
+    }
+
+    aCallback(entry, tagStringData);
+
+    readPos = (readPos + incBy) % mEntrySize;
+  }
+}
+
+void ThreadProfile::ToStreamAsJSON(std::ostream& stream)
+{
+  JSCustomObjectBuilder b;
+  JSCustomObject *profile = b.CreateObject();
+  BuildJSObject(b, profile);
+  b.Serialize(profile, stream);
+  b.DeleteObject(profile);
+}
+
+JSCustomObject* ThreadProfile::ToJSObject(JSContext *aCx)
+{
+  JSObjectBuilder b(aCx);
+  JSCustomObject *profile = b.CreateObject();
+  BuildJSObject(b, profile);
+
+  return profile;
+}
+
+void ThreadProfile::BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile) {
+  JSCustomArray *samples = b.CreateArray();
+  b.DefineProperty(profile, "samples", samples);
+
+  JSCustomObject *sample = nullptr;
+  JSCustomArray *frames = nullptr;
+  JSCustomArray *marker = nullptr;
+
+  int readPos = mReadPos;
+  while (readPos != mLastFlushPos) {
+    // Number of tag consumed
+    int incBy = 1;
+    ProfileEntry entry = mEntries[readPos];
 
     // Read ahead to the next tag, if it's a 'd' tag process it now
     const char* tagStringData = entry.mTagData;
     int readAheadPos = (readPos + 1) % mEntrySize;
     char tagBuff[DYNAMIC_MAX_STRING];
     // Make sure the string is always null terminated if it fills up
     // DYNAMIC_MAX_STRING-2
     tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
@@ -265,16 +326,29 @@ JSCustomObject* ThreadProfile2::ToJSObje
 
     switch (entry.mTagName) {
       case 's':
         sample = b.CreateObject();
         b.DefineProperty(sample, "name", tagStringData);
         frames = b.CreateArray();
         b.DefineProperty(sample, "frames", frames);
         b.ArrayPush(samples, sample);
+        // Created lazily
+        marker = nullptr;
+        break;
+      case 'm':
+        {
+          if (sample) {
+            if (!marker) {
+              marker = b.CreateArray();
+              b.DefineProperty(sample, "marker", marker);
+            }
+            b.ArrayPush(marker, tagStringData);
+          }
+        }
         break;
       case 'r':
         {
           if (sample) {
             b.DefineProperty(sample, "responsiveness", entry.mTagFloat);
           }
         }
         break;
@@ -315,24 +389,32 @@ JSCustomObject* ThreadProfile2::ToJSObje
               }
             }
             b.ArrayPush(frames, frame);
           }
         }
     }
     readPos = (readPos + incBy) % mEntrySize;
   }
-
-  return profile;
 }
 
-PseudoStack* ThreadProfile2::GetPseudoStack()
+PseudoStack* ThreadProfile::GetPseudoStack()
 {
   return mPseudoStack;
 }
 
-mozilla::Mutex* ThreadProfile2::GetMutex()
+mozilla::Mutex* ThreadProfile::GetMutex()
 {
   return &mMutex;
 }
 
-// END ThreadProfile2
+std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile)
+{
+  int readPos = profile.mReadPos;
+  while (readPos != profile.mLastFlushPos) {
+    stream << profile.mEntries[readPos];
+    readPos = (readPos + 1) % profile.mEntrySize;
+  }
+  return stream;
+}
+
+// END ThreadProfile
 ////////////////////////////////////////////////////////////////////////
rename from tools/profiler/ProfileEntry2.h
rename to tools/profiler/ProfileEntry.h
--- a/tools/profiler/ProfileEntry2.h
+++ b/tools/profiler/ProfileEntry.h
@@ -2,75 +2,86 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZ_PROFILE_ENTRY_H
 #define MOZ_PROFILE_ENTRY_H
 
 #include "GeckoProfilerImpl.h"
+#include "JSAObjectBuilder.h"
+#include "platform.h"
 #include "mozilla/Mutex.h"
 
-class ThreadProfile2;
+class ThreadProfile;
+class ThreadProfile;
 
-class ProfileEntry2
+class ProfileEntry
 {
 public:
-  ProfileEntry2();
+  ProfileEntry();
 
   // aTagData must not need release (i.e. be a string from the text segment)
-  ProfileEntry2(char aTagName, const char *aTagData);
-  ProfileEntry2(char aTagName, void *aTagPtr);
-  ProfileEntry2(char aTagName, double aTagFloat);
-  ProfileEntry2(char aTagName, uintptr_t aTagOffset);
-  ProfileEntry2(char aTagName, Address aTagAddress);
-  ProfileEntry2(char aTagName, int aTagLine);
-  ProfileEntry2(char aTagName, char aTagChar);
-  friend std::ostream& operator<<(std::ostream& stream, const ProfileEntry2& entry);
+  ProfileEntry(char aTagName, const char *aTagData);
+  ProfileEntry(char aTagName, void *aTagPtr);
+  ProfileEntry(char aTagName, double aTagFloat);
+  ProfileEntry(char aTagName, uintptr_t aTagOffset);
+  ProfileEntry(char aTagName, Address aTagAddress);
+  ProfileEntry(char aTagName, int aTagLine);
+  ProfileEntry(char aTagName, char aTagChar);
+  friend std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry);
   bool is_ent_hint(char hintChar);
   bool is_ent_hint();
   bool is_ent(char tagName);
   void* get_tagPtr();
   void log();
 
+  char getTagName() const { return mTagName; }
+
 private:
-  friend class ThreadProfile2;
+  friend class ThreadProfile;
   union {
     const char* mTagData;
     char        mTagChars[sizeof(void*)];
     void*       mTagPtr;
     double      mTagFloat;
     Address     mTagAddress;
     uintptr_t   mTagOffset;
     int         mTagLine;
     char        mTagChar;
   };
   char mTagName;
 };
 
+typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagStringData);
 
-class ThreadProfile2
+class ThreadProfile
 {
 public:
-  ThreadProfile2(int aEntrySize, PseudoStack *aStack);
-  ~ThreadProfile2();
-  void addTag(ProfileEntry2 aTag);
+  ThreadProfile(int aEntrySize, PseudoStack *aStack);
+  ~ThreadProfile();
+  void addTag(ProfileEntry aTag);
   void flush();
   void erase();
   char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff);
+  void IterateTags(IterateTagsCallback aCallback);
   friend std::ostream& operator<<(std::ostream& stream,
-                                  const ThreadProfile2& profile);
+                                  const ThreadProfile& profile);
+  void ToStreamAsJSON(std::ostream& stream);
   JSCustomObject *ToJSObject(JSContext *aCx);
   PseudoStack* GetPseudoStack();
   mozilla::Mutex* GetMutex();
+  void BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile);
 private:
   // Circular buffer 'Keep One Slot Open' implementation
   // for simplicity
-  ProfileEntry2* mEntries;
+  ProfileEntry* mEntries;
   int            mWritePos; // points to the next entry we will write to
   int            mLastFlushPos; // points to the next entry since the last flush()
   int            mReadPos;  // points to the next entry we will read to
   int            mEntrySize;
   PseudoStack*   mPseudoStack;
   mozilla::Mutex mMutex;
 };
 
+std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile);
+
 #endif /* ndef MOZ_PROFILE_ENTRY_H */
new file mode 100644
--- /dev/null
+++ b/tools/profiler/SaveProfileTask.h
@@ -0,0 +1,111 @@
+/* -*- 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_SAVETASK_H_
+#define PROFILER_SAVETASK_H_
+
+#include "platform.h"
+#include "nsThreadUtils.h"
+#include "nsIXULRuntime.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsXULAppAPI.h"
+#include "nsIJSRuntimeService.h"
+
+#ifdef XP_WIN
+ #include <windows.h>
+ #define getpid GetCurrentProcessId
+#else
+ #include <unistd.h>
+#endif
+
+static JSBool
+WriteCallback(const jschar *buf, uint32_t len, void *data)
+{
+  std::ofstream& stream = *static_cast<std::ofstream*>(data);
+  nsAutoCString profile = NS_ConvertUTF16toUTF8(buf, len);
+  stream << profile.Data();
+  return JS_TRUE;
+}
+
+/**
+ * This is an event used to save the profile on the main thread
+ * to be sure that it is not being modified while saving.
+ */
+class SaveProfileTask : public nsRunnable {
+public:
+  SaveProfileTask() {}
+
+  NS_IMETHOD Run() {
+    // Get file path
+#   if defined(SPS_PLAT_arm_android)
+    nsCString tmpPath;
+    tmpPath.AppendPrintf("/sdcard/profile_%i_%i.txt", XRE_GetProcessType(), getpid());
+#   else
+    nsCOMPtr<nsIFile> tmpFile;
+    nsAutoCString tmpPath;
+    if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)))) {
+      LOG("Failed to find temporary directory.");
+      return NS_ERROR_FAILURE;
+    }
+    tmpPath.AppendPrintf("profile_%i_%i.txt", XRE_GetProcessType(), getpid());
+
+    nsresult rv = tmpFile->AppendNative(tmpPath);
+    if (NS_FAILED(rv))
+      return rv;
+
+    rv = tmpFile->GetNativePath(tmpPath);
+    if (NS_FAILED(rv))
+      return rv;
+#   endif
+
+    // Create a JSContext to run a JSObjectBuilder :(
+    // Based on XPCShellEnvironment
+    JSRuntime *rt;
+    JSContext *cx;
+    nsCOMPtr<nsIJSRuntimeService> rtsvc 
+      = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
+    if (!rtsvc || NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
+      LOG("failed to get RuntimeService");
+      return NS_ERROR_FAILURE;;
+    }
+
+    cx = JS_NewContext(rt, 8192);
+    if (!cx) {
+      LOG("Failed to get context");
+      return NS_ERROR_FAILURE;
+    }
+
+    {
+      JSAutoRequest ar(cx);
+      static JSClass c = {
+          "global", JSCLASS_GLOBAL_FLAGS,
+          JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+          JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
+      };
+      JSObject *obj = JS_NewGlobalObject(cx, &c, NULL);
+
+      std::ofstream stream;
+      stream.open(tmpPath.get());
+      if (stream.is_open()) {
+        JSAutoCompartment autoComp(cx, obj);
+        JSObject* profileObj = profiler_get_profile_jsobject(cx);
+        jsval val = OBJECT_TO_JSVAL(profileObj);
+        JS_Stringify(cx, &val, nullptr, JSVAL_NULL, WriteCallback, &stream);
+        stream.close();
+        LOGF("Saved to %s", tmpPath.get());
+      } else {
+        LOG("Fail to open profile log file.");
+      }
+    }
+    JS_EndRequest(cx);
+    JS_DestroyContext(cx);
+
+    return NS_OK;
+  }
+};
+
+#endif
+
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -3,27 +3,28 @@
  * 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 <string>
 #include <stdio.h>
 #include <fstream>
 #include <sstream>
 #include "GeckoProfilerImpl.h"
+#include "SaveProfileTask.h"
+#include "ProfileEntry.h"
 #include "platform.h"
-#include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "shared-libraries.h"
 #include "mozilla/StackWalk.h"
+#include "TableTicker.h"
 
 // JSON
 #include "JSObjectBuilder.h"
 #include "JSCustomObjectBuilder.h"
-#include "nsIJSRuntimeService.h"
 
 // Meta
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIXULRuntime.h"
 #include "nsIXULAppInfo.h"
@@ -51,557 +52,35 @@
 #endif
 #ifdef USE_NS_STACKWALK
  #include "nsStackWalk.h"
 #endif
 
 using std::string;
 using namespace mozilla;
 
-#ifdef XP_WIN
- #include <windows.h>
- #define getpid GetCurrentProcessId
-#else
- #include <unistd.h>
-#endif
-
 #ifndef MAXPATHLEN
  #ifdef PATH_MAX
   #define MAXPATHLEN PATH_MAX
  #elif defined(MAX_PATH)
   #define MAXPATHLEN MAX_PATH
  #elif defined(_MAX_PATH)
   #define MAXPATHLEN _MAX_PATH
  #elif defined(CCHMAXPATH)
   #define MAXPATHLEN CCHMAXPATH
  #else
   #define MAXPATHLEN 1024
  #endif
 #endif
 
-#if _MSC_VER
- #define snprintf _snprintf
-#endif
-
-static const int DYNAMIC_MAX_STRING = 512;
-
-static mozilla::ThreadLocal<TableTicker *> tlsTicker;
-
-TimeStamp sLastTracerEvent;
-int sFrameNumber = 0;
-int sLastFrameNumber = 0;
-
-class ThreadProfile;
-
-class ProfileEntry
-{
-public:
-  ProfileEntry()
-    : mTagData(NULL)
-    , mTagName(0)
-  { }
-
-  // aTagData must not need release (i.e. be a string from the text segment)
-  ProfileEntry(char aTagName, const char *aTagData)
-    : mTagData(aTagData)
-    , mTagName(aTagName)
-  { }
-
-  ProfileEntry(char aTagName, void *aTagPtr)
-    : mTagPtr(aTagPtr)
-    , mTagName(aTagName)
-  { }
-
-  ProfileEntry(char aTagName, double aTagFloat)
-    : mTagFloat(aTagFloat)
-    , mTagName(aTagName)
-  { }
-
-  ProfileEntry(char aTagName, uintptr_t aTagOffset)
-    : mTagOffset(aTagOffset)
-    , mTagName(aTagName)
-  { }
-
-  ProfileEntry(char aTagName, Address aTagAddress)
-    : mTagAddress(aTagAddress)
-    , mTagName(aTagName)
-  { }
-
-  ProfileEntry(char aTagName, int aTagLine)
-    : mTagLine(aTagLine)
-    , mTagName(aTagName)
-  { }
-
-  friend std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry);
-
-  union {
-    const char* mTagData;
-    char mTagChars[sizeof(void*)];
-    void* mTagPtr;
-    double mTagFloat;
-    Address mTagAddress;
-    uintptr_t mTagOffset;
-    int mTagLine;
-  };
-  char mTagName;
-};
-
-typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagStringData);
-
-#define PROFILE_MAX_ENTRY 100000
-class ThreadProfile
-{
-public:
-  ThreadProfile(int aEntrySize, PseudoStack *aStack)
-    : mWritePos(0)
-    , mLastFlushPos(0)
-    , mReadPos(0)
-    , mEntrySize(aEntrySize)
-    , mStack(aStack)
-  {
-    mEntries = new ProfileEntry[mEntrySize];
-  }
-
-  ~ThreadProfile()
-  {
-    delete[] mEntries;
-  }
-
-  void addTag(ProfileEntry aTag)
-  {
-    // Called from signal, call only reentrant functions
-    mEntries[mWritePos] = aTag;
-    mWritePos = (mWritePos + 1) % mEntrySize;
-    if (mWritePos == mReadPos) {
-      // Keep one slot open
-      mEntries[mReadPos] = ProfileEntry();
-      mReadPos = (mReadPos + 1) % mEntrySize;
-    }
-    // we also need to move the flush pos to ensure we
-    // do not pass it
-    if (mWritePos == mLastFlushPos) {
-      mLastFlushPos = (mLastFlushPos + 1) % mEntrySize;
-    }
-  }
-
-  // flush the new entries
-  void flush()
-  {
-    mLastFlushPos = mWritePos;
-  }
-
-  // discards all of the entries since the last flush()
-  // NOTE: that if mWritePos happens to wrap around past
-  // mLastFlushPos we actually only discard mWritePos - mLastFlushPos entries
-  //
-  // r = mReadPos
-  // w = mWritePos
-  // f = mLastFlushPos
-  //
-  //     r          f    w
-  // |-----------------------------|
-  // |   abcdefghijklmnopq         | -> 'abcdefghijklmnopq'
-  // |-----------------------------|
-  //
-  //
-  // mWritePos and mReadPos have passed mLastFlushPos
-  //                      f
-  //                    w r
-  // |-----------------------------|
-  // |ABCDEFGHIJKLMNOPQRSqrstuvwxyz|
-  // |-----------------------------|
-  //                       w
-  //                       r
-  // |-----------------------------|
-  // |ABCDEFGHIJKLMNOPQRSqrstuvwxyz| -> ''
-  // |-----------------------------|
-  //
-  //
-  // mWritePos will end up the same as mReadPos
-  //                r
-  //              w f
-  // |-----------------------------|
-  // |ABCDEFGHIJKLMklmnopqrstuvwxyz|
-  // |-----------------------------|
-  //                r
-  //                w
-  // |-----------------------------|
-  // |ABCDEFGHIJKLMklmnopqrstuvwxyz| -> ''
-  // |-----------------------------|
-  //
-  //
-  // mWritePos has moved past mReadPos
-  //      w r       f
-  // |-----------------------------|
-  // |ABCDEFdefghijklmnopqrstuvwxyz|
-  // |-----------------------------|
-  //        r       w
-  // |-----------------------------|
-  // |ABCDEFdefghijklmnopqrstuvwxyz| -> 'defghijkl'
-  // |-----------------------------|
-
-  void erase()
-  {
-    mWritePos = mLastFlushPos;
-  }
-
-  char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff)
-  {
-    int readAheadPos = (readPos + 1) % mEntrySize;
-    int tagBuffPos = 0;
-
-    // Read the string stored in mTagData until the null character is seen
-    bool seenNullByte = false;
-    while (readAheadPos != mLastFlushPos && !seenNullByte) {
-      (*tagsConsumed)++;
-      ProfileEntry readAheadEntry = mEntries[readAheadPos];
-      for (size_t pos = 0; pos < sizeof(void*); pos++) {
-        tagBuff[tagBuffPos] = readAheadEntry.mTagChars[pos];
-        if (tagBuff[tagBuffPos] == '\0' || tagBuffPos == DYNAMIC_MAX_STRING-2) {
-          seenNullByte = true;
-          break;
-        }
-        tagBuffPos++;
-      }
-      if (!seenNullByte)
-        readAheadPos = (readAheadPos + 1) % mEntrySize;
-    }
-    return tagBuff;
-  }
-
-  friend std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile);
-
-  void IterateTags(IterateTagsCallback aCallback)
-  {
-    MOZ_ASSERT(aCallback);
-
-    int readPos = mReadPos;
-    while (readPos != mLastFlushPos) {
-      // Number of tag consumed
-      int incBy = 1;
-      const ProfileEntry& entry = mEntries[readPos];
-
-      // Read ahead to the next tag, if it's a 'd' tag process it now
-      const char* tagStringData = entry.mTagData;
-      int readAheadPos = (readPos + 1) % mEntrySize;
-      char tagBuff[DYNAMIC_MAX_STRING];
-      // Make sure the string is always null terminated if it fills up DYNAMIC_MAX_STRING-2
-      tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
-
-      if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
-        tagStringData = processDynamicTag(readPos, &incBy, tagBuff);
-      }
-
-      aCallback(entry, tagStringData);
-
-      readPos = (readPos + incBy) % mEntrySize;
-    }
-  }
-
-  void ToStreamAsJSON(std::ostream& stream)
-  {
-    JSCustomObjectBuilder b;
-    JSCustomObject *profile = b.CreateObject();
-    BuildJSObject(b, profile);
-    b.Serialize(profile, stream);
-    b.DeleteObject(profile);
-  }
-
-  JSCustomObject *ToJSObject(JSContext *aCx)
-  {
-    JSObjectBuilder b(aCx);
-    JSCustomObject *profile = b.CreateObject();
-    BuildJSObject(b, profile);
-
-    return profile;
-  }
-
-  void BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile) {
-    JSCustomArray *samples = b.CreateArray();
-    b.DefineProperty(profile, "samples", samples);
-
-    JSCustomObject *sample = nullptr;
-    JSCustomArray *frames = nullptr;
-    JSCustomArray *marker = nullptr;
-
-    int readPos = mReadPos;
-    while (readPos != mLastFlushPos) {
-      // Number of tag consumed
-      int incBy = 1;
-      ProfileEntry entry = mEntries[readPos];
-
-      // Read ahead to the next tag, if it's a 'd' tag process it now
-      const char* tagStringData = entry.mTagData;
-      int readAheadPos = (readPos + 1) % mEntrySize;
-      char tagBuff[DYNAMIC_MAX_STRING];
-      // Make sure the string is always null terminated if it fills up DYNAMIC_MAX_STRING-2
-      tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
-
-      if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
-        tagStringData = processDynamicTag(readPos, &incBy, tagBuff);
-      }
-
-      switch (entry.mTagName) {
-        case 's':
-          sample = b.CreateObject();
-          b.DefineProperty(sample, "name", tagStringData);
-          frames = b.CreateArray();
-          b.DefineProperty(sample, "frames", frames);
-          b.ArrayPush(samples, sample);
-          // Created lazily
-          marker = NULL;
-          break;
-        case 'm':
-          {
-            if (sample) {
-              if (!marker) {
-                marker = b.CreateArray();
-                b.DefineProperty(sample, "marker", marker);
-              }
-              b.ArrayPush(marker, tagStringData);
-            }
-          }
-          break;
-        case 'r':
-          {
-            if (sample) {
-              b.DefineProperty(sample, "responsiveness", entry.mTagFloat);
-            }
-          }
-          break;
-        case 'f':
-          {
-            if (sample) {
-              b.DefineProperty(sample, "frameNumber", entry.mTagLine);
-            }
-          }
-          break;
-        case 't':
-          {
-            if (sample) {
-              b.DefineProperty(sample, "time", entry.mTagFloat);
-            }
-          }
-          break;
-        case 'c':
-        case 'l':
-          {
-            if (sample) {
-              JSCustomObject *frame = b.CreateObject();
-              if (entry.mTagName == 'l') {
-                // Bug 753041
-                // We need a double cast here to tell GCC that we don't want to sign
-                // extend 32-bit addresses starting with 0xFXXXXXX.
-                unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
-                snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc);
-                b.DefineProperty(frame, "location", tagBuff);
-              } else {
-                b.DefineProperty(frame, "location", tagStringData);
-                readAheadPos = (readPos + incBy) % mEntrySize;
-                if (readAheadPos != mLastFlushPos &&
-                    mEntries[readAheadPos].mTagName == 'n') {
-                  b.DefineProperty(frame, "line",
-                                   mEntries[readAheadPos].mTagLine);
-                  incBy++;
-                }
-              }
-              b.ArrayPush(frames, frame);
-            }
-          }
-      }
-      readPos = (readPos + incBy) % mEntrySize;
-    }
-  }
-
-  PseudoStack* GetStack()
-  {
-    return mStack;
-  }
-private:
-  // Circular buffer 'Keep One Slot Open' implementation
-  // for simplicity
-  ProfileEntry *mEntries;
-  int mWritePos; // points to the next entry we will write to
-  int mLastFlushPos; // points to the next entry since the last flush()
-  int mReadPos;  // points to the next entry we will read to
-  int mEntrySize;
-  PseudoStack *mStack;
-};
-
-class SaveProfileTask;
-
-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;
-}
-
-class TableTicker: public Sampler {
- public:
-  TableTicker(int aInterval, int aEntrySize, PseudoStack *aStack,
-              const char** aFeatures, uint32_t aFeatureCount)
-    : Sampler(aInterval, true)
-    , mPrimaryThreadProfile(aEntrySize, aStack)
-    , mStartTime(TimeStamp::Now())
-    , mSaveRequested(false)
-  {
-    mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
-
-    //XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
-    mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
-    mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
-    mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
-    mPrimaryThreadProfile.addTag(ProfileEntry('m', "Start"));
-  }
-
-  ~TableTicker() { if (IsActive()) Stop(); }
-
-  virtual void SampleStack(TickSample* sample) {}
-
-  // Called within a signal. This function must be reentrant
-  virtual void Tick(TickSample* sample);
-
-  // Called within a signal. This function must be reentrant
-  virtual void RequestSave()
-  {
-    mSaveRequested = true;
-  }
-
-  virtual void HandleSaveRequest();
-
-  ThreadProfile* GetPrimaryThreadProfile()
-  {
-    return &mPrimaryThreadProfile;
-  }
-
-  void ToStreamAsJSON(std::ostream& stream);
-  JSObject *ToJSObject(JSContext *aCx);
-  JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b);
-
-  const bool ProfileJS() { return mProfileJS; }
-
-private:
-  // Not implemented on platforms which do not support backtracing
-  void doBacktrace(ThreadProfile &aProfile, TickSample* aSample);
-
-  void BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile);
-private:
-  // This represent the application's main thread (SAMPLER_INIT)
-  ThreadProfile mPrimaryThreadProfile;
-  TimeStamp mStartTime;
-  bool mSaveRequested;
-  bool mAddLeafAddresses;
-  bool mUseStackWalk;
-  bool mJankOnly;
-  bool mProfileJS;
-};
+///////////////////////////////////////////////////////////////////////
+// BEGIN SaveProfileTask et al
 
 std::string GetSharedLibraryInfoString();
 
-static JSBool
-WriteCallback(const jschar *buf, uint32_t len, void *data)
-{
-  std::ofstream& stream = *static_cast<std::ofstream*>(data);
-  nsAutoCString profile = NS_ConvertUTF16toUTF8(buf, len);
-  stream << profile.Data();
-  return JS_TRUE;
-}
-
-/**
- * This is an event used to save the profile on the main thread
- * to be sure that it is not being modified while saving.
- */
-class SaveProfileTask : public nsRunnable {
-public:
-  SaveProfileTask() {}
-
-  NS_IMETHOD Run() {
-    TableTicker *t = tlsTicker.get();
-    // Pause the profiler during saving.
-    // This will prevent us from recording sampling
-    // regarding profile saving. This will also
-    // prevent bugs caused by the circular buffer not
-    // being thread safe. Bug 750989.
-    t->SetPaused(true);
-
-    // Get file path
-#ifdef MOZ_WIDGET_ANDROID
-    nsCString tmpPath;
-    tmpPath.AppendPrintf("/sdcard/profile_%i_%i.txt", XRE_GetProcessType(), getpid());
-#else
-    nsCOMPtr<nsIFile> tmpFile;
-    nsAutoCString tmpPath;
-    if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)))) {
-      LOG("Failed to find temporary directory.");
-      return NS_ERROR_FAILURE;
-    }
-    tmpPath.AppendPrintf("profile_%i_%i.txt", XRE_GetProcessType(), getpid());
-
-    nsresult rv = tmpFile->AppendNative(tmpPath);
-    if (NS_FAILED(rv))
-      return rv;
-
-    rv = tmpFile->GetNativePath(tmpPath);
-    if (NS_FAILED(rv))
-      return rv;
-#endif
-
-    // Create a JSContext to run a JSCustomObjectBuilder :(
-    // Based on XPCShellEnvironment
-    JSRuntime *rt;
-    JSContext *cx;
-    nsCOMPtr<nsIJSRuntimeService> rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
-    if (!rtsvc || NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
-      LOG("failed to get RuntimeService");
-      return NS_ERROR_FAILURE;;
-    }
-
-    cx = JS_NewContext(rt, 8192);
-    if (!cx) {
-      LOG("Failed to get context");
-      return NS_ERROR_FAILURE;
-    }
-
-    {
-      JSAutoRequest ar(cx);
-      static JSClass c = {
-          "global", JSCLASS_GLOBAL_FLAGS,
-          JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-          JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
-      };
-      JSObject *obj = JS_NewGlobalObject(cx, &c, NULL, JS::SystemZone);
-
-      std::ofstream stream;
-      stream.open(tmpPath.get());
-      // Pause the profiler during saving.
-      // This will prevent us from recording sampling
-      // regarding profile saving. This will also
-      // prevent bugs caused by the circular buffer not
-      // being thread safe. Bug 750989.
-      if (stream.is_open()) {
-        JSAutoCompartment autoComp(cx, obj);
-        JSObject* profileObj = mozilla_sampler_get_profile_data1(cx);
-        JS::Value val = OBJECT_TO_JSVAL(profileObj);
-        JS_Stringify(cx, &val, nullptr, JSVAL_NULL, WriteCallback, &stream);
-        stream.close();
-        LOGF("Saved to %s", tmpPath.get());
-      } else {
-        LOG("Fail to open profile log file.");
-      }
-    }
-    JS_EndRequest(cx);
-    JS_DestroyContext(cx);
-
-    return NS_OK;
-  }
-};
-
 void TableTicker::HandleSaveRequest()
 {
   if (!mSaveRequested)
     return;
   mSaveRequested = false;
 
   // TODO: Use use the ipc/chromium Tasks here to support processes
   // without XPCOM.
@@ -691,22 +170,28 @@ void TableTicker::BuildJSObject(JSAObjec
   b.DefineProperty(profile, "meta", meta);
 
   // Lists the samples for each ThreadProfile
   JSCustomArray *threads = b.CreateArray();
   b.DefineProperty(profile, "threads", threads);
 
   // For now we only have one thread
   SetPaused(true);
+  ThreadProfile* prof = GetPrimaryThreadProfile();
+  prof->GetMutex()->Lock();
   JSCustomObject* threadSamples = b.CreateObject();
-  GetPrimaryThreadProfile()->BuildJSObject(b, threadSamples);
+  prof->BuildJSObject(b, threadSamples);
   b.ArrayPush(threads, threadSamples);
+  prof->GetMutex()->Unlock();
   SetPaused(false);
 }
 
+// END SaveProfileTask et al
+////////////////////////////////////////////////////////////////////////
+
 static
 void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
 {
   aProfile.addTag(ProfileEntry(aTagName, ""));
   // Add one to store the null termination
   size_t strLen = strlen(aStr) + 1;
   for (size_t j = 0; j < strLen;) {
     // Store as many characters in the void* as the platform allows
@@ -759,17 +244,17 @@ void addProfileEntry(volatile StackEntry
     lineno = entry.line();
   }
   if (lineno != -1) {
     aProfile.addTag(ProfileEntry('n', lineno));
   }
 }
 
 #ifdef USE_BACKTRACE
-void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
+void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
 {
   void *array[100];
   int count = backtrace (array, 100);
 
   aProfile.addTag(ProfileEntry('s', "(root)"));
 
   for (int i = 0; i < count; i++) {
     if( (intptr_t)array[i] == -1 ) break;
@@ -792,17 +277,17 @@ void StackWalkCallback(void* aPC, void* 
 {
   PCArray* array = static_cast<PCArray*>(aClosure);
   MOZ_ASSERT(array->count < array->size);
   array->sp_array[array->count] = aSP;
   array->array[array->count] = aPC;
   array->count++;
 }
 
-void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
+void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
 {
 #ifndef XP_MACOSX
   uintptr_t thread = GetThreadHandle(platform_data());
   MOZ_ASSERT(thread);
 #endif
   void* pc_array[1000];
   void* sp_array[1000];
   PCArray array = {
@@ -833,17 +318,17 @@ void TableTicker::doBacktrace(ThreadProf
 #endif // XP_WIN
 
   nsresult rv = NS_StackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
                              &array, thread, platformData);
 #endif
   if (NS_SUCCEEDED(rv)) {
     aProfile.addTag(ProfileEntry('s', "(root)"));
 
-    PseudoStack* stack = aProfile.GetStack();
+    PseudoStack* stack = aProfile.GetPseudoStack();
     uint32_t pseudoStackPos = 0;
 
     /* We have two stacks, the native C stack we extracted from unwinding,
      * and the pseudostack we managed during execution. We want to consolidate
      * the two in order. We do so by merging using the approximate stack address
      * when each entry was push. When pushing JS entry we may not now the stack
      * address in which case we have a NULL stack address in which case we assume
      * that it follows immediatly the previous element.
@@ -891,32 +376,20 @@ void doSampleStackTrace(PseudoStack *aSt
     aProfile.addTag(ProfileEntry('l', (void*)sample->pc));
 #ifdef ENABLE_ARM_LR_SAVING
     aProfile.addTag(ProfileEntry('L', (void*)sample->lr));
 #endif
   }
 #endif
 }
 
-/* used to keep track of the last event that we sampled during */
-unsigned int sLastSampledEventGeneration = 0;
-
-/* a counter that's incremented everytime we get responsiveness event
- * note: it might also be worth tracking everytime we go around
- * the event loop */
-unsigned int sCurrentEventGeneration = 0;
-/* we don't need to worry about overflow because we only treat the
- * case of them being the same as special. i.e. we only run into
- * a problem if 2^32 events happen between samples that we need
- * to know are associated with different events */
-
 void TableTicker::Tick(TickSample* sample)
 {
   // Marker(s) come before the sample
-  PseudoStack* stack = mPrimaryThreadProfile.GetStack();
+  PseudoStack* stack = mPrimaryThreadProfile.GetPseudoStack();
   for (int i = 0; stack->getMarker(i) != NULL; i++) {
     addDynamicTag(mPrimaryThreadProfile, 'm', stack->getMarker(i));
   }
   stack->mQueueClearMarker = true;
 
   bool recordSample = true;
   if (mJankOnly) {
     // if we are on a different event we can discard any temporary samples
@@ -936,17 +409,17 @@ void TableTicker::Tick(TickSample* sampl
       if (delta.ToMilliseconds() > 100.0) {
           recordSample = true;
       }
     }
   }
 
 #if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK)
   if (mUseStackWalk) {
-    doBacktrace(mPrimaryThreadProfile, sample);
+    doNativeBacktrace(mPrimaryThreadProfile, sample);
   } else {
     doSampleStackTrace(stack, mPrimaryThreadProfile, mAddLeafAddresses ? sample : nullptr);
   }
 #else
   doSampleStackTrace(stack, mPrimaryThreadProfile, mAddLeafAddresses ? sample : nullptr);
 #endif
 
   if (recordSample)
@@ -963,332 +436,37 @@ void TableTicker::Tick(TickSample* sampl
   }
 
   if (sLastFrameNumber != sFrameNumber) {
     mPrimaryThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
     sLastFrameNumber = sFrameNumber;
   }
 }
 
-std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile)
-{
-  int readPos = profile.mReadPos;
-  while (readPos != profile.mLastFlushPos) {
-    stream << profile.mEntries[readPos];
-    readPos = (readPos + 1) % profile.mEntrySize;
-  }
-  return stream;
-}
-
-std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry)
-{
-  if (entry.mTagName == 'r' || entry.mTagName == 't') {
-    stream << entry.mTagName << "-" << std::fixed << entry.mTagFloat << "\n";
-  } else if (entry.mTagName == 'l' || entry.mTagName == 'L') {
-    // Bug 739800 - Force l-tag addresses to have a "0x" prefix on all platforms
-    // Additionally, stringstream seemed to be ignoring formatter flags.
-    char tagBuff[1024];
-    unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
-    snprintf(tagBuff, 1024, "%c-%#llx\n", entry.mTagName, pc);
-    stream << tagBuff;
-  } else if (entry.mTagName == 'd') {
-    // TODO implement 'd' tag for text profile
-  } else {
-    stream << entry.mTagName << "-" << entry.mTagData << "\n";
-  }
-  return stream;
-}
-
-bool sps_version2()
-{
-  static int version = 0; // Raced on, potentially
-
-  if (version == 0) {
-    bool allow2 = false; // Is v2 allowable on this platform?
-#   if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
-       || defined(SPS_PLAT_x86_linux)
-    allow2 = true;
-#   elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) \
-         || defined(SPS_PLAT_x86_windows) || defined(SPS_PLAT_x86_android) \
-         || defined(SPS_PLAT_amd64_windows)
-    allow2 = false;
-#   else
-#     error "Unknown platform"
-#   endif
-
-    bool req2 = PR_GetEnv("MOZ_PROFILER_NEW") != NULL; // Has v2 been requested?
-
-    bool elfhackd = false;
-#   if defined(USE_ELF_HACK)
-    bool elfhackd = true;
-#   endif
-
-    if (req2 && allow2) {
-      version = 2;
-      LOG("------------------- MOZ_PROFILER_NEW set -------------------");
-    } else if (req2 && !allow2) {
-      version = 1;
-      LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
-      LOG("---------- but is not available on this platform -----------");
-    } else if (req2 && elfhackd) {
-      version = 1;
-      LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
-      LOG("--- but this build was not done with --disable-elf-hack ----");
-    } else {
-      version = 1;
-      LOG("----------------- MOZ_PROFILER_NEW not set -----------------");
-    }
-  }
-  return version == 2;
-}
-
-void mozilla_sampler_init1()
-{
-  if (stack_key_initialized)
-    return;
-
-  if (!tlsPseudoStack.init() || !tlsTicker.init()) {
-    LOG("Failed to init.");
-    return;
-  }
-  stack_key_initialized = true;
-
-  PseudoStack *stack = new PseudoStack();
-  tlsPseudoStack.set(stack);
-
-  // Allow the profiler to be started using signals
-  OS::RegisterStartHandler();
-
-  // 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");
-  if (!val || !*val) {
-    return;
-  }
-
-  const char* features[] = {"js", "leaf"
-#if defined(XP_WIN) || defined(XP_MACOSX)
-                         , "stackwalk"
-#endif
-                         };
-  mozilla_sampler_start1(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
-                         features, sizeof(features)/sizeof(const char*));
-}
-
-void mozilla_sampler_shutdown1()
-{
-  TableTicker *t = tlsTicker.get();
-  if (t) {
-    const char *val = PR_GetEnv("MOZ_PROFILER_SHUTDOWN");
-    if (val) {
-      std::ofstream stream;
-      stream.open(val);
-      if (stream.is_open()) {
-        t->ToStreamAsJSON(stream);
-        stream.close();
-      }
-    }
-  }
-
-  mozilla_sampler_stop1();
-  // We can't delete the Stack because we can be between a
-  // sampler call_enter/call_exit point.
-  // TODO Need to find a safe time to delete Stack
-}
-
-void mozilla_sampler_save1()
-{
-  TableTicker *t = tlsTicker.get();
-  if (!t) {
-    return;
-  }
-
-  t->RequestSave();
-  // We're on the main thread already so we don't
-  // have to wait to handle the save request.
-  t->HandleSaveRequest();
-}
-
-char* mozilla_sampler_get_profile1()
-{
-  TableTicker *t = tlsTicker.get();
-  if (!t) {
-    return NULL;
-  }
-
-  std::stringstream profile;
-  t->SetPaused(true);
-  profile << *(t->GetPrimaryThreadProfile());
-  t->SetPaused(false);
-
-  std::string profileString = profile.str();
-  char *rtn = (char*)malloc( (profileString.length() + 1) * sizeof(char) );
-  strcpy(rtn, profileString.c_str());
-  return rtn;
-}
-
-JSObject *mozilla_sampler_get_profile_data1(JSContext *aCx)
-{
-  TableTicker *t = tlsTicker.get();
-  if (!t) {
-    return NULL;
-  }
-
-  return t->ToJSObject(aCx);
-}
-
-
-const char** mozilla_sampler_get_features1()
-{
-  static const char* features[] = {
-#if defined(MOZ_PROFILING) && (defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK))
-    "stackwalk",
-#endif
-#if defined(ENABLE_SPS_LEAF_DATA)
-    "leaf",
-#endif
-    "jank",
-    "js",
-    NULL
-  };
-
-  return features;
-}
-
-// Values are only honored on the first start
-void mozilla_sampler_start1(int aProfileEntries, int aInterval,
-                            const char** aFeatures, uint32_t aFeatureCount)
-{
-  if (!stack_key_initialized)
-    mozilla_sampler_init1();
-
-  PseudoStack *stack = tlsPseudoStack.get();
-  if (!stack) {
-    ASSERT(false);
-    return;
-  }
-
-  mozilla_sampler_stop1();
-
-  TableTicker *t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
-                                   aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
-                                   stack, aFeatures, aFeatureCount);
-  tlsTicker.set(t);
-  t->Start();
-  if (t->ProfileJS())
-      stack->enableJSSampling();
-
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os)
-    os->NotifyObservers(nullptr, "profiler-started", nullptr);
-}
-
-void mozilla_sampler_stop1()
-{
-  if (!stack_key_initialized)
-    mozilla_sampler_init1();
-
-  TableTicker *t = tlsTicker.get();
-  if (!t) {
-    return;
-  }
-
-  bool disableJS = t->ProfileJS();
-
-  t->Stop();
-  delete t;
-  tlsTicker.set(NULL);
-  PseudoStack *stack = tlsPseudoStack.get();
-  ASSERT(stack != NULL);
-
-  if (disableJS)
-    stack->disableJSSampling();
-
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os)
-    os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
-}
-
-bool mozilla_sampler_is_active1()
-{
-  if (!stack_key_initialized)
-    mozilla_sampler_init1();
-
-  TableTicker *t = tlsTicker.get();
-  if (!t) {
-    return false;
-  }
-
-  return t->IsActive();
-}
-
-static double sResponsivenessTimes[100];
-static unsigned int sResponsivenessLoc = 0;
-void mozilla_sampler_responsiveness1(const TimeStamp& aTime)
-{
-  if (!sLastTracerEvent.IsNull()) {
-    if (sResponsivenessLoc == 100) {
-      for(size_t i = 0; i < 100-1; i++) {
-        sResponsivenessTimes[i] = sResponsivenessTimes[i+1];
-      }
-      sResponsivenessLoc--;
-    }
-    TimeDuration delta = aTime - sLastTracerEvent;
-    sResponsivenessTimes[sResponsivenessLoc++] = delta.ToMilliseconds();
-  }
-  sCurrentEventGeneration++;
-
-  sLastTracerEvent = aTime;
-}
-
-const double* mozilla_sampler_get_responsiveness1()
-{
-  return sResponsivenessTimes;
-}
-
-void mozilla_sampler_frame_number1(int frameNumber)
-{
-  sFrameNumber = frameNumber;
-}
-
 static void print_callback(const ProfileEntry& entry, const char* tagStringData) {
-  switch (entry.mTagName) {
+  switch (entry.getTagName()) {
     case 's':
     case 'c':
       printf_stderr("  %s\n", tagStringData);
   }
 }
 
 void mozilla_sampler_print_location1()
 {
   if (!stack_key_initialized)
-    mozilla_sampler_init1();
+    profiler_init();
 
   PseudoStack *stack = tlsPseudoStack.get();
   if (!stack) {
     MOZ_ASSERT(false);
     return;
   }
 
   ThreadProfile threadProfile(1000, stack);
   doSampleStackTrace(stack, threadProfile, NULL);
 
   threadProfile.flush();
 
   printf_stderr("Backtrace:\n");
   threadProfile.IterateTags(print_callback);
 }
 
-void mozilla_sampler_lock1()
-{
-  mozilla_sampler_stop1();
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os)
-    os->NotifyObservers(nullptr, "profiler-locked", nullptr);
-}
 
-void mozilla_sampler_unlock1()
-{
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os)
-    os->NotifyObservers(nullptr, "profiler-unlocked", nullptr);
-}
new file mode 100644
--- /dev/null
+++ b/tools/profiler/TableTicker.h
@@ -0,0 +1,105 @@
+/* -*- 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 "platform.h"
+#include "ProfileEntry.h"
+
+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;
+}
+
+void read_env_vars();
+typedef  enum { UnwINVALID, UnwNATIVE, UnwPSEUDO, UnwCOMBINED }  UnwMode;
+extern UnwMode sUnwindMode;
+extern int     sUnwindInterval;
+
+extern TimeStamp sLastTracerEvent;
+extern int sFrameNumber;
+extern int sLastFrameNumber;
+extern unsigned int sCurrentEventGeneration;
+extern unsigned int sLastSampledEventGeneration;
+
+class BreakpadSampler;
+
+class TableTicker: public Sampler {
+ public:
+  TableTicker(int aInterval, int aEntrySize, PseudoStack *aStack,
+              const char** aFeatures, uint32_t aFeatureCount)
+    : Sampler(aInterval, true)
+    , mPrimaryThreadProfile(aEntrySize, aStack)
+    , mStartTime(TimeStamp::Now())
+    , mSaveRequested(false)
+  {
+    mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
+
+    //XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
+    mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
+    mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
+    mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
+    mPrimaryThreadProfile.addTag(ProfileEntry('m', "Start"));
+  }
+
+  ~TableTicker() { if (IsActive()) Stop(); }
+
+  virtual void SampleStack(TickSample* sample) {}
+
+  // Called within a signal. This function must be reentrant
+  virtual void Tick(TickSample* sample);
+
+  // Called within a signal. This function must be reentrant
+  virtual void RequestSave()
+  {
+    mSaveRequested = true;
+  }
+
+  virtual void HandleSaveRequest();
+
+  ThreadProfile* GetPrimaryThreadProfile()
+  {
+    return &mPrimaryThreadProfile;
+  }
+
+  void ToStreamAsJSON(std::ostream& stream);
+  virtual JSObject *ToJSObject(JSContext *aCx);
+  JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b);
+
+  const bool ProfileJS() { return mProfileJS; }
+
+  virtual BreakpadSampler* AsBreakpadSampler() { return nullptr; }
+
+protected:
+  // Not implemented on platforms which do not support backtracing
+  void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
+
+  void BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile);
+
+  // This represent the application's main thread (SAMPLER_INIT)
+  ThreadProfile mPrimaryThreadProfile;
+  TimeStamp mStartTime;
+  bool mSaveRequested;
+  bool mAddLeafAddresses;
+  bool mUseStackWalk;
+  bool mJankOnly;
+  bool mProfileJS;
+};
+
+class BreakpadSampler: public TableTicker {
+ public:
+  BreakpadSampler(int aInterval, int aEntrySize, PseudoStack *aStack,
+              const char** aFeatures, uint32_t aFeatureCount)
+    : TableTicker(aInterval, aEntrySize, aStack, aFeatures, aFeatureCount)
+  {}
+
+  // Called within a signal. This function must be reentrant
+  virtual void Tick(TickSample* sample);
+
+  virtual BreakpadSampler* AsBreakpadSampler() { return this; }
+};
+
--- a/tools/profiler/UnwinderThread2.cpp
+++ b/tools/profiler/UnwinderThread2.cpp
@@ -23,17 +23,17 @@
 
 #include "mozilla/arm.h"
 #include "mozilla/StandardInteger.h"
 #include "PlatformMacros.h"
 
 #include "platform.h"
 #include <iostream>
 
-#include "ProfileEntry2.h"
+#include "ProfileEntry.h"
 #include "UnwinderThread2.h"
 
 #if !defined(SPS_OS_windows)
 # include <sys/time.h>
 # include <unistd.h>
 # include <pthread.h>
   // mmap
 # include <sys/mman.h>
@@ -85,25 +85,25 @@ void uwt__register_thread_for_profiling 
 // RUNS IN SIGHANDLER CONTEXT
 UnwinderThreadBuffer* uwt__acquire_empty_buffer()
 {
   return NULL;
 }
 
 // RUNS IN SIGHANDLER CONTEXT
 void
-uwt__release_full_buffer(ThreadProfile2* aProfile,
+uwt__release_full_buffer(ThreadProfile* aProfile,
                          UnwinderThreadBuffer* utb,
                          void* /* ucontext_t*, really */ ucV )
 {
 }
 
 // RUNS IN SIGHANDLER CONTEXT
 void
-utb__addEntry(/*MODIFIED*/UnwinderThreadBuffer* utb, ProfileEntry2 ent)
+utb__addEntry(/*MODIFIED*/UnwinderThreadBuffer* utb, ProfileEntry ent)
 {
 }
 
 //// END externally visible functions (WINDOWS STUBS)
 //////////////////////////////////////////////////////////
 
 #else // a supported target
 
@@ -128,22 +128,22 @@ static UnwinderThreadBuffer* acquire_emp
 // RUNS IN SIGHANDLER CONTEXT
 // Put this buffer in the queue of stuff going to the unwinder
 // thread, and mark it as FULL.  Before doing that, fill in stack
 // chunk and register fields if a native unwind is requested.
 // APROFILE is where the profile data should be added to.  UTB
 // is the partially-filled-in buffer, containing ProfileEntries.
 // UCV is the ucontext_t* from the signal handler.  If non-NULL, is
 // taken as a cue to request native unwind.
-static void release_full_buffer(ThreadProfile2* aProfile,
+static void release_full_buffer(ThreadProfile* aProfile,
                                 UnwinderThreadBuffer* utb,
                                 void* /* ucontext_t*, really */ ucV );
 
 // RUNS IN SIGHANDLER CONTEXT
-static void utb_add_prof_ent(UnwinderThreadBuffer* utb, ProfileEntry2 ent);
+static void utb_add_prof_ent(UnwinderThreadBuffer* utb, ProfileEntry ent);
 
 // Do a store memory barrier.
 static void do_MBAR();
 
 
 void uwt__init()
 {
   // Create the unwinder thread.
@@ -170,26 +170,26 @@ void uwt__register_thread_for_profiling(
 // RUNS IN SIGHANDLER CONTEXT
 UnwinderThreadBuffer* uwt__acquire_empty_buffer()
 {
   return acquire_empty_buffer();
 }
 
 // RUNS IN SIGHANDLER CONTEXT
 void
-uwt__release_full_buffer(ThreadProfile2* aProfile,
+uwt__release_full_buffer(ThreadProfile* aProfile,
                          UnwinderThreadBuffer* utb,
                          void* /* ucontext_t*, really */ ucV )
 {
   release_full_buffer( aProfile, utb, ucV );
 }
 
 // RUNS IN SIGHANDLER CONTEXT
 void
-utb__addEntry(/*MODIFIED*/UnwinderThreadBuffer* utb, ProfileEntry2 ent)
+utb__addEntry(/*MODIFIED*/UnwinderThreadBuffer* utb, ProfileEntry ent)
 {
   utb_add_prof_ent(utb, ent);
 }
 
 //// END externally visible functions
 //////////////////////////////////////////////////////////
 
 
@@ -248,34 +248,34 @@ typedef  enum { S_EMPTY, S_FILLING, S_EM
 
 typedef  struct { uintptr_t val; }  SpinLock;
 
 /* CONFIGURABLE */
 /* The maximum number of bytes in a stack snapshot */
 #define N_STACK_BYTES 32768
 
 /* CONFIGURABLE */
-/* The number of fixed ProfileEntry2 slots.  If more are required, they
+/* The number of fixed ProfileEntry slots.  If more are required, they
    are placed in mmap'd pages. */
 #define N_FIXED_PROF_ENTS 20
 
 /* CONFIGURABLE */
 /* The number of extra pages of ProfileEntries.  If (on arm) each
-   ProfileEntry2 is 8 bytes, then a page holds 512, and so 100 pages
+   ProfileEntry is 8 bytes, then a page holds 512, and so 100 pages
    is enough to hold 51200. */
 #define N_PROF_ENT_PAGES 100
 
 /* DERIVATIVE */
-#define N_PROF_ENTS_PER_PAGE (SPS_PAGE_SIZE / sizeof(ProfileEntry2))
+#define N_PROF_ENTS_PER_PAGE (SPS_PAGE_SIZE / sizeof(ProfileEntry))
 
-/* A page of ProfileEntry2s.  This might actually be slightly smaller
+/* A page of ProfileEntrys.  This might actually be slightly smaller
    than a page if SPS_PAGE_SIZE is not an exact multiple of
-   sizeof(ProfileEntry2). */
+   sizeof(ProfileEntry). */
 typedef
-  struct { ProfileEntry2 ents[N_PROF_ENTS_PER_PAGE]; }
+  struct { ProfileEntry ents[N_PROF_ENTS_PER_PAGE]; }
   ProfEntsPage;
 
 #define ProfEntsPage_INVALID ((ProfEntsPage*)1)
 
 
 /* Fields protected by the spinlock are marked SL */
 
 struct _UnwinderThreadBuffer {
@@ -284,21 +284,21 @@ struct _UnwinderThreadBuffer {
      ::state is S_FILLING, they are 'owned' by the sampler thread
      that set the state to S_FILLING.  If ::state is S_EMPTYING,
      they are 'owned' by the unwinder thread that set the state to
      S_EMPTYING.  If ::state is S_EMPTY or S_FULL, the buffer isn't
      owned by any thread, and so no thread may access these
      fields. */
   /* Sample number, needed to process samples in order */
   uint64_t       seqNo;
-  /* The ThreadProfile2 into which the results are eventually to be
+  /* The ThreadProfile into which the results are eventually to be
      dumped. */
-  ThreadProfile2* aProfile;
+  ThreadProfile* aProfile;
   /* Pseudostack and other info, always present */
-  ProfileEntry2   entsFixed[N_FIXED_PROF_ENTS];
+  ProfileEntry   entsFixed[N_FIXED_PROF_ENTS];
   ProfEntsPage*  entsPages[N_PROF_ENT_PAGES];
   uintptr_t      entsUsed;
   /* Do we also have data to do a native unwind? */
   bool           haveNativeInfo;
   /* If so, here is the register state and stack.  Unset if
      .haveNativeInfo is false. */
   ArchRegs       regs;
   unsigned char  stackImg[N_STACK_BYTES];
@@ -600,17 +600,17 @@ static UnwinderThreadBuffer* acquire_emp
     buff->entsPages[i] = ProfEntsPage_INVALID;
   return buff;
 }
 
 
 // RUNS IN SIGHANDLER CONTEXT
 /* The calling thread owns the buffer, as denoted by its state being
    S_FILLING.  So we can mess with it without further locking. */
-static void release_full_buffer(ThreadProfile2* aProfile,
+static void release_full_buffer(ThreadProfile* aProfile,
                                 UnwinderThreadBuffer* buff,
                                 void* /* ucontext_t*, really */ ucV )
 {
   MOZ_ASSERT(buff->state == S_FILLING);
 
   ////////////////////////////////////////////////////
   // BEGIN fill
 
@@ -740,23 +740,23 @@ static void munmap_ProfEntsPage(ProfEnts
 {
   MOZ_ALWAYS_TRUE(is_page_aligned(pep));
   ::munmap(pep, sizeof(ProfEntsPage));
 }
 
 
 // RUNS IN SIGHANDLER CONTEXT
 void
-utb_add_prof_ent(/*MODIFIED*/UnwinderThreadBuffer* utb, ProfileEntry2 ent)
+utb_add_prof_ent(/*MODIFIED*/UnwinderThreadBuffer* utb, ProfileEntry ent)
 {
   uintptr_t limit
     = N_FIXED_PROF_ENTS + (N_PROF_ENTS_PER_PAGE * N_PROF_ENT_PAGES);
   if (utb->entsUsed == limit) {
     /* We're full.  Now what? */
-    LOG("BPUnw: utb__addEntry: NO SPACE for ProfileEntry2; ignoring.");
+    LOG("BPUnw: utb__addEntry: NO SPACE for ProfileEntry; ignoring.");
     return;
   }
   MOZ_ASSERT(utb->entsUsed < limit);
 
   /* Will it fit in the fixed array? */
   if (utb->entsUsed < N_FIXED_PROF_ENTS) {
     utb->entsFixed[utb->entsUsed] = ent;
     utb->entsUsed++;
@@ -768,28 +768,28 @@ utb_add_prof_ent(/*MODIFIED*/UnwinderThr
   uintptr_t j     = i - N_FIXED_PROF_ENTS;
   uintptr_t j_div = j / N_PROF_ENTS_PER_PAGE; /* page number */
   uintptr_t j_mod = j % N_PROF_ENTS_PER_PAGE; /* page offset */
   ProfEntsPage* pep = utb->entsPages[j_div];
   if (pep == ProfEntsPage_INVALID) {
     pep = mmap_anon_ProfEntsPage();
     if (pep == ProfEntsPage_INVALID) {
       /* Urr, we ran out of memory.  Now what? */
-      LOG("BPUnw: utb__addEntry: MMAP FAILED for ProfileEntry2; ignoring.");
+      LOG("BPUnw: utb__addEntry: MMAP FAILED for ProfileEntry; ignoring.");
       return;
     }
     utb->entsPages[j_div] = pep;
   }
   pep->ents[j_mod] = ent;
   utb->entsUsed++;
 }
 
 
 // misc helper
-static ProfileEntry2 utb_get_profent(UnwinderThreadBuffer* buff, uintptr_t i)
+static ProfileEntry utb_get_profent(UnwinderThreadBuffer* buff, uintptr_t i)
 {
   MOZ_ASSERT(i < buff->entsUsed);
   if (i < N_FIXED_PROF_ENTS) {
     return buff->entsFixed[i];
   } else {
     uintptr_t j     = i - N_FIXED_PROF_ENTS;
     uintptr_t j_div = j / N_PROF_ENTS_PER_PAGE; /* page number */
     uintptr_t j_mod = j % N_PROF_ENTS_PER_PAGE; /* page offset */
@@ -944,17 +944,17 @@ static void* unwind_thr_fn(void* exit_no
        has been requested. */
     const uintptr_t infUW = ~(uintptr_t)0; // infinity
     bool  need_native_unw = false;
     uintptr_t ix_first_hP = infUW; // "not found"
     uintptr_t ix_last_hQ  = infUW; // "not found"
 
     uintptr_t k;
     for (k = 0; k < buff->entsUsed; k++) {
-      ProfileEntry2 ent = utb_get_profent(buff, k);
+      ProfileEntry ent = utb_get_profent(buff, k);
       if (ent.is_ent_hint('N')) {
         need_native_unw = true;
       }
       else if (ent.is_ent_hint('P') && ix_first_hP == ~(uintptr_t)0) {
         ix_first_hP = k;
       }
       else if (ent.is_ent_hint('Q')) {
         ix_last_hQ = k;
@@ -976,39 +976,39 @@ static void* unwind_thr_fn(void* exit_no
       MOZ_ASSERT(ix_first_hP < ix_last_hQ);
       MOZ_ASSERT(ix_last_hQ <= buff->entsUsed);
     }
 
     /* Neither N nor P.  This is very unusual but has been observed to happen.
        Just copy to the output. */
     if (!need_native_unw && !have_P) {
       for (k = 0; k < buff->entsUsed; k++) {
-        ProfileEntry2 ent = utb_get_profent(buff, k);
+        ProfileEntry ent = utb_get_profent(buff, k);
         // action flush-hints
         if (ent.is_ent_hint('F')) { buff->aProfile->flush(); continue; }
         // skip ones we can't copy
         if (ent.is_ent_hint() || ent.is_ent('S')) { continue; }
         // and copy everything else
         buff->aProfile->addTag( ent );
       }
     }
     else /* Native only-case. */
     if (need_native_unw && !have_P) {
       for (k = 0; k < buff->entsUsed; k++) {
-        ProfileEntry2 ent = utb_get_profent(buff, k);
+        ProfileEntry ent = utb_get_profent(buff, k);
         // action a native-unwind-now hint
         if (ent.is_ent_hint('N')) {
           MOZ_ASSERT(buff->haveNativeInfo);
           PCandSP* pairs = NULL;
           unsigned int nPairs = 0;
           do_breakpad_unwind_Buffer(&pairs, &nPairs, buff, oldest_ix);
-          buff->aProfile->addTag( ProfileEntry2('s', "(root)") );
+          buff->aProfile->addTag( ProfileEntry('s', "(root)") );
           for (unsigned int i = 0; i < nPairs; i++) {
             buff->aProfile
-                ->addTag( ProfileEntry2('l', reinterpret_cast<void*>(pairs[i].pc)) );
+                ->addTag( ProfileEntry('l', reinterpret_cast<void*>(pairs[i].pc)) );
           }
           if (pairs)
             free(pairs);
           continue;
         }
         // action flush-hints
         if (ent.is_ent_hint('F')) { buff->aProfile->flush(); continue; }
         // skip ones we can't copy
@@ -1020,20 +1020,20 @@ static void* unwind_thr_fn(void* exit_no
     else /* Pseudostack-only case */
     if (!need_native_unw && have_P) {
       /* If there's no request for a native stack, it's easy: just
          copy the tags verbatim into aProfile, skipping the ones that
          can't be copied -- 'h' (hint) tags, and "'S' (void*)"
          stack-pointer tags.  Except, insert a sample-start tag when
          we see the start of the first pseudostack frame. */
       for (k = 0; k < buff->entsUsed; k++) {
-        ProfileEntry2 ent = utb_get_profent(buff, k);
+        ProfileEntry ent = utb_get_profent(buff, k);
         // We need to insert a sample-start tag before the first frame
         if (k == ix_first_hP) {
-          buff->aProfile->addTag( ProfileEntry2('s', "(root)") );
+          buff->aProfile->addTag( ProfileEntry('s', "(root)") );
         }
         // action flush-hints
         if (ent.is_ent_hint('F')) { buff->aProfile->flush(); continue; }
         // skip ones we can't copy
         if (ent.is_ent_hint() || ent.is_ent('S')) { continue; }
         // and copy everything else
         buff->aProfile->addTag( ent );
       }
@@ -1051,27 +1051,27 @@ static void* unwind_thr_fn(void* exit_no
 
       // Get native unwind info
       PCandSP* pairs = NULL;
       unsigned int n_pairs = 0;
       do_breakpad_unwind_Buffer(&pairs, &n_pairs, buff, oldest_ix);
 
       // Entries before the pseudostack frames
       for (k = 0; k < ix_first_hP; k++) {
-        ProfileEntry2 ent = utb_get_profent(buff, k);
+        ProfileEntry ent = utb_get_profent(buff, k);
         // action flush-hints
         if (ent.is_ent_hint('F')) { buff->aProfile->flush(); continue; }
         // skip ones we can't copy
         if (ent.is_ent_hint() || ent.is_ent('S')) { continue; }
         // and copy everything else
         buff->aProfile->addTag( ent );
       }
 
       // BEGIN merge
-      buff->aProfile->addTag( ProfileEntry2('s', "(root)") );
+      buff->aProfile->addTag( ProfileEntry('s', "(root)") );
       unsigned int next_N = 0; // index in pairs[]
       unsigned int next_P = ix_first_hP; // index in buff profent array
       bool last_was_P = false;
       if (0) LOGF("at mergeloop: n_pairs %llu ix_last_hQ %llu",
                   (unsigned long long int)n_pairs,
                   (unsigned long long int)ix_last_hQ);
       while (true) {
         if (next_P <= ix_last_hQ) {
@@ -1106,17 +1106,17 @@ static void* unwind_thr_fn(void* exit_no
           // Scan forwards to find the SP of the current P entry
           u_int64_t sp_cur_P = 0;
           unsigned int m = next_P + 1;
           while (1) {
             /* This assertion should hold because in a well formed
                input, we must eventually find the hint-Q that marks
                the end of this frame's entries. */
             MOZ_ASSERT(m < buff->entsUsed);
-            ProfileEntry2 ent = utb_get_profent(buff, m);
+            ProfileEntry ent = utb_get_profent(buff, m);
             if (ent.is_ent_hint('Q'))
               break;
             if (ent.is_ent('S')) {
               sp_cur_P = reinterpret_cast<u_int64_t>(ent.get_tagPtr());
               break;
             }
             m++;
           }
@@ -1131,45 +1131,45 @@ static void* unwind_thr_fn(void* exit_no
                                            (void*)(intptr_t)sp_cur_N);
           }
         }
         /* So, we know which we are going to use. */
         if (use_P) {
           unsigned int m = next_P + 1;
           while (true) {
             MOZ_ASSERT(m < buff->entsUsed);
-            ProfileEntry2 ent = utb_get_profent(buff, m);
+            ProfileEntry ent = utb_get_profent(buff, m);
             if (ent.is_ent_hint('Q')) {
               next_P = m + 1;
               break;
             }
             // we don't expect a flush-hint here
             MOZ_ASSERT(!ent.is_ent_hint('F'));
             // skip ones we can't copy
             if (ent.is_ent_hint() || ent.is_ent('S')) { m++; continue; }
             // and copy everything else
             buff->aProfile->addTag( ent );
             m++;
           }
         } else {
           buff->aProfile
-              ->addTag( ProfileEntry2('l', reinterpret_cast<void*>(pairs[next_N].pc)) );
+              ->addTag( ProfileEntry('l', reinterpret_cast<void*>(pairs[next_N].pc)) );
           next_N++;
         }
         /* Remember what we chose, for next time. */
         last_was_P = use_P;
       }
 
       MOZ_ASSERT(next_P == ix_last_hQ + 1);
       MOZ_ASSERT(next_N == n_pairs);
       // END merge
 
       // Entries after the pseudostack frames
       for (k = ix_last_hQ+1; k < buff->entsUsed; k++) {
-        ProfileEntry2 ent = utb_get_profent(buff, k);
+        ProfileEntry ent = utb_get_profent(buff, k);
         // action flush-hints
         if (ent.is_ent_hint('F')) { buff->aProfile->flush(); continue; }
         // skip ones we can't copy
         if (ent.is_ent_hint() || ent.is_ent('S')) { continue; }
         // and copy everything else
         buff->aProfile->addTag( ent );
       }
 
@@ -1177,32 +1177,32 @@ static void* unwind_thr_fn(void* exit_no
       if (pairs)
         free(pairs);
     }
 
 #if 0
     bool show = true;
     if (show) LOG("----------------");
     for (k = 0; k < buff->entsUsed; k++) {
-      ProfileEntry2 ent = utb_get_profent(buff, k);
+      ProfileEntry ent = utb_get_profent(buff, k);
       if (show) ent.log();
       if (ent.is_ent_hint('F')) {
         /* This is a flush-hint */
         buff->aProfile->flush();
       } 
       else if (ent.is_ent_hint('N')) {
         /* This is a do-a-native-unwind-right-now hint */
         MOZ_ASSERT(buff->haveNativeInfo);
         PCandSP* pairs = NULL;
         unsigned int nPairs = 0;
         do_breakpad_unwind_Buffer(&pairs, &nPairs, buff, oldest_ix);
-        buff->aProfile->addTag( ProfileEntry2('s', "(root)") );
+        buff->aProfile->addTag( ProfileEntry('s', "(root)") );
         for (unsigned int i = 0; i < nPairs; i++) {
           buff->aProfile
-              ->addTag( ProfileEntry2('l', reinterpret_cast<void*>(pairs[i].pc)) );
+              ->addTag( ProfileEntry('l', reinterpret_cast<void*>(pairs[i].pc)) );
         }
         if (pairs)
           free(pairs);
       } else {
         /* Copy in verbatim */
         buff->aProfile->addTag( ent );
       }
     }
--- a/tools/profiler/UnwinderThread2.h
+++ b/tools/profiler/UnwinderThread2.h
@@ -2,32 +2,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZ_UNWINDER_THREAD_2_H
 #define MOZ_UNWINDER_THREAD_2_H
 
 #include "GeckoProfilerImpl.h"
-#include "ProfileEntry2.h"
+#include "ProfileEntry.h"
 
 /* Top level exports of UnwinderThread.cpp. */
 
 // Abstract type.  A buffer which is used to transfer information between
 // the sampled thread(s) and the unwinder thread(s).
 typedef
   struct _UnwinderThreadBuffer 
   UnwinderThreadBuffer;
 
 // RUNS IN SIGHANDLER CONTEXT
-// Called in the sampled thread (signal) context.  Adds a ProfileEntry2
+// Called in the sampled thread (signal) context.  Adds a ProfileEntry
 // into an UnwinderThreadBuffer that the thread has previously obtained
 // by a call to utb__acquire_empty_buffer.
 void utb__addEntry(/*MOD*/UnwinderThreadBuffer* utb,
-                   ProfileEntry2 ent);
+                   ProfileEntry ent);
 
 // Create the unwinder thread.  At the moment there can be only one.
 void uwt__init();
 
 // Request the unwinder thread to exit, and wait until it has done so.
 void uwt__deinit();
 
 // Registers a sampler thread for profiling.  Threads must be registered
@@ -48,13 +48,13 @@ UnwinderThreadBuffer* uwt__acquire_empty
 // Called in the sampled thread (signal) context.  Release a buffer
 // that the sampled thread has acquired, handing the contents to
 // the unwinder thread, and, if necessary, passing sufficient
 // information (stack top chunk, + registers) to also do a native
 // unwind.  If 'ucV' is NULL, no native unwind is done.  If non-NULL,
 // it is assumed to point to a ucontext_t* that holds the initial 
 // register state for the unwind.  The results of all of this are
 // dumped into |aProfile| (by the unwinder thread, not the calling thread).
-void uwt__release_full_buffer(ThreadProfile2* aProfile,
+void uwt__release_full_buffer(ThreadProfile* aProfile,
                               UnwinderThreadBuffer* utb,
                               void* /* ucontext_t*, really */ ucV);
 
 #endif /* ndef MOZ_UNWINDER_THREAD_2_H */
new file mode 100644
--- /dev/null
+++ b/tools/profiler/platform.cpp
@@ -0,0 +1,451 @@
+/* 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 <iostream>
+#include <fstream>
+#include <sstream>
+#include <errno.h>
+
+#include "platform.h"
+#include "PlatformMacros.h"
+#include "prenv.h"
+#include "mozilla/ThreadLocal.h"
+#include "PseudoStack.h"
+#include "TableTicker.h"
+#include "UnwinderThread2.h"
+#include "nsIObserverService.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "mozilla/Services.h"
+
+mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
+mozilla::ThreadLocal<TableTicker *> tlsTicker;
+// 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.
+bool stack_key_initialized;
+
+TimeStamp sLastTracerEvent; // is raced on
+int       sFrameNumber = 0;
+int       sLastFrameNumber = 0;
+
+/* used to keep track of the last event that we sampled during */
+unsigned int sLastSampledEventGeneration = 0;
+
+/* a counter that's incremented everytime we get responsiveness event
+ * note: it might also be worth tracking everytime we go around
+ * the event loop */
+unsigned int sCurrentEventGeneration = 0;
+/* we don't need to worry about overflow because we only treat the
+ * case of them being the same as special. i.e. we only run into
+ * a problem if 2^32 events happen between samples that we need
+ * to know are associated with different events */
+
+bool sps_version2()
+{
+  static int version = 0; // Raced on, potentially
+
+  if (version == 0) {
+    bool allow2 = false; // Is v2 allowable on this platform?
+#   if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
+       || defined(SPS_PLAT_x86_linux)
+    allow2 = true;
+#   elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) \
+         || defined(SPS_PLAT_x86_windows) || defined(SPS_PLAT_x86_android) \
+         || defined(SPS_PLAT_amd64_windows)
+    allow2 = false;
+#   else
+#     error "Unknown platform"
+#   endif
+
+    bool req2 = PR_GetEnv("MOZ_PROFILER_NEW") != NULL; // Has v2 been requested?
+
+    bool elfhackd = false;
+#   if defined(USE_ELF_HACK)
+    bool elfhackd = true;
+#   endif
+
+    if (req2 && allow2) {
+      version = 2;
+      LOG("------------------- MOZ_PROFILER_NEW set -------------------");
+    } else if (req2 && !allow2) {
+      version = 1;
+      LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
+      LOG("---------- but is not available on this platform -----------");
+    } else if (req2 && elfhackd) {
+      version = 1;
+      LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
+      LOG("--- but this build was not done with --disable-elf-hack ----");
+    } else {
+      version = 1;
+      LOG("----------------- MOZ_PROFILER_NEW not set -----------------");
+    }
+  }
+  return version == 2;
+}
+
+static inline const char* name_UnwMode(UnwMode m)
+{
+  switch (m) {
+    case UnwINVALID:  return "invalid";
+    case UnwNATIVE:   return "native";
+    case UnwPSEUDO:   return "pseudo";
+    case UnwCOMBINED: return "combined";
+    default:          return "??name_UnwMode??";
+  }
+}
+
+// Read env vars at startup, so as to set sUnwindMode and sInterval.
+void read_env_vars()
+{
+  bool nativeAvail = false;
+# if defined(HAVE_NATIVE_UNWIND)
+  nativeAvail = true;
+# endif
+
+  MOZ_ASSERT(sUnwindMode     == UnwINVALID);
+  MOZ_ASSERT(sUnwindInterval == 0);
+
+  /* Set defaults */
+  sUnwindMode     = nativeAvail ? UnwCOMBINED : UnwPSEUDO;
+  sUnwindInterval = 0;  /* We'll have to look elsewhere */
+
+  const char* strM = PR_GetEnv("MOZ_PROFILER_MODE");
+  const char* strI = PR_GetEnv("MOZ_PROFILER_INTERVAL");
+
+  if (strM) {
+    if (0 == strcmp(strM, "pseudo"))
+      sUnwindMode = UnwPSEUDO;
+    else if (0 == strcmp(strM, "native") && nativeAvail)
+      sUnwindMode = UnwNATIVE;
+    else if (0 == strcmp(strM, "combined") && nativeAvail)
+      sUnwindMode = UnwCOMBINED;
+    else goto usage;
+  }
+
+  if (strI) {
+    errno = 0;
+    long int n = strtol(strI, (char**)NULL, 10);
+    if (errno == 0 && n >= 1 && n <= 1000) {
+      sUnwindInterval = n;
+    }
+    else goto usage;
+  }
+
+  goto out;
+
+ usage:
+  LOG( "SPS: ");
+  LOG( "SPS: Environment variable usage:");
+  LOG( "SPS: ");
+  LOG( "SPS:   MOZ_PROFILER_MODE=native    for native unwind only");
+  LOG( "SPS:   MOZ_PROFILER_MODE=pseudo    for pseudo unwind only");
+  LOG( "SPS:   MOZ_PROFILER_MODE=combined  for combined native & pseudo unwind");
+  LOG( "SPS:   If unset, default is 'combined' on native-capable");
+  LOG( "SPS:     platforms, 'pseudo' on others.");
+  LOG( "SPS: ");
+  LOG( "SPS:   MOZ_PROFILER_INTERVAL=<number>   (milliseconds, 1 to 1000)");
+  LOG( "SPS:   If unset, platform default is used.");
+  LOG( "SPS: ");
+  LOGF("SPS:   This platform %s native unwinding.",
+       nativeAvail ? "supports" : "does not support");
+  LOG( "SPS: ");
+  /* Re-set defaults */
+  sUnwindMode       = nativeAvail ? UnwCOMBINED : UnwPSEUDO;
+  sUnwindInterval   = 0;  /* We'll have to look elsewhere */
+
+ out:
+  LOG( "SPS:");
+  LOGF("SPS: Unwind mode       = %s", name_UnwMode(sUnwindMode));
+  LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")",
+       (int)sUnwindInterval);
+  LOG( "SPS: Use env var MOZ_PROFILER_MODE=help for further information.");
+  LOG( "SPS:");
+
+  return;
+}
+
+////////////////////////////////////////////////////////////////////////
+// BEGIN externally visible functions
+
+void mozilla_sampler_init()
+{
+  if (stack_key_initialized)
+    return;
+
+  LOG("BEGIN mozilla_sampler_init");
+  if (!tlsPseudoStack.init() || !tlsTicker.init()) {
+    LOG("Failed to init.");
+    return;
+  }
+  stack_key_initialized = true;
+
+  PseudoStack *stack = new PseudoStack();
+  tlsPseudoStack.set(stack);
+
+  if (sps_version2()) {
+    // Read mode settings from MOZ_PROFILER_MODE and interval
+    // settings from MOZ_PROFILER_INTERVAL.
+    read_env_vars();
+
+    // Create the unwinder thread.  ATM there is only one.
+    uwt__init();
+
+# if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
+     || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android) \
+     || defined(SPS_PLAT_x86_windows) || defined(SPS_PLAT_amd64_windows) /* no idea if windows is correct */
+    // On Linuxes, register this thread (temporarily) for profiling
+    int aLocal;
+    uwt__register_thread_for_profiling( &aLocal );
+# elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin)
+    // Registration is done in platform-macos.cc
+# else
+#   error "Unknown plat"
+# endif
+  }
+
+  // Allow the profiler to be started using signals
+  OS::RegisterStartHandler();
+
+  // 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");
+  if (!val || !*val) {
+    return;
+  }
+
+  const char* features[] = {"js"
+                         , "leaf"
+#if defined(XP_WIN) || defined(XP_MACOSX)
+                         , "stackwalk"
+#endif
+                         };
+  profiler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
+                         features, sizeof(features)/sizeof(const char*));
+  LOG("END   mozilla_sampler_init");
+}
+
+void mozilla_sampler_shutdown()
+{
+  // Save the profile on shutdown if requested.
+  TableTicker *t = tlsTicker.get();
+  if (t) {
+    const char *val = PR_GetEnv("MOZ_PROFILER_SHUTDOWN");
+    if (val) {
+      std::ofstream stream;
+      stream.open(val);
+      if (stream.is_open()) {
+        t->ToStreamAsJSON(stream);
+        stream.close();
+      }
+    }
+  }
+
+  // Shut down and reap the unwinder thread.  We have to do this
+  // before stopping the sampler, so as to guarantee that the unwinder
+  // thread doesn't try to access memory that the subsequent call to
+  // mozilla_sampler_stop causes to be freed.
+  if (sps_version2()) {
+    uwt__deinit();
+  }
+
+  profiler_stop();
+  // We can't delete the Stack because we can be between a
+  // sampler call_enter/call_exit point.
+  // TODO Need to find a safe time to delete Stack
+}
+
+void mozilla_sampler_save()
+{
+  TableTicker *t = tlsTicker.get();
+  if (!t) {
+    return;
+  }
+
+  t->RequestSave();
+  // We're on the main thread already so we don't
+  // have to wait to handle the save request.
+  t->HandleSaveRequest();
+}
+
+char* mozilla_sampler_get_profile()
+{
+  TableTicker *t = tlsTicker.get();
+  if (!t) {
+    return NULL;
+  }
+
+  std::stringstream profile;
+  t->SetPaused(true);
+  profile << *(t->GetPrimaryThreadProfile());
+  t->SetPaused(false);
+
+  std::string profileString = profile.str();
+  char *rtn = (char*)malloc( (profileString.length() + 1) * sizeof(char) );
+  strcpy(rtn, profileString.c_str());
+  return rtn;
+}
+
+JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
+{
+  TableTicker *t = tlsTicker.get();
+  if (!t) {
+    return NULL;
+  }
+
+  return t->ToJSObject(aCx);
+}
+
+
+const char** mozilla_sampler_get_features()
+{
+  static const char* features[] = {
+#if defined(MOZ_PROFILING) && defined(HAVE_NATIVE_UNWIND)
+    "stackwalk",
+#endif
+#if defined(ENABLE_SPS_LEAF_DATA)
+    "leaf",
+#endif
+    "jank",
+    "js",
+    NULL
+  };
+
+  return features;
+}
+
+// Values are only honored on the first start
+void mozilla_sampler_start(int aProfileEntries, int aInterval,
+                           const char** aFeatures, uint32_t aFeatureCount)
+{
+  if (!stack_key_initialized)
+    profiler_init();
+
+  /* If the sampling interval was set using env vars, use that
+     in preference to anything else. */
+  if (sUnwindInterval > 0)
+    aInterval = sUnwindInterval;
+
+  PseudoStack *stack = tlsPseudoStack.get();
+  if (!stack) {
+    ASSERT(false);
+    return;
+  }
+
+  // Reset the current state if the profiler is running
+  profiler_stop();
+
+  TableTicker* t;
+  if (sps_version2()) {
+    t = new BreakpadSampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
+                           aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
+                           stack, aFeatures, aFeatureCount);
+  } else {
+    t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
+                        aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
+                        stack, aFeatures, aFeatureCount);
+  }
+  tlsTicker.set(t);
+  t->Start();
+  if (t->ProfileJS())
+      stack->enableJSSampling();
+
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os)
+    os->NotifyObservers(nullptr, "profiler-started", nullptr);
+}
+
+void mozilla_sampler_stop()
+{
+  if (!stack_key_initialized)
+    profiler_init();
+
+  TableTicker *t = tlsTicker.get();
+  if (!t) {
+    return;
+  }
+
+  bool disableJS = t->ProfileJS();
+
+  t->Stop();
+  delete t;
+  tlsTicker.set(NULL);
+  PseudoStack *stack = tlsPseudoStack.get();
+  ASSERT(stack != NULL);
+
+  if (disableJS)
+    stack->disableJSSampling();
+
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os)
+    os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
+}
+
+bool mozilla_sampler_is_active()
+{
+  if (!stack_key_initialized)
+    profiler_init();
+
+  TableTicker *t = tlsTicker.get();
+  if (!t) {
+    return false;
+  }
+
+  return t->IsActive();
+}
+
+static double sResponsivenessTimes[100];
+static unsigned int sResponsivenessLoc = 0;
+void mozilla_sampler_responsiveness(const TimeStamp& aTime)
+{
+  if (!sLastTracerEvent.IsNull()) {
+    if (sResponsivenessLoc == 100) {
+      for(size_t i = 0; i < 100-1; i++) {
+        sResponsivenessTimes[i] = sResponsivenessTimes[i+1];
+      }
+      sResponsivenessLoc--;
+    }
+    TimeDuration delta = aTime - sLastTracerEvent;
+    sResponsivenessTimes[sResponsivenessLoc++] = delta.ToMilliseconds();
+  }
+  sCurrentEventGeneration++;
+
+  sLastTracerEvent = aTime;
+}
+
+const double* mozilla_sampler_get_responsiveness()
+{
+  return sResponsivenessTimes;
+}
+
+void mozilla_sampler_frame_number(int frameNumber)
+{
+  sFrameNumber = frameNumber;
+}
+
+void mozilla_sampler_print_location2()
+{
+  // FIXME
+}
+
+void mozilla_sampler_lock()
+{
+  profiler_stop();
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os)
+    os->NotifyObservers(nullptr, "profiler-locked", nullptr);
+}
+
+void mozilla_sampler_unlock()
+{
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os)
+    os->NotifyObservers(nullptr, "profiler-unlocked", nullptr);
+}
+
+// END externally visible functions
+////////////////////////////////////////////////////////////////////////
+
--- a/tools/profiler/platform.h
+++ b/tools/profiler/platform.h
@@ -37,16 +37,17 @@
 #else
 #define __android_log_print(a, ...)
 #endif
 
 #include "mozilla/StandardInteger.h"
 #include "mozilla/Util.h"
 #include "mozilla/unused.h"
 #include "mozilla/TimeStamp.h"
+#include "PlatformMacros.h"
 #include "v8-support.h"
 #include <vector>
 #define ASSERT(a) MOZ_ASSERT(a)
 #ifdef ANDROID
 #if defined(__arm__) || defined(__thumb__)
 #define ENABLE_SPS_LEAF_DATA
 #define ENABLE_ARM_LR_SAVING
 #endif
@@ -185,17 +186,32 @@ class Thread {
   PlatformData* data_;
 
   char name_[kMaxThreadNameLength];
   int stack_size_;
 
   DISALLOW_COPY_AND_ASSIGN(Thread);
 };
 
+// ----------------------------------------------------------------------------
+// HAVE_NATIVE_UNWIND
+//
+// Pseudo backtraces are available on all platforms.  Native
+// backtraces are available only on selected platforms.  Breakpad is
+// the only supported native unwinder.  HAVE_NATIVE_UNWIND is set at
+// build time to indicate whether native unwinding is possible on this
+// platform.  The actual unwind mode currently in use is stored in
+// sUnwindMode.
 
+#undef HAVE_NATIVE_UNWIND
+#if defined(MOZ_PROFILING) \
+    && (defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
+        || defined(SPS_PLAT_x86_linux))
+# define HAVE_NATIVE_UNWIND
+#endif
 
 // ----------------------------------------------------------------------------
 // Sampler
 //
 // A sampler periodically samples the state of the VM and optionally
 // (if used for profiling) the program counter and stack pointer for
 // the thread that created it.