Bug 683229 - Add user space profiling using Simple Profiler System (SPS). r=cjones r=jmuizelaar
authorBenoit Girard <b56girard@gmail.com>
Fri, 26 Aug 2011 17:05:37 -0700
changeset 80020 78e97de08278a99d5aa57373d847cb2892e26d63
parent 80019 e0802a531ceda76416d6c535078ee3d3ff0f6dbe
child 80021 4d44e0defe3877d5d77d99ca0d94f9cde3b34cbb
push id506
push userclegnitto@mozilla.com
push dateWed, 09 Nov 2011 02:03:18 +0000
treeherdermozilla-aurora@63587fc7bb93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones, jmuizelaar
bugs683229
milestone10.0a1
Bug 683229 - Add user space profiling using Simple Profiler System (SPS). r=cjones r=jmuizelaar
config/autoconf.mk.in
configure.in
toolkit/library/libxul-config.mk
toolkit/toolkit-makefiles.sh
toolkit/toolkit-tiers.mk
toolkit/xre/nsAppRunner.cpp
tools/profiler/Makefile.in
tools/profiler/sampler.h
tools/profiler/sps/Makefile.in
tools/profiler/sps/TableTicker.cpp
tools/profiler/sps/platform.cc
tools/profiler/sps/platform.h
tools/profiler/sps/sps_sampler.h
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -102,16 +102,17 @@ MOZ_DEBUG_DISABLE_DEFS	= @MOZ_DEBUG_DISA
 MOZ_DEBUG_FLAGS	= @MOZ_DEBUG_FLAGS@
 MOZ_DEBUG_LDFLAGS=@MOZ_DEBUG_LDFLAGS@
 MOZ_EXTENSIONS  = @MOZ_EXTENSIONS@
 MOZ_JSDEBUGGER  = @MOZ_JSDEBUGGER@
 MOZ_IPDL_TESTS 	= @MOZ_IPDL_TESTS@
 MOZ_LEAKY	= @MOZ_LEAKY@
 MOZ_MEMORY      = @MOZ_MEMORY@
 MOZ_PROFILING   = @MOZ_PROFILING@
+MOZ_ENABLE_PROFILER_SPS = @MOZ_ENABLE_PROFILER_SPS@
 MOZ_JPROF       = @MOZ_JPROF@
 MOZ_SHARK       = @MOZ_SHARK@
 MOZ_CALLGRIND   = @MOZ_CALLGRIND@
 MOZ_VTUNE       = @MOZ_VTUNE@
 MOZ_ETW         = @MOZ_ETW@
 MOZ_TRACE_JSCALLS = @MOZ_TRACE_JSCALLS@
 MOZ_TRACEVIS    = @MOZ_TRACEVIS@
 DEHYDRA_PATH    = @DEHYDRA_PATH@
--- a/configure.in
+++ b/configure.in
@@ -1819,16 +1819,27 @@ MOZ_ARG_ENABLE_BOOL(jprof,
     MOZ_JPROF=1,
     MOZ_JPROF= )
 if test -n "$MOZ_JPROF"; then
     MOZ_PROFILING=1
     AC_DEFINE(MOZ_JPROF)
 fi
 
 dnl ========================================================
+dnl SPS Profiler
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(sps,
+[  --enable-sps          Enable sps profiling tool.],
+    MOZ_ENABLE_PROFILER_SPS=1,
+    MOZ_ENABLE_PROFILER_SPS= )
+if test -n "$MOZ_ENABLE_PROFILER_SPS"; then
+    AC_DEFINE(MOZ_ENABLE_PROFILER_SPS)
+fi
+
+dnl ========================================================
 dnl shark
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(shark,
 [  --enable-shark          Enable shark remote profiling. Implies --enable-profiling.],
     MOZ_SHARK=1,
     MOZ_SHARK= )
 if test -n "$MOZ_SHARK"; then
     MOZ_PROFILING=1
@@ -8279,16 +8290,17 @@ AC_SUBST(MOZ_DEBUG_ENABLE_DEFS)
 AC_SUBST(MOZ_DEBUG_DISABLE_DEFS)
 AC_SUBST(MOZ_DEBUG_FLAGS)
 AC_SUBST(MOZ_DEBUG_LDFLAGS)
 AC_SUBST(WARNINGS_AS_ERRORS)
 AC_SUBST(MOZ_EXTENSIONS)
 AC_SUBST(MOZ_JSDEBUGGER)
 AC_SUBST(MOZ_LOG_REFCNT)
 AC_SUBST(MOZ_LEAKY)
+AC_SUBST(MOZ_ENABLE_PROFILER_SPS)
 AC_SUBST(MOZ_JPROF)
 AC_SUBST(MOZ_SHARK)
 AC_SUBST(MOZ_CALLGRIND)
 AC_SUBST(MOZ_VTUNE)
 AC_SUBST(MOZ_ETW)
 AC_SUBST(MOZ_PROFILING)
 AC_SUBST(MOZ_QUANTIFY)
 AC_SUBST(LIBICONV)
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -263,16 +263,20 @@ COMPONENT_LIBS += imgicon
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),android)
 COMPONENT_LIBS += widget_android
 endif
 
 STATIC_LIBS += thebes ycbcr
 
+ifeq ($(MOZ_WIDGET_TOOLKIT),android)
+STATIC_LIBS += profiler
+endif
+
 STATIC_LIBS += angle
 
 ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
 COMPONENT_LIBS += gkwidget
 endif
 ifeq (os2,$(MOZ_WIDGET_TOOLKIT))
 COMPONENT_LIBS += wdgtos2
 endif
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -1054,16 +1054,20 @@ fi # MOZ_L10N
 
 if [ "$MOZ_JPROF" ]; then
   add_makefiles "
     tools/jprof/Makefile
     tools/jprof/stub/Makefile
   "
 fi
 
+add_makefile "
+  tools/profiler/Makefile
+"
+
 if [ "$MOZ_LEAKY" ]; then
   add_makefiles "
     tools/leaky/Makefile
   "
 fi
 
 if [ "$NS_TRACE_MALLOC" ]; then
   add_makefiles "
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -192,16 +192,18 @@ endif
 
 tier_platform_dirs += profile
 
 # This must preceed xpfe
 ifdef MOZ_JPROF
 tier_platform_dirs        += tools/jprof
 endif
 
+tier_platform_dirs  += tools/profiler
+
 tier_platform_dirs	+= xpfe/components
 
 ifdef MOZ_ENABLE_XREMOTE
 tier_platform_dirs += widget/src/xremoteclient
 endif
 
 ifdef MOZ_SPELLCHECK
 tier_platform_dirs	+= extensions/spellcheck
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -2607,20 +2607,24 @@ static DWORD InitDwriteBG(LPVOID lpdwThr
 PRTime gXRE_mainTimestamp = 0;
 
 #ifdef MOZ_X11
 #ifndef MOZ_PLATFORM_MAEMO
 bool fire_glxtest_process();
 #endif
 #endif
 
+#include "sampler.h"
+
 int
 XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
 {
   NS_TIME_FUNCTION;
+  SAMPLER_INIT();
+  SAMPLE_CHECKPOINT("Startup", "XRE_Main");
 
   gXRE_mainTimestamp = PR_Now();
 
   nsresult rv;
   ArgResult ar;
 
 #ifdef DEBUG
   if (PR_GetEnv("XRE_MAIN_BREAK"))
new file mode 100644
--- /dev/null
+++ b/tools/profiler/Makefile.in
@@ -0,0 +1,78 @@
+#! gmake
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+#   Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Benoit Girard <bgirard@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH       = ../..
+topsrcdir	  = @top_srcdir@
+srcdir      = @srcdir@
+VPATH       = \
+  $(srcdir) \
+  $(srcdir)/sps \
+  $(NULL)
+
+include $(DEPTH)/config/autoconf.mk
+
+EXPORTS = \
+  sampler.h \
+  sps_sampler.h \
+  $(NULL)
+
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/ipc/chromium/src \
+  -I$(topsrcdir)/ipc/chromium/src/base \
+  $(NULL)
+
+ifeq ($(OS_TARGET),Android)
+MODULE          = profiler
+LIBRARY_NAME    = profiler
+EXPORT_LIBRARY  = 1
+LIBXUL_LIBRARY  = 1
+
+CPPSRCS		= \
+  $(NULL)
+
+DEFINES += -DMOZ_ENABLE_PROFILER_SPS
+
+CPPSRCS += \
+  platform.cc \
+  TableTicker.cpp \
+  $(NULL)
+endif
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/tools/profiler/sampler.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Benoit Girard <bgirard@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* *************** SPS Sampler Information ****************
+ *
+ * SPS is an always on profiler that takes fast and low overheads samples
+ * of the program execution using only userspace functionity for portability.
+ * The goal of this module is to provide performance data in a generic
+ * cross platform way without requiring custom tools or kernel support.
+ *
+ * Non goals: Support features that are platform specific or replace
+ *            platform specific profilers.
+ *
+ * Samples are collected to form a timeline with optional timeline event (markers)
+ * used for filtering.
+ *
+ * SPS collects samples in a platform independant way by using a speudo stack abstraction
+ * of the real program stack by using 'sample stack frames'. When a sample is collected
+ * all active sample stack frames and the program counter are recorded.
+ */
+
+/* *************** SPS Sampler File Format ****************
+ *
+ * Simple new line seperated tag format:
+ * S      -> BOF tags EOF
+ * tags   -> tag tags
+ * tag    -> CHAR - STRING
+ *
+ * Tags:
+ * 's' - Sample tag followed by the first stack frame followed by 0 or more 'c' tags.
+ * 'c' - Continue Sample tag gives remaining tag element. If a 'c' tag is seen without
+ *         a preceding 's' tag it should be ignored. This is to support the behavior
+ *         of circular buffers.
+ * 'm' - Timeline marker. Zero or more may appear before a 's' tag.
+ * 'l' - Information about the program counter library and address. Post processing
+ *         can include function and source line. If built with leaf data enabled
+ *         this tag will describe the last 'c' tag.
+ *
+ * NOTE: File format is planned to be extended to include a dictionary to reduce size.
+ */
+
+#ifndef SAMPLER_H
+#define SAMPLER_H
+
+#if defined(_MSC_VER)
+#define FULLFUNCTION __FUNCSIG__
+#elif (__GNUC__ >= 4)
+#define FULLFUNCTION __PRETTY_FUNCTION__
+#else
+#define FULLFUNCTION __FUNCTION__
+#endif
+
+// Initialize the sampler. Any other calls will be silently discarded
+// before the sampler has been initialized (i.e. early start-up code)
+#define SAMPLER_INIT()
+#define SAMPLER_DEINIT()
+#define SAMPLE_CHECKPOINT(name_space, info)
+#define SAMPLE_MARKER(info)
+
+// Redefine the macros for platforms where SPS is supported.
+#ifdef ANDROID
+
+#include "sps_sampler.h"
+
+#endif
+
+#endif
new file mode 100644
--- /dev/null
+++ b/tools/profiler/sps/Makefile.in
@@ -0,0 +1,62 @@
+#! gmake
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org Code.
+#
+# The Initial Developer of the Original Code is
+# Kipp E.B. Hickman.
+# Portions created by the Initial Developer are Copyright (C) 2001
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= profilersps
+EXPORTS		= 
+LIBRARY_NAME	= profilersps
+EXPORT_LIBRARY	= 1
+LIBXUL_LIBRARY  = 1
+
+CPPSRCS		= \
+		sampler.cc \
+		$(NULL)
+
+EXPORTS         = \
+                $(NULL)
+
+EXPORTS         := $(addprefix $(srcdir)/, $(EXPORTS))
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/tools/profiler/sps/TableTicker.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Benoit Girard <bgirard@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <sys/stat.h>   // open
+#include <fcntl.h>      // open
+#include <unistd.h>
+#include <stdio.h>
+#include <semaphore.h>
+#include <string.h>
+#include "sps_sampler.h"
+#include "platform.h"
+#include "nsXULAppAPI.h"
+#include "nsThreadUtils.h"
+#include "prenv.h"
+
+pthread_key_t pkey_stack;
+pthread_key_t pkey_ticker;
+
+class Profile;
+
+class ProfileEntry
+{
+public:
+  ProfileEntry()
+    : mTagData(NULL)
+    , mLeafAddress(0)
+    , mTagName(0)
+  { }
+
+  // aTagData must not need release (i.e. be a string from the text segment)
+  ProfileEntry(char aTagName, const char *aTagData)
+    : mTagData(aTagData)
+    , mLeafAddress(0)
+    , mTagName(aTagName)
+  { }
+
+  ProfileEntry(char aTagName, const char *aTagData, Address aLeafAddress)
+    : mTagData(aTagData)
+    , mLeafAddress(aLeafAddress)
+    , mTagName(aTagName)
+  { }
+
+  void WriteTag(Profile *profile, FILE* stream);
+
+private:
+  const char* mTagData;
+  Address mLeafAddress;
+  char mTagName;
+};
+
+#define PROFILE_MAX_ENTRY 100000
+class Profile
+{
+public:
+  Profile(int aEntrySize)
+    : mWritePos(0)
+    , mReadPos(0)
+    , mEntrySize(aEntrySize)
+  {
+    mEntries = new ProfileEntry[mEntrySize];
+  }
+
+  ~Profile()
+  {
+    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;
+    }
+  }
+
+  void WriteProfile(FILE* stream)
+  {
+    LOG("Save profile");
+    // Can't be called from signal because
+    // get_maps calls non reentrant functions.
+#ifdef ENABLE_SPS_LEAF_DATA
+    mMaps = getmaps(getpid());
+#endif
+
+    int oldReadPos = mReadPos;
+    while (mReadPos != mWritePos) {
+      mEntries[mReadPos].WriteTag(this, stream);
+      mReadPos = (mReadPos + 1) % mEntrySize;
+    }
+    mReadPos = oldReadPos;
+  }
+
+#ifdef ENABLE_SPS_LEAF_DATA
+  MapInfo& getMap()
+  {
+    return mMaps;
+  }
+#endif
+private:
+  // Circular buffer 'Keep One Slot Open' implementation
+  // for simplicity
+  ProfileEntry *mEntries;
+  int mWritePos; // points to the next entry we will write to
+  int mReadPos;  // points to the next entry we will read to
+  int mEntrySize;
+#ifdef ENABLE_SPS_LEAF_DATA
+  MapInfo mMaps;
+#endif
+};
+
+class SaveProfileTask;
+
+class TableTicker: public Sampler {
+ public:
+  explicit TableTicker(int aEntrySize, int aInterval)
+    : Sampler(aInterval, true)
+    , mProfile(aEntrySize)
+    , mSaveRequested(false)
+  {
+    mProfile.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();
+
+  Stack* GetStack()
+  {
+    return &mStack;
+  }
+
+  Profile* GetProfile()
+  {
+    return &mProfile;
+  }
+ private:
+  Profile mProfile;
+  Stack mStack;
+  bool mSaveRequested;
+};
+
+/**
+ * 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 = (TableTicker*)pthread_getspecific(pkey_ticker);
+
+    char buff[PATH_MAX];
+#ifdef ANDROID
+  #define FOLDER "/sdcard/"
+#else
+  #define FOLDER "/tmp/"
+#endif
+    snprintf(buff, PATH_MAX, FOLDER "profile_%i_%i.txt", XRE_GetProcessType(), getpid());
+
+    FILE* stream = ::fopen(buff, "w");
+    if (stream) {
+      t->GetProfile()->WriteProfile(stream);
+      ::fclose(stream);
+      LOG("Saved to " FOLDER "profile_TYPE_ID.txt");
+    } else {
+      LOG("Fail to open profile log file.");
+    }
+
+    return NS_OK;
+  }
+};
+
+void TableTicker::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);
+}
+
+
+void TableTicker::Tick(TickSample* sample)
+{
+  // Marker(s) come before the sample
+  int i = 0;
+  const char *marker = mStack.getMarker(i++);
+  for (int i = 0; marker != NULL; i++) {
+    mProfile.addTag(ProfileEntry('m', marker));
+    marker = mStack.getMarker(i++);
+  }
+  mStack.mQueueClearMarker = true;
+
+  // Sample
+  // 's' tag denotes the start of a sample block
+  // followed by 0 or more 'c' tags.
+  for (int i = 0; i < mStack.mStackPointer; i++) {
+    if (i == 0) {
+      Address pc = 0;
+      if (sample) {
+        pc = sample->pc;
+      }
+      mProfile.addTag(ProfileEntry('s', mStack.mStack[i], pc));
+    } else {
+      mProfile.addTag(ProfileEntry('c', mStack.mStack[i]));
+    }
+  }
+}
+
+
+void ProfileEntry::WriteTag(Profile *profile, FILE *stream)
+{
+  fprintf(stream, "%c-%s\n", mTagName, mTagData);
+
+#ifdef ENABLE_SPS_LEAF_DATA
+  if (mLeafAddress) {
+    bool found = false;
+    MapInfo& maps = profile->getMap();
+    unsigned long pc = (unsigned long)mLeafAddress;
+    // TODO Use binary sort (STL)
+    for (size_t i = 0; i < maps.GetSize(); i++) {
+      MapEntry &e = maps.GetEntry(i);
+      if (pc > e.GetStart() && pc < e.GetEnd()) {
+        if (e.GetName()) {
+          found = true;
+          fprintf(stream, "l-%s@%li\n", e.GetName(), pc - e.GetStart());
+          break;
+        }
+      }
+    }
+    if (!found) {
+      fprintf(stream, "l-???@%li\n", pc);
+    }
+  }
+#endif
+}
+
+#define PROFILE_DEFAULT_ENTRY 100000
+void mozilla_sampler_init()
+{
+  const char *val = PR_GetEnv("MOZ_PROFILER_SPS");
+  if (!val || !*val) {
+    return;
+  }
+
+  // TODO linux port: Use TLS with ifdefs
+  // TODO window port: See bug 683229 comment 15
+  // profiler uses getspecific because TLS is not supported on android.
+  // getspecific was picked over nspr because it had less overhead required
+  // to make the checkpoint function fast.
+  if (pthread_key_create(&pkey_stack, NULL) ||
+        pthread_key_create(&pkey_ticker, NULL)) {
+    LOG("Failed to init.");
+    return;
+  }
+
+  TableTicker *t = new TableTicker(PROFILE_DEFAULT_ENTRY, 10);
+  pthread_setspecific(pkey_ticker, t);
+  pthread_setspecific(pkey_stack, t->GetStack());
+
+  t->Start();
+}
+
+void mozilla_sampler_deinit()
+{
+  TableTicker *t = (TableTicker*)pthread_getspecific(pkey_ticker);
+  if (!t) {
+    return;
+  }
+
+  t->Stop();
+  pthread_setspecific(pkey_stack, NULL);
+  // We can't delete the TableTicker because we can be between a
+  // sampler call_enter/call_exit point.
+  // TODO Need to find a safe time to delete TableTicker
+}
+
new file mode 100644
--- /dev/null
+++ b/tools/profiler/sps/platform.cc
@@ -0,0 +1,322 @@
+// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+# vim: sw=2
+*/
+#include <stdio.h>
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#ifdef ANDROID
+#include <android/log.h>
+#else
+#define __android_log_print(a, ...)
+#endif
+// Ubuntu Dapper requires memory pages to be marked as
+// executable. Otherwise, OS raises an exception when executing code
+// in that page.
+#include <sys/types.h>  // mmap & munmap
+#include <sys/mman.h>   // mmap & munmap
+#include <sys/stat.h>   // open
+#include <fcntl.h>      // open
+#include <unistd.h>     // sysconf
+#ifdef __GLIBC__
+#include <execinfo.h>   // backtrace, backtrace_symbols
+#endif  // def __GLIBC__
+#include <strings.h>    // index
+#include <errno.h>
+#include <stdarg.h>
+#include "platform.h"
+
+#include <string.h>
+#include <stdio.h>
+
+// Real time signals are not supported on android.
+// This behaves as a standard signal.
+#define SIGNAL_SAVE_PROFILE 42
+
+#define PATH_MAX_TOSTRING(x) #x
+#define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x)
+
+#ifdef ENABLE_SPS_LEAF_DATA
+/* a crapy version of getline, because it's not included in bionic */
+static ssize_t getline(char **lineptr, size_t *n, FILE *stream)
+{
+ char *ret;
+ if (!*lineptr) {
+   *lineptr = (char*)malloc(4096);
+ }
+ ret = fgets(*lineptr, 4096, stream);
+ if (!ret)
+   return 0;
+ return strlen(*lineptr);
+}
+
+MapInfo getmaps(pid_t pid)
+{
+ MapInfo info;
+ char path[PATH_MAX];
+ snprintf(path, PATH_MAX, "/proc/%d/maps", pid);
+ FILE *maps = fopen(path, "r");
+ char *line = NULL;
+ int count = 0;
+ size_t line_size = 0;
+ while (maps && getline (&line, &line_size, maps) > 0) {
+   int ret;
+   //XXX: needs input sanitizing
+   unsigned long start;
+   unsigned long end;
+   char perm[6] = "";
+   unsigned long offset;
+   char name[PATH_MAX] = "";
+   ret = sscanf(line,
+                "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n",
+                &start, &end, perm, &offset, name);
+   if (!strchr(perm, 'x')) {
+     // Ignore non executable entries
+     continue;
+   }
+   if (ret != 5 && ret != 4) {
+     LOG("Get maps line failed");
+     continue;
+   }
+   MapEntry entry(start, end, offset, name);
+   info.AddMapEntry(entry);
+   if (count > 10000) {
+     LOG("Get maps failed");
+     break;
+   }
+   count++;
+ }
+ free(line);
+ return info;
+}
+#endif
+
+static Sampler* sActiveSampler = NULL;
+
+
+#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
+// Android runs a fairly new Linux kernel, so signal info is there,
+// but the C library doesn't have the structs defined.
+
+struct sigcontext {
+  uint32_t trap_no;
+  uint32_t error_code;
+  uint32_t oldmask;
+  uint32_t gregs[16];
+  uint32_t arm_cpsr;
+  uint32_t fault_address;
+};
+typedef uint32_t __sigset_t;
+typedef struct sigcontext mcontext_t;
+typedef struct ucontext {
+  uint32_t uc_flags;
+  struct ucontext* uc_link;
+  stack_t uc_stack;
+  mcontext_t uc_mcontext;
+  __sigset_t uc_sigmask;
+} ucontext_t;
+enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11};
+
+#endif
+
+static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) {
+  sActiveSampler->RequestSave();
+}
+
+#ifdef ANDROID
+#define V8_HOST_ARCH_ARM 1
+#define SYS_gettid __NR_gettid
+#define SYS_tgkill __NR_tgkill
+#else
+#define V8_HOST_ARCH_X64 1
+#endif
+static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
+  if (!sActiveSampler)
+    return;
+
+#ifndef ENABLE_SPS_LEAF_DATA
+  TickSample* sample = NULL;
+#else
+  TickSample sample_obj;
+  TickSample* sample = &sample_obj;
+
+  // If profiling, we extract the current pc and sp.
+  if (sActiveSampler->IsProfiling()) {
+    // Extracting the sample from the context is extremely machine dependent.
+    ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
+    mcontext_t& mcontext = ucontext->uc_mcontext;
+#if V8_HOST_ARCH_IA32
+    sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
+    sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
+    sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
+#elif V8_HOST_ARCH_X64
+    sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
+    sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
+    sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
+#elif V8_HOST_ARCH_ARM
+// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
+#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
+    sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
+    sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
+    sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
+#else
+    sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
+    sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
+    sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
+#endif
+#elif V8_HOST_ARCH_MIPS
+    // Implement this on MIPS.
+    UNIMPLEMENTED();
+#endif
+  }
+#endif
+  sActiveSampler->Tick(sample);
+}
+
+void tgkill(pid_t tgid, pid_t tid, int signalno) {
+  syscall(SYS_tgkill, tgid, tid, signalno);
+}
+
+//class Sampler::PlatformData : public Malloced {
+class Sampler::PlatformData {
+ public:
+  explicit PlatformData(Sampler* sampler)
+      : sampler_(sampler),
+        signal_handler_installed_(false),
+        vm_tgid_(getpid()),
+        // Glibc doesn't provide a wrapper for gettid(2).
+        vm_tid_(gettid()),
+        signal_sender_launched_(false) {
+  }
+
+  void SignalSender() {
+    while (sampler_->IsActive()) {
+      sampler_->HandleSaveRequest();
+
+      // Glibc doesn't provide a wrapper for tgkill(2).
+      tgkill(vm_tgid_, vm_tid_, SIGPROF);
+      // Convert ms to us and subtract 100 us to compensate delays
+      // occuring during signal delivery.
+
+      // TODO measure and confirm this.
+      const useconds_t interval = sampler_->interval_ * 1000 - 100;
+      //int result = usleep(interval);
+      usleep(interval);
+      // sometimes usleep is defined as returning void
+      int result = 0;
+#ifdef DEBUG
+      if (result != 0 && errno != EINTR) {
+        LOG("SignalSender usleep error");
+        ASSERT(result == 0 || errno == EINTR);
+      }
+#endif
+      mozilla::unused << result;
+    }
+  }
+
+  Sampler* sampler_;
+  bool signal_handler_installed_;
+  struct sigaction old_sigprof_signal_handler_;
+  struct sigaction old_sigsave_signal_handler_;
+  pid_t vm_tgid_;
+  pid_t vm_tid_;
+  bool signal_sender_launched_;
+  pthread_t signal_sender_thread_;
+};
+
+
+static void* SenderEntry(void* arg) {
+  Sampler::PlatformData* data =
+      reinterpret_cast<Sampler::PlatformData*>(arg);
+  data->SignalSender();
+  return 0;
+}
+
+
+Sampler::Sampler(int interval, bool profiling)
+    : interval_(interval),
+      profiling_(profiling),
+      synchronous_(profiling),
+      active_(false) {
+  data_ = new PlatformData(this);
+}
+
+Sampler::~Sampler() {
+  ASSERT(!data_->signal_sender_launched_);
+  delete data_;
+}
+
+
+void Sampler::Start() {
+  LOG("Sampler Started");
+  if (sActiveSampler != NULL) return;
+
+  // Request profiling signals.
+  LOG("Request signal");
+  struct sigaction sa;
+  sa.sa_sigaction = ProfilerSignalHandler;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = SA_RESTART | SA_SIGINFO;
+  if (sigaction(SIGPROF, &sa, &data_->old_sigprof_signal_handler_) != 0) {
+    LOG("Error installing signal");
+    return;
+  }
+
+  // Request save profile signals
+  struct sigaction sa2;
+  sa2.sa_sigaction = ProfilerSaveSignalHandler;
+  sigemptyset(&sa2.sa_mask);
+  sa2.sa_flags = SA_RESTART | SA_SIGINFO;
+  if (sigaction(SIGNAL_SAVE_PROFILE, &sa2, &data_->old_sigsave_signal_handler_) != 0) {
+    LOG("Error installing start signal");
+    return;
+  }
+  LOG("Signal installed");
+  data_->signal_handler_installed_ = true;
+
+  // Start a thread that sends SIGPROF signal to VM thread.
+  // Sending the signal ourselves instead of relying on itimer provides
+  // much better accuracy.
+  active_ = true;
+  if (pthread_create(
+          &data_->signal_sender_thread_, NULL, SenderEntry, data_) == 0) {
+    data_->signal_sender_launched_ = true;
+  }
+  LOG("Profiler thread started");
+
+  // Set this sampler as the active sampler.
+  sActiveSampler = this;
+}
+
+
+void Sampler::Stop() {
+  active_ = false;
+
+  // Wait for signal sender termination (it will exit after setting
+  // active_ to false).
+  if (data_->signal_sender_launched_) {
+    pthread_join(data_->signal_sender_thread_, NULL);
+    data_->signal_sender_launched_ = false;
+  }
+
+  // Restore old signal handler
+  if (data_->signal_handler_installed_) {
+    sigaction(SIGPROF, &data_->old_sigsave_signal_handler_, 0);
+    sigaction(SIGPROF, &data_->old_sigprof_signal_handler_, 0);
+    data_->signal_handler_installed_ = false;
+  }
+
+  // This sampler is no longer the active sampler.
+  sActiveSampler = NULL;
+}
+
new file mode 100644
--- /dev/null
+++ b/tools/profiler/sps/platform.h
@@ -0,0 +1,153 @@
+// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifdef ANDROID
+#include <android/log.h>
+#else
+#define __android_log_print(a, ...)
+#endif
+
+#include "mozilla/Util.h"
+#include "mozilla/unused.h"
+#include <vector>
+#define ASSERT(a) MOZ_ASSERT(a)
+#ifdef ANDROID
+#define ENABLE_SPS_LEAF_DATA
+#define LOG(text) __android_log_print(ANDROID_LOG_ERROR, "profiler", "%s", text);
+#else
+#define LOG(text) printf("Profiler: %s\n", text)
+#endif
+
+#include <stdint.h>
+typedef uint8 byte;
+typedef byte* Address;
+
+class MapEntry {
+public:
+  MapEntry(unsigned long aStart, unsigned long aEnd, unsigned long aOffset, char *aName)
+    : mStart(aStart)
+    , mEnd(aEnd)
+    , mOffset(aOffset)
+    , mName(strdup(aName))
+  {}
+
+  MapEntry(const MapEntry& aEntry)
+    : mStart(aEntry.mStart)
+    , mEnd(aEntry.mEnd)
+    , mOffset(aEntry.mOffset)
+    , mName(strdup(aEntry.mName))
+  {}
+
+  ~MapEntry()
+  {
+    free(mName);
+  }
+
+  unsigned long GetStart() { return mStart; }
+  unsigned long GetEnd() { return mEnd; }
+  char* GetName() { return mName; }
+
+private:
+  unsigned long mStart;
+  unsigned long mEnd;
+  unsigned long mOffset;
+  char *mName;
+};
+
+class MapInfo {
+public:
+  MapInfo() {}
+
+  void AddMapEntry(MapEntry entry)
+  {
+    mEntries.push_back(entry);
+  }
+
+  MapEntry& GetEntry(size_t i)
+  {
+    return mEntries[i];
+  }
+
+  size_t GetSize()
+  {
+    return mEntries.size();
+  }
+private:
+  std::vector<MapEntry> mEntries;
+};
+
+#ifdef ENABLE_SPS_LEAF_DATA
+struct MapInfo getmaps(pid_t pid);
+#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.
+
+// TickSample captures the information collected for each sample.
+class TickSample {
+ public:
+  TickSample()
+      :
+        pc(NULL),
+        sp(NULL),
+        fp(NULL),
+        function(NULL),
+        frames_count(0) {}
+  Address pc;  // Instruction pointer.
+  Address sp;  // Stack pointer.
+  Address fp;  // Frame pointer.
+  Address function;  // The last called JS function.
+  static const int kMaxFramesCount = 64;
+  Address stack[kMaxFramesCount];  // Call stack.
+  int frames_count;  // Number of captured frames.
+};
+
+class Sampler {
+ public:
+  // Initialize sampler.
+  explicit Sampler(int interval, bool profiling);
+  virtual ~Sampler();
+
+  // Performs stack sampling.
+  virtual void SampleStack(TickSample* sample) = 0;
+
+  // This method is called for each sampling period with the current
+  // program counter.
+  virtual void Tick(TickSample* sample) = 0;
+
+  // Request a save from a signal handler
+  virtual void RequestSave() = 0;
+  // Process any outstanding request outside a signal handler.
+  virtual void HandleSaveRequest() = 0;
+
+  // Start and stop sampler.
+  void Start();
+  void Stop();
+
+  // Is the sampler used for profiling?
+  bool IsProfiling() const { return profiling_; }
+
+  // Is the sampler running in sync with the JS thread? On platforms
+  // where the sampler is implemented with a thread that wakes up
+  // every now and then, having a synchronous sampler implies
+  // suspending/resuming the JS thread.
+  bool IsSynchronous() const { return synchronous_; }
+
+  // Whether the sampler is running (that is, consumes resources).
+  bool IsActive() const { return active_; }
+
+  class PlatformData;
+
+ private:
+  const int interval_;
+  const bool profiling_;
+  const bool synchronous_;
+  bool active_;
+  PlatformData* data_;  // Platform specific data.
+};
+
new file mode 100644
--- /dev/null
+++ b/tools/profiler/sps/sps_sampler.h
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Benoit Girard <bgirard@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <pthread.h>
+#include "nscore.h"
+#include "base/atomicops.h"
+
+// TODO Merge into Sampler.h
+
+extern pthread_key_t pkey_stack;
+extern pthread_key_t pkey_ticker;
+
+#define SAMPLER_INIT() mozilla_sampler_init();
+#define SAMPLER_DEINIT() mozilla_sampler_deinit();
+#define SAMPLE_CHECKPOINT(name_space, info) mozilla::SamplerStackFrameRAII only_one_sampleraii_per_scope(FULLFUNCTION, name_space "::" info);
+#define SAMPLE_MARKER(info) mozilla_sampler_add_marker(info);
+
+// STORE_SEQUENCER: Because signals can interrupt our profile modification
+//                  we need to make stores are not re-ordered by the compiler
+//                  or hardware to make sure the profile is consistent at
+//                  every point the signal can fire.
+#ifdef ARCH_CPU_ARM_FAMILY
+// TODO Is there something cheaper that will prevent
+//      memory stores from being reordered
+// Uses: pLinuxKernelMemoryBarrier
+# define STORE_SEQUENCER() base::subtle::MemoryBarrier();
+#elif ARCH_CPU_ARM_FAMILY
+# define STORE_SEQUENCER() asm volatile("" ::: "memory");
+#else
+# error "Memory clobber not supported for your platform."
+#endif
+
+// Returns a handdle to pass on exit. This can check that we are popping the
+// correct callstack.
+inline void* mozilla_sampler_call_enter(const char *aInfo);
+inline void  mozilla_sampler_call_exit(void* handle);
+inline void  mozilla_sampler_add_marker(const char *aInfo);
+
+void mozilla_sampler_init();
+
+namespace mozilla {
+
+class NS_STACK_CLASS SamplerStackFrameRAII {
+public:
+  SamplerStackFrameRAII(const char *aFuncName, const char *aInfo) {
+    mHandle = mozilla_sampler_call_enter(aInfo);
+  }
+  ~SamplerStackFrameRAII() {
+    mozilla_sampler_call_exit(mHandle);
+  }
+private:
+  void* mHandle;
+};
+
+} //mozilla
+
+// the SamplerStack members are read by signal
+// handlers, so the mutation of them needs to be signal-safe.
+struct Stack
+{
+public:
+  Stack()
+    : mStackPointer(0)
+    , mMarkerPointer(0)
+    , mDroppedStackEntries(0)
+    , mQueueClearMarker(false)
+  { }
+
+  void addMarker(const char *aMarker)
+  {
+    if (mQueueClearMarker) {
+      clearMarkers();
+    }
+    if (!aMarker) {
+      return; //discard
+    }
+    if (mMarkerPointer == 1024) {
+      return; //array full, silently drop
+    }
+    mMarkers[mMarkerPointer] = aMarker;
+    STORE_SEQUENCER();
+    mMarkerPointer++;
+  }
+
+  // called within signal. Function must be reentrant
+  const char* getMarker(int aMarkerId)
+  {
+    if (mQueueClearMarker) {
+      clearMarkers();
+    }
+    if (aMarkerId >= mMarkerPointer) {
+      return NULL;
+    }
+    return mMarkers[aMarkerId];
+  }
+
+  // called within signal. Function must be reentrant
+  void clearMarkers()
+  {
+    mMarkerPointer = 0;
+    mQueueClearMarker = false;
+  }
+
+  void push(const char *aName)
+  {
+    if (mStackPointer >= 1024) {
+      mDroppedStackEntries++;
+      return;
+    }
+
+    // Make sure we increment the pointer after the name has
+    // been written such that mStack is always consistent.
+    mStack[mStackPointer] = aName;
+    // Prevent the optimizer from re-ordering these instructions
+    asm("":::"memory");
+    mStackPointer++;
+  }
+  void pop()
+  {
+    if (mDroppedStackEntries > 0) {
+      mDroppedStackEntries--;
+    } else {
+      mStackPointer--;
+    }
+  }
+  bool isEmpty()
+  {
+    return mStackPointer == 0;
+  }
+
+  // Keep a list of active checkpoints
+  const char *mStack[1024];
+  // Keep a list of active markers to be applied to the next sample taken
+  const char *mMarkers[1024];
+  sig_atomic_t mStackPointer;
+  sig_atomic_t mMarkerPointer;
+  sig_atomic_t mDroppedStackEntries;
+  // We don't want to modify _markers from within the signal so we allow
+  // it to queue a clear operation.
+  sig_atomic_t mQueueClearMarker;
+};
+
+inline void* mozilla_sampler_call_enter(const char *aInfo)
+{
+  Stack *stack = (Stack*)pthread_getspecific(pkey_stack);
+  if (!stack) {
+    return stack;
+  }
+  stack->push(aInfo);
+
+  // The handle is meant to support future changes
+  // but for now it is simply use to save a call to
+  // pthread_getspecific on exit. It also supports the
+  // case where the sampler is initialized between
+  // enter and exit.
+  return stack;
+}
+
+inline void mozilla_sampler_call_exit(void *aHandle)
+{
+  if (!aHandle)
+    return;
+
+  Stack *stack = (Stack*)aHandle;
+  stack->pop();
+}
+
+inline void mozilla_sampler_add_marker(const char *aMarker)
+{
+  Stack *stack = (Stack*)pthread_getspecific(pkey_stack);
+  if (!stack) {
+    return;
+  }
+  stack->addMarker(aMarker);
+}
+