Bug 779291: Implement SPS stackwalk using the breakpad unwinder. r=bgirard,glandium
authorJulian Seward <jseward@acm.org>
Sun, 10 Mar 2013 23:00:23 +0100
changeset 134924 8b366545161d029c6f172d23d07b0a1c978c7065
parent 134923 71d1bd406c7ec99a921035c66f37030bf25aa45c
child 134925 bb7e29036271e2b428be5c558dc5efdc91031898
push id2452
push userlsblakk@mozilla.com
push dateMon, 13 May 2013 16:59:38 +0000
treeherdermozilla-beta@d4b152d29d8d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgirard, glandium
bugs779291, 850089, 850132
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 779291: Implement SPS stackwalk using the breakpad unwinder. r=bgirard,glandium bug 850089 - fix SPS with disable-crashreporter. Patch by Ted Mielczarek <ted@mielczarek.org>, r=glandium Bug 850132 - SPS breakpad unwind (bug 779291) breaks Win64 builds with "Unsupported platform". Patch by Makoto Kato <m_kato@ga2.so-net.ne.jp>, r=ted
js/xpconnect/src/XPCJSRuntime.cpp
toolkit/crashreporter/google-breakpad/src/common/Makefile.in
toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in
toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in
toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc
toolkit/crashreporter/google-breakpad/src/processor/Makefile.in
toolkit/moz.build
toolkit/xre/Makefile.in
tools/profiler/Makefile.in
tools/profiler/PlatformMacros.h
tools/profiler/ProfileEntry2.cpp
tools/profiler/ProfileEntry2.h
tools/profiler/TableTicker.cpp
tools/profiler/TableTicker2.cpp
tools/profiler/UnwinderThread2.cpp
tools/profiler/UnwinderThread2.h
tools/profiler/local_debug_info_symbolizer.cc
tools/profiler/local_debug_info_symbolizer.h
tools/profiler/nsProfiler.cpp
tools/profiler/platform-linux.cc
tools/profiler/platform-macos.cc
tools/profiler/platform.h
tools/profiler/shim_mac_dump_syms.h
tools/profiler/shim_mac_dump_syms.mm
tools/profiler/sps_sampler.h
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1349,17 +1349,17 @@ XPCJSRuntime::~XPCJSRuntime()
         JS_DestroyRuntime(mJSRuntime);
         JS_ShutDown();
 #ifdef DEBUG_shaver_off
         fprintf(stderr, "nJRSI: destroyed runtime %p\n", (void *)mJSRuntime);
 #endif
     }
 #ifdef MOZ_ENABLE_PROFILER_SPS
     // Tell the profiler that the runtime is gone
-    if (ProfileStack *stack = mozilla_profile_stack())
+    if (PseudoStack *stack = mozilla_get_pseudo_stack())
         stack->sampleRuntime(nullptr);
 #endif
 
 #ifdef DEBUG
     for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
         NS_ASSERTION(!mScratchStrings[i].mInUse, "Uh, string wrapper still in use!");
     }
 #endif
@@ -2556,17 +2556,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
                               xpc::WrapperFactory::Rewrap,
                               xpc::WrapperFactory::WrapForSameCompartment,
                               xpc::WrapperFactory::PrepareForWrapping);
     js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper);
 #ifdef MOZ_CRASHREPORTER
     JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
 #endif
 #ifdef MOZ_ENABLE_PROFILER_SPS
-    if (ProfileStack *stack = mozilla_profile_stack())
+    if (PseudoStack *stack = mozilla_get_pseudo_stack())
         stack->sampleRuntime(mJSRuntime);
 #endif
     JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);
     js::SetActivityCallback(mJSRuntime, ActivityCallback, this);
 
     // The JS engine needs to keep the source code around in order to implement
     // Function.prototype.toSource(). It'd be nice to not have to do this for
     // chrome code and simply stub out requests for source on it. Life is not so
--- a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in
+++ b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in
@@ -7,18 +7,20 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= breakpad_common
 LIBRARY_NAME	= breakpad_common_s
 ifneq (WINNT,$(OS_TARGET))
+ifdef MOZ_CRASHREPORTER
 HOST_LIBRARY_NAME = host_breakpad_common_s
 endif
+endif
 
 LOCAL_INCLUDES 	= -I$(srcdir)/..
 
 CPPSRCS	= \
   string_conversion.cc \
   module.cc \
   unique_string.cc \
   $(NULL)
@@ -29,29 +31,31 @@ CPPSRCS += \
   dwarf/bytereader.cc \
   dwarf_cfi_to_module.cc \
   dwarf/dwarf2reader.cc \
   dwarf_cu_to_module.cc \
   language.cc \
   dwarf/dwarf2diehandler.cc \
   dwarf_line_to_module.cc \
   $(NULL)
+ifdef MOZ_CRASHREPORTER
 HOST_CPPSRCS = \
   string_conversion.cc \
   module.cc \
   unique_string.cc \
   md5.cc \
   dwarf_cfi_to_module.cc \
   dwarf_cu_to_module.cc \
   language.cc \
   stabs_to_module.cc \
   stabs_reader.cc \
   dwarf_line_to_module.cc \
   $(NULL)
 endif
+endif
 
 ifeq ($(OS_ARCH),Linux)
 CPPSRCS += \
   linux/dump_symbols.cc \
   linux/elf_symbols_to_module.cc
 endif
 
 ifeq ($(OS_ARCH),Darwin)
@@ -94,18 +98,20 @@ HOST_CXXFLAGS += -DHAVE_A_OUT_H
 OS_CXXFLAGS += -DHAVE_A_OUT_H
 endif
 ifeq (Darwin,$(OS_ARCH))
 HOST_CXXFLAGS += -DHAVE_MACH_O_NLIST_H
 OS_CXXFLAGS += -DHAVE_MACH_O_NLIST_H
 endif
 
 ifneq (WINNT,$(OS_TARGET))
+ifdef MOZ_CRASHREPORTER
 HOST_CSRCS = $(CSRCS)
 endif
+endif
 
 # need static lib
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 ifneq (WINNT,$(OS_TARGET))
 # Headers from this directory are included as "common/header.h". Having
--- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in
+++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in
@@ -5,26 +5,29 @@
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= breakpad_dwarf
+
+ifdef MOZ_CRASHREPORTER
 HOST_LIBRARY_NAME = host_breakpad_dwarf_s
 
 LOCAL_INCLUDES 	= -I$(srcdir)/../..
 
 HOST_CPPSRCS = \
   bytereader.cc \
   dwarf2diehandler.cc \
   dwarf2reader.cc \
   functioninfo.cc \
   $(NULL)
+endif
 
 # This code is only compiled for build-time tools,
 # so enabling RTTI should be fine.
 HOST_CXXFLAGS := -funsigned-char $(filter-out -fno-rtti,$(HOST_CXXFLAGS))
 
 # need static lib
 FORCE_STATIC_LIB = 1
 
--- a/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in
@@ -6,17 +6,19 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= breakpad_linux_common
 LIBRARY_NAME	= breakpad_linux_common_s
+ifdef MOZ_CRASHREPORTER
 HOST_LIBRARY_NAME = host_breakpad_linux_common_s
+endif
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src \
   $(NULL)
 
 CPPSRCS	= \
   elfutils.cc \
   file_id.cc \
@@ -33,25 +35,27 @@ TARGET_LOCAL_INCLUDES += -I$(topsrcdir)/
 endif
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 DEFINES += -DELFSIZE=32
 endif
 
 DEFINES += -DNO_STABS_SUPPORT
 
+ifdef MOZ_CRASHREPORTER
 HOST_CPPSRCS = \
   dump_symbols.cc \
   elf_symbols_to_module.cc \
   elfutils.cc \
   file_id.cc \
   guid_creator.cc \
   linux_libc_support.cc \
   memory_mapped_file.cc \
   $(NULL)
+endif
 
 # need static lib
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 # See https://bugzilla.mozilla.org/show_bug.cgi?id=741348#c11
 file_id.$(OBJ_SUFFIX): STL_FLAGS=
--- a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc
@@ -621,17 +621,17 @@ bool LoadSymbols(const string& obj_file,
       bool result =
           LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".eh_frame",
                                  eh_frame_section, true,
                                  got_section, text_section, big_endian, module);
       found_usable_info = found_usable_info || result;
     }
   }
 
-  if (!found_debug_info_section) {
+  if (!found_debug_info_section && symbol_data != ONLY_CFI) {
     fprintf(stderr, "%s: file contains no debugging information"
             " (no \".stab\" or \".debug_info\" sections)\n",
             obj_file.c_str());
 
     // Failed, but maybe there's a .gnu_debuglink section?
     if (read_gnu_debug_link) {
       const Shdr* gnu_debuglink_section
           = FindElfSectionByName<ElfClass>(".gnu_debuglink", SHT_PROGBITS,
--- a/toolkit/crashreporter/google-breakpad/src/processor/Makefile.in
+++ b/toolkit/crashreporter/google-breakpad/src/processor/Makefile.in
@@ -6,17 +6,19 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= breakpad_sps_common
 LIBRARY_NAME	= breakpad_sps_common_s
+ifdef MOZ_CRASHREPORTER
 HOST_LIBRARY_NAME = host_breakpad_sps_common_s
+endif
 
 LOCAL_INCLUDES 	= -I$(srcdir)/../.. -I$(srcdir)/..
 
 CPPSRCS	= \
   stackwalker.cc \
   stackwalker_amd64.cc \
   stackwalker_arm.cc \
   stackwalker_ppc.cc \
@@ -29,17 +31,19 @@ CPPSRCS	= \
   call_stack.cc \
   logging.cc \
   pathname_stripper.cc \
   tokenize.cc \
   source_line_resolver_base.cc \
   stack_frame_symbolizer.cc \
   $(NULL)
 
+ifdef MOZ_CRASHREPORTER
 HOST_CPPSRCS = \
   logging.cc \
   pathname_stripper.cc \
   $(NULL)
+endif
 
 # need static lib
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
--- a/toolkit/moz.build
+++ b/toolkit/moz.build
@@ -36,8 +36,19 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co
     PARALLEL_DIRS += ['system/osxproxy']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     PARALLEL_DIRS += ['system/windowsproxy']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     PARALLEL_DIRS += ['system/androidproxy']
 
 if CONFIG['MOZ_CRASHREPORTER']:
     PARALLEL_DIRS += ['crashreporter']
+elif CONFIG['MOZ_ENABLE_PROFILER_SPS']:
+    # Profiler requires some crashreporter code,
+    # so build it even if crashreporter is disabled.
+    PARALLEL_DIRS += [
+    'crashreporter/google-breakpad/src/common',
+    'crashreporter/google-breakpad/src/processor',
+    ]
+    if CONFIG['OS_ARCH'] == 'Darwin':
+        PARALLEL_DIRS += ['crashreporter/google-breakpad/src/common/mac']
+    elif CONFIG['OS_ARCH'] == 'Linux':
+        PARALLEL_DIRS += ['crashreporter/google-breakpad/src/common/linux']
--- a/toolkit/xre/Makefile.in
+++ b/toolkit/xre/Makefile.in
@@ -122,51 +122,60 @@ SHARED_LIBRARY_LIBS += \
 endif
 endif
 
 ifdef MOZ_ENABLE_XREMOTE
 SHARED_LIBRARY_LIBS += $(DEPTH)/widget/xremoteclient/$(LIB_PREFIX)xremote_client_s.$(LIB_SUFFIX)
 LOCAL_INCLUDES += -I$(topsrcdir)/widget/xremoteclient
 endif
 
-ifdef MOZ_CRASHREPORTER
-SHARED_LIBRARY_LIBS += $(DEPTH)/toolkit/crashreporter/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX)
-ifeq ($(OS_ARCH),WINNT)
-SHARED_LIBRARY_LIBS += \
-  $(DEPTH)/toolkit/crashreporter/breakpad-windows-libxul/$(LIB_PREFIX)google_breakpad_libxul_s.$(LIB_SUFFIX)
-ifdef MOZ_ENABLE_PROFILER_SPS
+ifneq (,$(MOZ_CRASHREPORTER)$(MOZ_ENABLE_PROFILER_SPS))
 SHARED_LIBRARY_LIBS += \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)breakpad_common_s.$(LIB_SUFFIX) \
   $(NULL)
+
+ifeq ($(OS_ARCH),Darwin)
+SHARED_LIBRARY_LIBS += \
+  $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/mac/$(LIB_PREFIX)breakpad_mac_common_s.$(LIB_SUFFIX)
+  $(NULL)
+endif
+ifeq ($(OS_ARCH),Linux)
+SHARED_LIBRARY_LIBS += \
+  $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/linux/$(LIB_PREFIX)breakpad_linux_common_s.$(LIB_SUFFIX) \
+  $(NULL)
 endif
 endif
 
 ifdef MOZ_ENABLE_PROFILER_SPS
 SHARED_LIBRARY_LIBS += \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/processor/$(LIB_PREFIX)breakpad_sps_common_s.$(LIB_SUFFIX) \
   $(NULL)
 endif
 
+ifdef MOZ_CRASHREPORTER
+SHARED_LIBRARY_LIBS += $(DEPTH)/toolkit/crashreporter/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX)
+ifeq ($(OS_ARCH),WINNT)
+SHARED_LIBRARY_LIBS += \
+  $(DEPTH)/toolkit/crashreporter/breakpad-windows-libxul/$(LIB_PREFIX)google_breakpad_libxul_s.$(LIB_SUFFIX)
+endif
+
 ifeq ($(OS_ARCH),Darwin)
 SHARED_LIBRARY_LIBS += \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/$(LIB_PREFIX)minidump_file_writer_s.$(LIB_SUFFIX) \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/$(LIB_PREFIX)crash_generation_s.$(LIB_SUFFIX) \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/mac/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \
-  $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)breakpad_common_s.$(LIB_SUFFIX) \
-  $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/mac/$(LIB_PREFIX)breakpad_mac_common_s.$(LIB_SUFFIX)
+  $(NULL)
 endif
 
 ifeq ($(OS_ARCH),Linux)
 SHARED_LIBRARY_LIBS += \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/$(LIB_PREFIX)crash_generation_s.$(LIB_SUFFIX) \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/linux/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/$(LIB_PREFIX)minidump_writer_s.$(LIB_SUFFIX) \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/$(LIB_PREFIX)minidump_file_writer_s.$(LIB_SUFFIX) \
-  $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)breakpad_common_s.$(LIB_SUFFIX) \
-  $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/linux/$(LIB_PREFIX)breakpad_linux_common_s.$(LIB_SUFFIX) \
   $(NULL)
 endif
 
 ifeq ($(OS_ARCH),SunOS)
 SHARED_LIBRARY_LIBS += \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/solaris/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/$(LIB_PREFIX)minidump_file_writer_s.$(LIB_SUFFIX) \
   $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)breakpad_common_s.$(LIB_SUFFIX) \
--- a/tools/profiler/Makefile.in
+++ b/tools/profiler/Makefile.in
@@ -20,16 +20,17 @@ ifdef MOZ_ENABLE_PROFILER_SPS
 EXPORTS += \
   sps_sampler.h \
   shared-libraries.h \
   $(NULL)
 
 LOCAL_INCLUDES += \
   -I$(topsrcdir)/mozglue/linker \
   -I$(topsrcdir)/ipc/chromium/src \
+  -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src \
   $(NULL)
 
 ifneq (,$(filter armeabi,$(ANDROID_CPU_ARCH)))
 DEFINES += -DARCH_ARMV6
 endif
 
 MODULE          = profiler
 MODULE_NAME     = nsProfilerModule
@@ -40,16 +41,20 @@ IS_COMPONENT    = 1
 ifndef _MSC_VER
 FAIL_ON_WARNINGS = 1
 endif # !_MSC_VER
 
 CPPSRCS		= \
   nsProfilerFactory.cpp \
   nsProfiler.cpp \
   TableTicker.cpp \
+  TableTicker2.cpp \
+  UnwinderThread2.cpp \
+  ProfileEntry2.cpp \
+  local_debug_info_symbolizer.cc \
   JSObjectBuilder.cpp \
   JSCustomObjectBuilder.cpp \
   $(NULL)
 
 XPIDLSRCS = \
   nsIProfiler.idl \
   $(NULL)
 
@@ -64,16 +69,18 @@ CPPSRCS += \
   $(NULL)
 endif
 
 ifeq ($(OS_TARGET),Darwin)
 CPPSRCS += \
   shared-libraries-macos.cc \
   platform-macos.cc \
   $(NULL)
+CMMSRCS += \
+  shim_mac_dump_syms.mm
 endif
 
 ifeq ($(OS_TARGET),WINNT)
 CPPSRCS += \
   shared-libraries-win32.cc \
   platform-win32.cc \
   $(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/tools/profiler/PlatformMacros.h
@@ -0,0 +1,76 @@
+/* -*- 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 SPS_PLATFORM_MACROS_H
+#define SPS_PLATFORM_MACROS_H
+
+/* Define platform selection macros in a consistent way.  Don't add
+   anything else to this file, so it can remain freestanding.  The
+   primary factorisation is on (ARCH,OS) pairs ("PLATforms") but ARCH_
+   and OS_ macros are defined too, since they are sometimes
+   convenient. */
+
+#undef SPS_PLAT_arm_android
+#undef SPS_PLAT_amd64_linux
+#undef SPS_PLAT_x86_linux
+#undef SPS_PLAT_amd64_darwin
+#undef SPS_PLAT_x86_darwin
+#undef SPS_PLAT_x86_windows
+#undef SPS_PLAT_amd64_windows
+
+#undef SPS_ARCH_arm
+#undef SPS_ARCH_x86
+#undef SPS_ARCH_amd64
+
+#undef SPS_OS_android
+#undef SPS_OS_linux
+#undef SPS_OS_darwin
+#undef SPS_OS_windows
+
+#if defined(__linux__) && defined(__x86_64__)
+#  define SPS_PLAT_amd64_linux 1
+#  define SPS_ARCH_amd64 1
+#  define SPS_OS_linux 1
+
+#elif defined(__ANDROID__) && defined(__arm__)
+#  define SPS_PLAT_arm_android 1
+#  define SPS_ARCH_arm 1
+#  define SPS_OS_android 1
+
+#elif defined(__ANDROID__) && defined(__i386__)
+#  define SPS_PLAT_x86_android 1
+#  define SPS_ARCH_x86 1
+#  define SPS_OS_android 1
+
+#elif defined(__linux__) && defined(__i386__)
+#  define SPS_PLAT_x86_linux 1
+#  define SPS_ARCH_x86 1
+#  define SPS_OS_linux 1
+
+#elif defined(__APPLE__) && defined(__x86_64__)
+#  define SPS_PLAT_amd64_darwin 1
+#  define SPS_ARCH_amd64 1
+#  define SPS_OS_darwin 1
+
+#elif defined(__APPLE__) && defined(__i386__)
+#  define SPS_PLAT_x86_darwin 1
+#  define SPS_ARCH_x86 1
+#  define SPS_OS_darwin 1
+
+#elif defined(_MSC_VER) && defined(_M_IX86)
+#  define SPS_PLAT_x86_windows 1
+#  define SPS_ARCH_x86 1
+#  define SPS_OS_windows 1
+
+#elif defined(_MSC_VER) && defined(_M_X64)
+#  define SPS_PLAT_amd64_windows 1
+#  define SPS_ARCH_amd64 1
+#  define SPS_OS_windows 1
+
+#else
+#  error "Unsupported platform"
+#endif
+
+#endif /* ndef SPS_PLATFORM_MACROS_H */
new file mode 100644
--- /dev/null
+++ b/tools/profiler/ProfileEntry2.cpp
@@ -0,0 +1,338 @@
+/* -*- 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 <iostream>
+#include "sps_sampler.h"
+#include "platform.h"
+#include "nsThreadUtils.h"
+
+// JSON
+#include "JSObjectBuilder.h"
+
+// Self
+#include "ProfileEntry2.h"
+
+#if _MSC_VER
+ #define snprintf _snprintf
+#endif
+
+////////////////////////////////////////////////////////////////////////
+// BEGIN ProfileEntry2
+
+ProfileEntry2::ProfileEntry2()
+  : 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)
+  : mTagData(aTagData)
+  , mTagName(aTagName)
+{ }
+
+ProfileEntry2::ProfileEntry2(char aTagName, void *aTagPtr)
+  : mTagPtr(aTagPtr)
+  , mTagName(aTagName)
+{ }
+
+ProfileEntry2::ProfileEntry2(char aTagName, double aTagFloat)
+  : mTagFloat(aTagFloat)
+  , mTagName(aTagName)
+{ }
+
+ProfileEntry2::ProfileEntry2(char aTagName, uintptr_t aTagOffset)
+  : mTagOffset(aTagOffset)
+  , mTagName(aTagName)
+{ }
+
+ProfileEntry2::ProfileEntry2(char aTagName, Address aTagAddress)
+  : mTagAddress(aTagAddress)
+  , mTagName(aTagName)
+{ }
+
+ProfileEntry2::ProfileEntry2(char aTagName, int aTagLine)
+  : mTagLine(aTagLine)
+  , mTagName(aTagName)
+{ }
+
+ProfileEntry2::ProfileEntry2(char aTagName, char aTagChar)
+  : mTagChar(aTagChar)
+  , mTagName(aTagName)
+{ }
+
+bool ProfileEntry2::is_ent_hint(char hintChar) {
+  return mTagName == 'h' && mTagChar == hintChar;
+}
+
+bool ProfileEntry2::is_ent_hint() {
+  return mTagName == 'h';
+}
+
+bool ProfileEntry2::is_ent(char tagChar) {
+  return mTagName == tagChar;
+}
+
+void* ProfileEntry2::get_tagPtr() {
+  // No consistency checking.  Oh well.
+  return mTagPtr;
+}
+
+void ProfileEntry2::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
+  //   mTagFloat  (double)       r,t
+  switch (mTagName) {
+    case 'm': case 'c': case 's':
+      LOGF("%c \"%s\"", mTagName, mTagData); break;
+    case 'd': case 'l': case 'L': case 'S':
+      LOGF("%c %p", mTagName, mTagPtr); break;
+    case 'n': case 'f':
+      LOGF("%c %d", mTagName, mTagLine); break;
+    case 'h':
+      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
+////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////
+// BEGIN ThreadProfile2
+
+#define PROFILE_MAX_ENTRY  100000
+#define DYNAMIC_MAX_STRING 512
+
+ThreadProfile2::ThreadProfile2(int aEntrySize, PseudoStack *aStack)
+  : mWritePos(0)
+  , mLastFlushPos(0)
+  , mReadPos(0)
+  , mEntrySize(aEntrySize)
+  , mPseudoStack(aStack)
+  , mMutex("ThreadProfile2::mMutex")
+{
+  mEntries = new ProfileEntry2[mEntrySize];
+}
+
+ThreadProfile2::~ThreadProfile2()
+{
+  delete[] mEntries;
+}
+
+void ThreadProfile2::addTag(ProfileEntry2 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();
+    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()
+{
+  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 ThreadProfile2::erase()
+{
+  mWritePos = mLastFlushPos;
+}
+
+char* ThreadProfile2::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];
+    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)
+{
+  JSObjectBuilder b(aCx);
+
+  JSCustomObject *profile = b.CreateObject();
+  JSCustomArray *samples = b.CreateArray();
+  b.DefineProperty(profile, "samples", samples);
+
+  JSCustomObject *sample = NULL;
+  JSCustomArray *frames = NULL;
+
+  int readPos = mReadPos;
+  while (readPos != mLastFlushPos) {
+    // Number of tag consumed
+    int incBy = 1;
+    ProfileEntry2 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);
+        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;
+  }
+
+  return profile;
+}
+
+PseudoStack* ThreadProfile2::GetPseudoStack()
+{
+  return mPseudoStack;
+}
+
+mozilla::Mutex* ThreadProfile2::GetMutex()
+{
+  return &mMutex;
+}
+
+// END ThreadProfile2
+////////////////////////////////////////////////////////////////////////
new file mode 100644
--- /dev/null
+++ b/tools/profiler/ProfileEntry2.h
@@ -0,0 +1,75 @@
+/* -*- 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 MOZ_PROFILE_ENTRY_H
+#define MOZ_PROFILE_ENTRY_H
+
+#include "mozilla/Mutex.h"
+
+class ThreadProfile2;
+
+class ProfileEntry2
+{
+public:
+  ProfileEntry2();
+
+  // 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);
+  bool is_ent_hint(char hintChar);
+  bool is_ent_hint();
+  bool is_ent(char tagName);
+  void* get_tagPtr();
+  void log();
+
+private:
+  friend class ThreadProfile2;
+  union {
+    const char* mTagData;
+    char        mTagChars[sizeof(void*)];
+    void*       mTagPtr;
+    double      mTagFloat;
+    Address     mTagAddress;
+    uintptr_t   mTagOffset;
+    int         mTagLine;
+    char        mTagChar;
+  };
+  char mTagName;
+};
+
+
+class ThreadProfile2
+{
+public:
+  ThreadProfile2(int aEntrySize, PseudoStack *aStack);
+  ~ThreadProfile2();
+  void addTag(ProfileEntry2 aTag);
+  void flush();
+  void erase();
+  char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff);
+  friend std::ostream& operator<<(std::ostream& stream,
+                                  const ThreadProfile2& profile);
+  JSCustomObject *ToJSObject(JSContext *aCx);
+  PseudoStack* GetPseudoStack();
+  mozilla::Mutex* GetMutex();
+private:
+  // Circular buffer 'Keep One Slot Open' implementation
+  // for simplicity
+  ProfileEntry2* 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;
+};
+
+#endif /* ndef MOZ_PROFILE_ENTRY_H */
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -26,16 +26,17 @@
 #include "nsIHttpProtocolHandler.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIXULRuntime.h"
 #include "nsIXULAppInfo.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
+#include "PlatformMacros.h"
 
 // JS
 #include "jsdbgapi.h"
 
 // we eventually want to make this runtime switchable
 #if defined(MOZ_PROFILING) && (defined(XP_UNIX) && !defined(XP_MACOSX))
  #ifndef ANDROID
   #define USE_BACKTRACE
@@ -77,23 +78,17 @@ using namespace mozilla;
 #endif
 
 #if _MSC_VER
  #define snprintf _snprintf
 #endif
 
 static const int DYNAMIC_MAX_STRING = 512;
 
-mozilla::ThreadLocal<ProfileStack *> tlsStack;
-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;
+static mozilla::ThreadLocal<TableTicker *> tlsTicker;
 
 TimeStamp sLastTracerEvent;
 int sFrameNumber = 0;
 int sLastFrameNumber = 0;
 
 class ThreadProfile;
 
 class ProfileEntry
@@ -150,17 +145,17 @@ public:
 };
 
 typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagStringData);
 
 #define PROFILE_MAX_ENTRY 100000
 class ThreadProfile
 {
 public:
-  ThreadProfile(int aEntrySize, ProfileStack *aStack)
+  ThreadProfile(int aEntrySize, PseudoStack *aStack)
     : mWritePos(0)
     , mLastFlushPos(0)
     , mReadPos(0)
     , mEntrySize(aEntrySize)
     , mStack(aStack)
   {
     mEntries = new ProfileEntry[mEntrySize];
   }
@@ -411,45 +406,45 @@ public:
               b.ArrayPush(frames, frame);
             }
           }
       }
       readPos = (readPos + incBy) % mEntrySize;
     }
   }
 
-  ProfileStack* GetStack()
+  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;
-  ProfileStack *mStack;
+  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, ProfileStack *aStack,
+  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");
 
@@ -581,17 +576,17 @@ public:
       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_data(cx);
+        JSObject* profileObj = mozilla_sampler_get_profile_data1(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.");
       }
     }
@@ -724,17 +719,17 @@ void addDynamicTag(ThreadProfile &aProfi
     j += sizeof(void*)/sizeof(char);
     // Cast to *((void**) to pass the text data to a void*
     aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
   }
 }
 
 static
 void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
-                     ProfileStack *stack, void *lastpc)
+                     PseudoStack *stack, void *lastpc)
 {
   int lineno = -1;
 
   // 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
@@ -838,17 +833,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)"));
 
-    ProfileStack* stack = aProfile.GetStack();
+    PseudoStack* stack = aProfile.GetStack();
     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.
@@ -877,17 +872,17 @@ void TableTicker::doBacktrace(ThreadProf
 
       aProfile.addTag(ProfileEntry('l', (void*)array.array[i-1]));
     }
   }
 }
 #endif
 
 static
-void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSample *sample)
+void doSampleStackTrace(PseudoStack *aStack, ThreadProfile &aProfile, TickSample *sample)
 {
   // Sample
   // 's' tag denotes the start of a sample block
   // followed by 0 or more 'c' tags.
   aProfile.addTag(ProfileEntry('s', "(root)"));
   for (uint32_t i = 0; i < aStack->stackSize(); i++) {
     addProfileEntry(aStack->mStack[i], aProfile, aStack, nullptr);
   }
@@ -911,17 +906,17 @@ 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
-  ProfileStack* stack = mPrimaryThreadProfile.GetStack();
+  PseudoStack* stack = mPrimaryThreadProfile.GetStack();
   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
@@ -997,29 +992,72 @@ std::ostream& operator<<(std::ostream& s
   } else if (entry.mTagName == 'd') {
     // TODO implement 'd' tag for text profile
   } else {
     stream << entry.mTagName << "-" << entry.mTagData << "\n";
   }
   return stream;
 }
 
-void mozilla_sampler_init()
+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 (!tlsStack.init() || !tlsTicker.init()) {
+  if (!tlsPseudoStack.init() || !tlsTicker.init()) {
     LOG("Failed to init.");
     return;
   }
   stack_key_initialized = true;
 
-  ProfileStack *stack = new ProfileStack();
-  tlsStack.set(stack);
+  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");
@@ -1027,55 +1065,55 @@ void mozilla_sampler_init()
     return;
   }
 
   const char* features[] = {"js", "leaf"
 #if defined(XP_WIN) || defined(XP_MACOSX)
                          , "stackwalk"
 #endif
                          };
-  mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
-                        features, sizeof(features)/sizeof(const char*));
+  mozilla_sampler_start1(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
+                         features, sizeof(features)/sizeof(const char*));
 }
 
-void mozilla_sampler_shutdown()
+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_stop();
+  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_save()
+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_profile()
+char* mozilla_sampler_get_profile1()
 {
   TableTicker *t = tlsTicker.get();
   if (!t) {
     return NULL;
   }
 
   std::stringstream profile;
   t->SetPaused(true);
@@ -1083,28 +1121,28 @@ char* mozilla_sampler_get_profile()
   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)
+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_features()
+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
@@ -1112,146 +1150,145 @@ const char** mozilla_sampler_get_feature
     "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)
+void mozilla_sampler_start1(int aProfileEntries, int aInterval,
+                            const char** aFeatures, uint32_t aFeatureCount)
 {
   if (!stack_key_initialized)
-    mozilla_sampler_init();
+    mozilla_sampler_init1();
 
-  ProfileStack *stack = tlsStack.get();
+  PseudoStack *stack = tlsPseudoStack.get();
   if (!stack) {
     ASSERT(false);
     return;
   }
 
-  mozilla_sampler_stop();
+  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_stop()
+void mozilla_sampler_stop1()
 {
   if (!stack_key_initialized)
-    mozilla_sampler_init();
+    mozilla_sampler_init1();
 
   TableTicker *t = tlsTicker.get();
   if (!t) {
     return;
   }
 
   bool disableJS = t->ProfileJS();
 
   t->Stop();
   delete t;
   tlsTicker.set(NULL);
-  ProfileStack *stack = tlsStack.get();
+  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()
+bool mozilla_sampler_is_active1()
 {
   if (!stack_key_initialized)
-    mozilla_sampler_init();
+    mozilla_sampler_init1();
 
   TableTicker *t = tlsTicker.get();
   if (!t) {
     return false;
   }
 
   return t->IsActive();
 }
 
-double sResponsivenessTimes[100];
-double sCurrResponsiveness = 0.f;
-unsigned int sResponsivenessLoc = 0;
-void mozilla_sampler_responsiveness(TimeStamp aTime)
+static double sResponsivenessTimes[100];
+static unsigned int sResponsivenessLoc = 0;
+void mozilla_sampler_responsiveness1(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()
+const double* mozilla_sampler_get_responsiveness1()
 {
   return sResponsivenessTimes;
 }
 
-void mozilla_sampler_frame_number(int frameNumber)
+void mozilla_sampler_frame_number1(int frameNumber)
 {
   sFrameNumber = frameNumber;
 }
 
-void print_callback(const ProfileEntry& entry, const char* tagStringData) {
+static void print_callback(const ProfileEntry& entry, const char* tagStringData) {
   switch (entry.mTagName) {
     case 's':
     case 'c':
       printf_stderr("  %s\n", tagStringData);
   }
 }
 
-void mozilla_sampler_print_location()
+void mozilla_sampler_print_location1()
 {
   if (!stack_key_initialized)
-    mozilla_sampler_init();
+    mozilla_sampler_init1();
 
-  ProfileStack *stack = tlsStack.get();
+  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_lock()
+void mozilla_sampler_lock1()
 {
-  mozilla_sampler_stop();
+  mozilla_sampler_stop1();
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os)
     os->NotifyObservers(nullptr, "profiler-locked", nullptr);
 }
 
-void mozilla_sampler_unlock()
+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/TableTicker2.cpp
@@ -0,0 +1,1007 @@
+/* -*- 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/. */
+
+// System
+#include <string>
+#include <stdio.h>
+#include <errno.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#if defined(ANDROID)
+# include "android-signal-defs.h"
+#endif
+
+// Profiler
+#include "PlatformMacros.h"
+#include "sps_sampler.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 "UnwinderThread2.h"
+
+// JSON
+#include "JSObjectBuilder.h"
+#include "nsIJSRuntimeService.h"
+
+// Meta
+#include "nsXPCOM.h"
+#include "nsXPCOMCID.h"
+#include "nsIHttpProtocolHandler.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIXULRuntime.h"
+#include "nsIXULAppInfo.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+
+// JS
+#include "jsdbgapi.h"
+
+// This file's exports are listed in sps_sampler.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;
+
+
+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,
+                     volatile StackEntry &entry,
+                     PseudoStack *stack, void *lastpc)
+{
+  int lineno = -1;
+
+  // Add a pseudostack-entry start label
+  utb__addEntry( utb, ProfileEntry2('h', 'P') );
+  // And the SP value, if it is non-zero
+  if (entry.stackAddress() != 0) {
+    utb__addEntry( utb, ProfileEntry2('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', "") );
+    // 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]))) );
+    }
+    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(),
+                                                lastpc);
+          if (jspc) {
+            lineno = JS_PCToLineNumber(NULL, entry.script(), jspc);
+          }
+        }
+      } else {
+        lineno = JS_PCToLineNumber(NULL, entry.script(), entry.pc());
+      }
+    } else {
+      lineno = entry.line();
+    }
+  } else {
+    utb__addEntry( utb, ProfileEntry2('c', sampleLabel) );
+    lineno = entry.line();
+  }
+  if (lineno != -1) {
+    utb__addEntry( utb, ProfileEntry2('n', lineno) );
+  }
+
+  // Add a pseudostack-entry end label
+  utb__addEntry( utb, ProfileEntry2('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
+  // 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);
+  }
+# ifdef ENABLE_SPS_LEAF_DATA
+  if (sample) {
+    utb__addEntry( utb, ProfileEntry2('l', (void*)sample->pc) );
+#   ifdef ENABLE_ARM_LR_SAVING
+    utb__addEntry( utb, ProfileEntry2('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)
+{
+  /* 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. */
+  if (!utb)
+    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)) );
+  }
+  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) {
+      // XXX: we also probably want to add an entry to the profile to help
+      // distinguish which samples are part of the same event. That, or record
+      // the event generation in each sample
+      mPrimaryThreadProfile.erase();
+    }
+    sLastSampledEventGeneration = sCurrentEventGeneration;
+
+    recordSample = false;
+    // only record the events when we have a we haven't seen a tracer
+    // event for 100ms
+    if (!sLastTracerEvent.IsNull()) {
+      TimeDuration delta = sample->timestamp - sLastTracerEvent;
+      if (delta.ToMilliseconds() > 100.0) {
+          recordSample = true;
+      }
+    }
+  }
+
+  // JRS 2012-Sept-27: this logic used to involve mUseStackWalk.
+  // That should be reinstated, but for the moment, use the
+  // 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*/) );
+      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*/) );
+      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*/) );
+  }
+
+  // Add any extras
+  if (!sLastTracerEvent.IsNull() && sample) {
+    TimeDuration delta = sample->timestamp - sLastTracerEvent;
+    utb__addEntry( utb, ProfileEntry2('r', delta.ToMilliseconds()) );
+  }
+
+  if (sample) {
+    TimeDuration delta = sample->timestamp - mStartTime;
+    utb__addEntry( utb, ProfileEntry2('t', delta.ToMilliseconds()) );
+  }
+
+  if (sLastFrameNumber != sFrameNumber) {
+    utb__addEntry( utb, ProfileEntry2('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
+     stack and register context needed to do a native unwind, if that
+     is currently enabled. */
+
+  /* If a native unwind has been requested, we'll start it off using
+     the context obtained from the signal handler, to avoid the
+     problem of having to unwind through the signal frame itself. */
+
+  /* On Linux and Android, the initial register state is in the
+     supplied sample->context.  But on MacOS it's not, so we have to
+     fake it up here (sigh). */
+  if (sUnwindMode == UnwNATIVE || sUnwindMode == UnwCOMBINED) {
+#   if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
+       || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
+    void* ucV = (void*)sample->context;
+#   elif defined(SPS_PLAT_amd64_darwin)
+    struct __darwin_mcontext64 mc;
+    memset(&mc, 0, sizeof(mc));
+    ucontext_t uc;
+    memset(&uc, 0, sizeof(uc));
+    uc.uc_mcontext = &mc;
+    mc.__ss.__rip = (uint64_t)sample->pc;
+    mc.__ss.__rsp = (uint64_t)sample->sp;
+    mc.__ss.__rbp = (uint64_t)sample->fp;
+    void* ucV = (void*)&uc;
+#   elif defined(SPS_PLAT_x86_darwin)
+    struct __darwin_mcontext32 mc;
+    memset(&mc, 0, sizeof(mc));
+    ucontext_t uc;
+    memset(&uc, 0, sizeof(uc));
+    uc.uc_mcontext = &mc;
+    mc.__ss.__eip = (uint32_t)sample->pc;
+    mc.__ss.__esp = (uint32_t)sample->sp;
+    mc.__ss.__ebp = (uint32_t)sample->fp;
+    void* ucV = (void*)&uc;
+#   elif defined(SPS_OS_windows)
+    /* Totally fake this up so it at least builds.  No idea if we can
+       even ever get here on Windows. */
+    void* ucV = NULL;
+#   else
+#     error "Unsupported platform"
+#   endif
+    uwt__release_full_buffer(&mPrimaryThreadProfile, utb, ucV);
+  } 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 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(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
+////////////////////////////////////////////////////////////////////////
new file mode 100644
--- /dev/null
+++ b/tools/profiler/UnwinderThread2.cpp
@@ -0,0 +1,1766 @@
+/* -*- 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 <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef MOZ_VALGRIND
+# include <valgrind/helgrind.h>
+# include <valgrind/memcheck.h>
+#else
+# define VALGRIND_HG_MUTEX_LOCK_PRE(_mx,_istry)  /* */
+# define VALGRIND_HG_MUTEX_LOCK_POST(_mx)        /* */
+# define VALGRIND_HG_MUTEX_UNLOCK_PRE(_mx)       /* */
+# define VALGRIND_HG_MUTEX_UNLOCK_POST(_mx)      /* */
+# define VALGRIND_MAKE_MEM_DEFINED(_addr,_len)   ((void)0)
+# define VALGRIND_MAKE_MEM_UNDEFINED(_addr,_len) ((void)0)
+#endif
+
+#include "mozilla/arm.h"
+#include "mozilla/StandardInteger.h"
+#include "PlatformMacros.h"
+
+#include "sps_sampler.h"
+#include "platform.h"
+#include <iostream>
+
+#include "ProfileEntry2.h"
+#include "UnwinderThread2.h"
+
+#if !defined(SPS_OS_windows)
+# include <sys/time.h>
+# include <unistd.h>
+# include <pthread.h>
+  // mmap
+# include <sys/mman.h>
+#endif
+
+#if defined(SPS_OS_android)
+# include "android-signal-defs.h"
+#endif
+
+#if defined(SPS_OS_darwin)
+# include <mach-o/dyld.h>
+#endif
+
+
+/* Verbosity of this module, for debugging:
+     0  silent
+     1  adds info about debuginfo load success/failure
+     2  adds slow-summary stats for buffer fills/misses (RECOMMENDED)
+     3  adds per-sample summary lines
+     4  adds per-sample frame listing
+   Note that level 3 and above produces risk of deadlock, and 
+   are not recommended for extended use.
+*/
+#define LOGLEVEL 2
+
+
+// The 'else' of this covers the entire rest of the file
+#if defined(SPS_OS_windows)
+
+//////////////////////////////////////////////////////////
+//// BEGIN externally visible functions (WINDOWS STUBS)
+
+// On Windows this will all need reworking.  sps_sampler.h
+// will ensure these functions are never actually called,
+// so just provide no-op stubs for now.
+
+void uwt__init()
+{
+}
+
+void uwt__deinit()
+{
+}
+
+void uwt__register_thread_for_profiling ( void* stackTop )
+{
+}
+
+// RUNS IN SIGHANDLER CONTEXT
+UnwinderThreadBuffer* uwt__acquire_empty_buffer()
+{
+  return NULL;
+}
+
+// RUNS IN SIGHANDLER CONTEXT
+void
+uwt__release_full_buffer(ThreadProfile2* aProfile,
+                         UnwinderThreadBuffer* utb,
+                         void* /* ucontext_t*, really */ ucV )
+{
+}
+
+// RUNS IN SIGHANDLER CONTEXT
+void
+utb__addEntry(/*MODIFIED*/UnwinderThreadBuffer* utb, ProfileEntry2 ent)
+{
+}
+
+//// END externally visible functions (WINDOWS STUBS)
+//////////////////////////////////////////////////////////
+
+#else // a supported target
+
+//////////////////////////////////////////////////////////
+//// BEGIN externally visible functions
+
+// Forward references
+// the unwinder thread ID, its fn, and a stop-now flag
+static void* unwind_thr_fn ( void* exit_nowV );
+static pthread_t unwind_thr;
+static int       unwind_thr_exit_now = 0; // RACED ON
+
+// Threads must be registered with this file before they can be
+// sampled.  So that we know the max safe stack address for each
+// registered thread.
+static void thread_register_for_profiling ( void* stackTop );
+
+// RUNS IN SIGHANDLER CONTEXT
+// Acquire an empty buffer and mark it as FILLING
+static UnwinderThreadBuffer* acquire_empty_buffer();
+
+// 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,
+                                UnwinderThreadBuffer* utb,
+                                void* /* ucontext_t*, really */ ucV );
+
+// RUNS IN SIGHANDLER CONTEXT
+static void utb_add_prof_ent(UnwinderThreadBuffer* utb, ProfileEntry2 ent);
+
+// Do a store memory barrier.
+static void do_MBAR();
+
+
+void uwt__init()
+{
+  // Create the unwinder thread.
+  MOZ_ASSERT(unwind_thr_exit_now == 0);
+  int r = pthread_create( &unwind_thr, NULL,
+                          unwind_thr_fn, (void*)&unwind_thr_exit_now );
+  MOZ_ALWAYS_TRUE(r==0);
+}
+
+void uwt__deinit()
+{
+  // Shut down the unwinder thread.
+  MOZ_ASSERT(unwind_thr_exit_now == 0);
+  unwind_thr_exit_now = 1;
+  do_MBAR();
+  int r = pthread_join(unwind_thr, NULL); MOZ_ALWAYS_TRUE(r==0);
+}
+
+void uwt__register_thread_for_profiling(void* stackTop)
+{
+  thread_register_for_profiling(stackTop);
+}
+
+// RUNS IN SIGHANDLER CONTEXT
+UnwinderThreadBuffer* uwt__acquire_empty_buffer()
+{
+  return acquire_empty_buffer();
+}
+
+// RUNS IN SIGHANDLER CONTEXT
+void
+uwt__release_full_buffer(ThreadProfile2* 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_add_prof_ent(utb, ent);
+}
+
+//// END externally visible functions
+//////////////////////////////////////////////////////////
+
+
+//////////////////////////////////////////////////////////
+//// BEGIN type UnwindThreadBuffer
+
+MOZ_STATIC_ASSERT(sizeof(uint32_t) == 4, "uint32_t size incorrect");
+MOZ_STATIC_ASSERT(sizeof(uint64_t) == 8, "uint64_t size incorrect");
+MOZ_STATIC_ASSERT(sizeof(uintptr_t) == sizeof(void*),
+                  "uintptr_t size incorrect");
+
+typedef
+  struct { 
+    uint64_t rsp;
+    uint64_t rbp;
+    uint64_t rip; 
+  }
+  AMD64Regs;
+
+typedef
+  struct {
+    uint32_t r15;
+    uint32_t r14;
+    uint32_t r13;
+    uint32_t r12;
+    uint32_t r11;
+    uint32_t r7;
+  }
+  ARMRegs;
+
+typedef
+  struct {
+    uint32_t esp;
+    uint32_t ebp;
+    uint32_t eip;
+  }
+  X86Regs;
+
+#if defined(SPS_ARCH_amd64)
+typedef  AMD64Regs  ArchRegs;
+#elif defined(SPS_ARCH_arm)
+typedef  ARMRegs  ArchRegs;
+#elif defined(SPS_ARCH_x86)
+typedef  X86Regs  ArchRegs;
+#else
+# error "Unknown plat"
+#endif
+
+#if defined(SPS_ARCH_amd64) || defined(SPS_ARCH_arm) || defined(SPS_ARCH_x86)
+# define SPS_PAGE_SIZE 4096
+#else
+# error "Unknown plat"
+#endif
+
+typedef  enum { S_EMPTY, S_FILLING, S_EMPTYING, S_FULL }  State;
+
+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
+   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
+   is enough to hold 51200. */
+#define N_PROF_ENT_PAGES 100
+
+/* DERIVATIVE */
+#define N_PROF_ENTS_PER_PAGE (SPS_PAGE_SIZE / sizeof(ProfileEntry2))
+
+/* A page of ProfileEntry2s.  This might actually be slightly smaller
+   than a page if SPS_PAGE_SIZE is not an exact multiple of
+   sizeof(ProfileEntry2). */
+typedef
+  struct { ProfileEntry2 ents[N_PROF_ENTS_PER_PAGE]; }
+  ProfEntsPage;
+
+#define ProfEntsPage_INVALID ((ProfEntsPage*)1)
+
+
+/* Fields protected by the spinlock are marked SL */
+
+struct _UnwinderThreadBuffer {
+  /*SL*/ State  state;
+  /* The rest of these are protected, in some sense, by ::state.  If
+     ::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
+     dumped. */
+  ThreadProfile2* aProfile;
+  /* Pseudostack and other info, always present */
+  ProfileEntry2   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];
+  unsigned int   stackImgUsed;
+  void*          stackImgAddr; /* VMA corresponding to stackImg[0] */
+  void*          stackMaxSafe; /* VMA for max safe stack reading */
+};
+/* Indexing scheme for ents:
+     0 <= i < N_FIXED_PROF_ENTS
+       is at entsFixed[i]
+
+     i >= N_FIXED_PROF_ENTS
+       is at let j = i - N_FIXED_PROF_ENTS
+             in  entsPages[j / N_PROFENTS_PER_PAGE]
+                  ->ents[j % N_PROFENTS_PER_PAGE]
+     
+   entsPages[] are allocated on demand.  Because zero can
+   theoretically be a valid page pointer, use 
+   ProfEntsPage_INVALID == (ProfEntsPage*)1 to mark invalid pages.
+
+   It follows that the max entsUsed value is N_FIXED_PROF_ENTS +
+   N_PROFENTS_PER_PAGE * N_PROFENTS_PAGES, and at that point no more
+   ProfileEntries can be storedd.
+*/
+
+
+typedef
+  struct {
+    pthread_t thrId;
+    void*     stackTop;
+    uint64_t  nSamples; 
+  }
+  StackLimit;
+
+/* Globals -- the buffer array */
+#define N_UNW_THR_BUFFERS 10
+/*SL*/ static UnwinderThreadBuffer** g_buffers     = NULL;
+/*SL*/ static uint64_t               g_seqNo       = 0;
+/*SL*/ static SpinLock               g_spinLock    = { 0 };
+
+/* Globals -- the thread array */
+#define N_SAMPLING_THREADS 10
+/*SL*/ static StackLimit g_stackLimits[N_SAMPLING_THREADS];
+/*SL*/ static int        g_stackLimitsUsed = 0;
+
+/* Stats -- atomically incremented, no lock needed */
+static uintptr_t g_stats_totalSamples = 0; // total # sample attempts
+static uintptr_t g_stats_noBuffAvail  = 0; // # failed due to no buffer avail
+
+/* We must be VERY CAREFUL what we do with the spinlock held.  The
+   only thing it is safe to do with it held is modify (viz, read or
+   write) g_buffers, g_buffers[], g_seqNo, g_buffers[]->state,
+   g_stackLimits[] and g_stackLimitsUsed.  No arbitrary computations,
+   no syscalls, no printfs, no file IO, and absolutely no dynamic
+   memory allocation (else we WILL eventually deadlock).
+
+   This applies both to the signal handler and to the unwinder thread.
+*/
+
+//// END type UnwindThreadBuffer
+//////////////////////////////////////////////////////////
+
+// fwds
+// the interface to breakpad
+typedef  struct { u_int64_t pc; u_int64_t sp; }  PCandSP;
+
+static
+void do_breakpad_unwind_Buffer(/*OUT*/PCandSP** pairs,
+                               /*OUT*/unsigned int* nPairs,
+                               UnwinderThreadBuffer* buff,
+                               int buffNo /* for debug printing only */);
+
+static bool is_page_aligned(void* v)
+{
+  uintptr_t w = (uintptr_t) v;
+  return (w & (SPS_PAGE_SIZE-1)) == 0  ? true  : false;
+}
+
+
+/* Implement machine-word sized atomic compare-and-swap.  Returns true
+   if success, false if failure. */
+static bool do_CASW(uintptr_t* addr, uintptr_t expected, uintptr_t nyu)
+{
+#if defined(__GNUC__)
+  return __sync_bool_compare_and_swap(addr, expected, nyu);
+#else
+# error "Unhandled compiler"
+#endif
+}
+
+/* Hint to the CPU core that we are in a spin-wait loop, and that
+   other processors/cores/threads-running-on-the-same-core should be
+   given priority on execute resources, if that is possible.  Not
+   critical if this is a no-op on some targets. */
+static void do_SPINLOOP_RELAX()
+{
+#if (defined(SPS_ARCH_amd64) || defined(SPS_ARCH_x86)) && defined(__GNUC__)
+  __asm__ __volatile__("rep; nop");
+#elif defined(SPS_PLAT_arm_android) && MOZILLA_ARM_ARCH >= 7
+  __asm__ __volatile__("wfe");
+#endif
+}
+
+/* Tell any cores snoozing in spin loops to wake up. */
+static void do_SPINLOOP_NUDGE()
+{
+#if (defined(SPS_ARCH_amd64) || defined(SPS_ARCH_x86)) && defined(__GNUC__)
+  /* this is a no-op */
+#elif defined(SPS_PLAT_arm_android) && MOZILLA_ARM_ARCH >= 7
+  __asm__ __volatile__("sev");
+#endif
+}
+
+/* Perform a full memory barrier. */
+static void do_MBAR()
+{
+#if defined(__GNUC__)
+  __sync_synchronize();
+#else
+# error "Unhandled compiler"
+#endif
+}
+
+static void spinLock_acquire(SpinLock* sl)
+{
+  uintptr_t* val = &sl->val;
+  VALGRIND_HG_MUTEX_LOCK_PRE(sl, 0/*!isTryLock*/);
+  while (1) {
+    bool ok = do_CASW( val, 0, 1 );
+    if (ok) break;
+    do_SPINLOOP_RELAX();
+  }
+  do_MBAR();
+  VALGRIND_HG_MUTEX_LOCK_POST(sl);
+}
+
+static void spinLock_release(SpinLock* sl)
+{
+  uintptr_t* val = &sl->val;
+  VALGRIND_HG_MUTEX_UNLOCK_PRE(sl);
+  do_MBAR();
+  bool ok = do_CASW( val, 1, 0 );
+  /* This must succeed at the first try.  To fail would imply that
+     the lock was unheld. */
+  MOZ_ALWAYS_TRUE(ok);
+  do_SPINLOOP_NUDGE();
+  VALGRIND_HG_MUTEX_UNLOCK_POST(sl);
+}
+
+static void sleep_ms(unsigned int ms)
+{
+  struct timespec req;
+  req.tv_sec = ((time_t)ms) / 1000;
+  req.tv_nsec = 1000 * 1000 * (((unsigned long)ms) % 1000);
+  nanosleep(&req, NULL);
+}
+
+/* Use CAS to implement standalone atomic increment. */
+static void atomic_INC(uintptr_t* loc)
+{
+  while (1) {
+    uintptr_t old = *loc;
+    uintptr_t nyu = old + 1;
+    bool ok = do_CASW( loc, old, nyu );
+    if (ok) break;
+  }
+}
+
+/* Register a thread for profiling.  It must not be allowed to receive
+   signals before this is done, else the signal handler will
+   MOZ_ASSERT. */
+static void thread_register_for_profiling(void* stackTop)
+{
+  int i;
+  /* Minimal sanity check on stackTop */
+  MOZ_ASSERT( (void*)&i < stackTop );
+
+  spinLock_acquire(&g_spinLock);
+
+  pthread_t me = pthread_self();
+  for (i = 0; i < g_stackLimitsUsed; i++) {
+    /* check for duplicate registration */
+    MOZ_ASSERT(g_stackLimits[i].thrId != me);
+  }
+  if (!(g_stackLimitsUsed < N_SAMPLING_THREADS))
+    MOZ_CRASH();  // Don't continue -- we'll get memory corruption.
+  g_stackLimits[g_stackLimitsUsed].thrId    = me;
+  g_stackLimits[g_stackLimitsUsed].stackTop = stackTop;
+  g_stackLimits[g_stackLimitsUsed].nSamples = 0;
+  g_stackLimitsUsed++;
+
+  spinLock_release(&g_spinLock);
+  LOGF("BPUnw: thread_register_for_profiling(stacktop %p, me %p)", 
+       stackTop, (void*)me);
+}
+
+
+__attribute__((unused))
+static void show_registered_threads()
+{
+  int i;
+  spinLock_acquire(&g_spinLock);
+  for (i = 0; i < g_stackLimitsUsed; i++) {
+    LOGF("[%d]  pthread_t=%p  nSamples=%lld",
+         i, (void*)g_stackLimits[i].thrId, 
+            (unsigned long long int)g_stackLimits[i].nSamples);
+  }
+  spinLock_release(&g_spinLock);
+}
+
+
+// RUNS IN SIGHANDLER CONTEXT
+static UnwinderThreadBuffer* acquire_empty_buffer()
+{
+  /* acq lock
+     if buffers == NULL { rel lock; exit }
+     scan to find a free buff; if none { rel lock; exit }
+     set buff state to S_FILLING
+     fillseqno++; and remember it
+     rel lock
+  */
+  int i;
+
+  atomic_INC( &g_stats_totalSamples );
+
+  /* This code is critical.  We are in a signal handler and possibly
+     with the malloc lock held.  So we can't allocate any heap, and
+     can't safely call any C library functions, not even the pthread_
+     functions.  And we certainly can't do any syscalls.  In short,
+     this function needs to be self contained, not do any allocation,
+     and not hold on to the spinlock for any significant length of
+     time. */
+
+  spinLock_acquire(&g_spinLock);
+
+  /* First of all, look for this thread's entry in g_stackLimits[].
+     We need to find it in order to figure out how much stack we can
+     safely copy into the sample.  This assumes that pthread_self()
+     is safe to call in a signal handler, which strikes me as highly
+     likely. */
+  pthread_t me = pthread_self();
+  MOZ_ASSERT(g_stackLimitsUsed >= 0 && g_stackLimitsUsed <= N_SAMPLING_THREADS);
+  for (i = 0; i < g_stackLimitsUsed; i++) {
+    if (g_stackLimits[i].thrId == me)
+      break;
+  }
+  /* "this thread is registered for profiling" */
+  MOZ_ASSERT(i < g_stackLimitsUsed);
+
+  /* The furthest point that we can safely scan back up the stack. */
+  void* myStackTop = g_stackLimits[i].stackTop;
+  g_stackLimits[i].nSamples++;
+
+  /* Try to find a free buffer to use. */
+  if (g_buffers == NULL) {
+    /* The unwinder thread hasn't allocated any buffers yet.
+       Nothing we can do. */
+    spinLock_release(&g_spinLock);
+    atomic_INC( &g_stats_noBuffAvail );
+    return NULL;
+  }
+
+  for (i = 0; i < N_UNW_THR_BUFFERS; i++) {
+    if (g_buffers[i]->state == S_EMPTY)
+      break;
+  }
+  MOZ_ASSERT(i >= 0 && i <= N_UNW_THR_BUFFERS);
+
+  if (i == N_UNW_THR_BUFFERS) {
+    /* Again, no free buffers .. give up. */
+    spinLock_release(&g_spinLock);
+    atomic_INC( &g_stats_noBuffAvail );
+    if (LOGLEVEL >= 3)
+      LOG("BPUnw: handler:  no free buffers");
+    return NULL;
+  }
+
+  /* So we can use this one safely.  Whilst still holding the lock,
+     mark the buffer as belonging to us, and increment the sequence
+     number. */
+  UnwinderThreadBuffer* buff = g_buffers[i];
+  MOZ_ASSERT(buff->state == S_EMPTY);
+  buff->state = S_FILLING;
+  buff->seqNo = g_seqNo;
+  g_seqNo++;
+
+  /* And drop the lock.  We own the buffer, so go on and fill it. */
+  spinLock_release(&g_spinLock);
+
+  /* Now we own the buffer, initialise it. */
+  buff->aProfile       = NULL;
+  buff->entsUsed       = 0;
+  buff->haveNativeInfo = false;
+  buff->stackImgUsed   = 0;
+  buff->stackImgAddr   = 0;
+  buff->stackMaxSafe   = myStackTop; /* We will need this in
+                                        release_full_buffer() */
+  for (i = 0; i < N_PROF_ENT_PAGES; i++)
+    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,
+                                UnwinderThreadBuffer* buff,
+                                void* /* ucontext_t*, really */ ucV )
+{
+  MOZ_ASSERT(buff->state == S_FILLING);
+
+  ////////////////////////////////////////////////////
+  // BEGIN fill
+
+  /* The buffer already will have some of its ProfileEntries filled
+     in, but everything else needs to be filled in at this point. */
+  //LOGF("Release full buffer: %lu ents", buff->entsUsed);
+  /* Where the resulting info is to be dumped */
+  buff->aProfile = aProfile;
+
+  /* And, if we have register state, that and the stack top */
+  buff->haveNativeInfo = ucV != NULL;
+  if (buff->haveNativeInfo) {
+#   if defined(SPS_PLAT_amd64_linux)
+    ucontext_t* uc = (ucontext_t*)ucV;
+    mcontext_t* mc = &(uc->uc_mcontext);
+    buff->regs.rip = mc->gregs[REG_RIP];
+    buff->regs.rsp = mc->gregs[REG_RSP];
+    buff->regs.rbp = mc->gregs[REG_RBP];
+#   elif defined(SPS_PLAT_amd64_darwin)
+    ucontext_t* uc = (ucontext_t*)ucV;
+    struct __darwin_mcontext64* mc = uc->uc_mcontext;
+    struct __darwin_x86_thread_state64* ss = &mc->__ss;
+    buff->regs.rip = ss->__rip;
+    buff->regs.rsp = ss->__rsp;
+    buff->regs.rbp = ss->__rbp;
+#   elif defined(SPS_PLAT_arm_android)
+    ucontext_t* uc = (ucontext_t*)ucV;
+    mcontext_t* mc = &(uc->uc_mcontext);
+    buff->regs.r15 = mc->arm_pc; //gregs[R15];
+    buff->regs.r14 = mc->arm_lr; //gregs[R14];
+    buff->regs.r13 = mc->arm_sp; //gregs[R13];
+    buff->regs.r12 = mc->arm_ip; //gregs[R12];
+    buff->regs.r11 = mc->arm_fp; //gregs[R11];
+    buff->regs.r7  = mc->arm_r7; //gregs[R7];
+#   elif defined(SPS_PLAT_x86_linux)
+    ucontext_t* uc = (ucontext_t*)ucV;
+    mcontext_t* mc = &(uc->uc_mcontext);
+    buff->regs.eip = mc->gregs[REG_EIP];
+    buff->regs.esp = mc->gregs[REG_ESP];
+    buff->regs.ebp = mc->gregs[REG_EBP];
+#   elif defined(SPS_PLAT_x86_darwin)
+    ucontext_t* uc = (ucontext_t*)ucV;
+    struct __darwin_mcontext32* mc = uc->uc_mcontext;
+    struct __darwin_i386_thread_state* ss = &mc->__ss;
+    buff->regs.eip = ss->__eip;
+    buff->regs.esp = ss->__esp;
+    buff->regs.ebp = ss->__ebp;
+#   elif defined(SPS_PLAT_x86_android)
+    ucontext_t* uc = (ucontext_t*)ucV;
+    mcontext_t* mc = &(uc->uc_mcontext);
+    buff->regs.eip = mc->eip;
+    buff->regs.esp = mc->esp;
+    buff->regs.ebp = mc->ebp;
+#   else
+#     error "Unknown plat"
+#   endif
+
+    /* Copy up to N_STACK_BYTES from rsp-REDZONE upwards, but not
+       going past the stack's registered top point.  Do some basic
+       sanity checks too. */
+    { 
+#     if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_amd64_darwin)
+      uintptr_t rEDZONE_SIZE = 128;
+      uintptr_t start = buff->regs.rsp - rEDZONE_SIZE;
+#     elif defined(SPS_PLAT_arm_android)
+      uintptr_t rEDZONE_SIZE = 0;
+      uintptr_t start = buff->regs.r13 - rEDZONE_SIZE;
+#     elif defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_darwin) \
+           || defined(SPS_PLAT_x86_android)
+      uintptr_t rEDZONE_SIZE = 0;
+      uintptr_t start = buff->regs.esp - rEDZONE_SIZE;
+#     else
+#       error "Unknown plat"
+#     endif
+      uintptr_t end   = (uintptr_t)buff->stackMaxSafe;
+      uintptr_t ws    = sizeof(void*);
+      start &= ~(ws-1);
+      end   &= ~(ws-1);
+      uintptr_t nToCopy = 0;
+      if (start < end) {
+        nToCopy = end - start;
+        if (nToCopy > N_STACK_BYTES)
+          nToCopy = N_STACK_BYTES;
+      }
+      MOZ_ASSERT(nToCopy <= N_STACK_BYTES);
+      buff->stackImgUsed = nToCopy;
+      buff->stackImgAddr = (void*)start;
+      if (nToCopy > 0) {
+        memcpy(&buff->stackImg[0], (void*)start, nToCopy);
+        (void)VALGRIND_MAKE_MEM_DEFINED(&buff->stackImg[0], nToCopy);
+      }
+    }
+  } /* if (buff->haveNativeInfo) */
+  // END fill
+  ////////////////////////////////////////////////////
+
+  /* And now relinquish ownership of the buff, so that an unwinder
+     thread can pick it up. */
+  spinLock_acquire(&g_spinLock);
+  buff->state = S_FULL;
+  spinLock_release(&g_spinLock);
+}
+
+
+// RUNS IN SIGHANDLER CONTEXT
+// Allocate a ProfEntsPage, without using malloc, or return
+// ProfEntsPage_INVALID if we can't for some reason.
+static ProfEntsPage* mmap_anon_ProfEntsPage()
+{
+# if defined(SPS_OS_darwin)
+  void* v = ::mmap(NULL, sizeof(ProfEntsPage), PROT_READ|PROT_WRITE, 
+                   MAP_PRIVATE|MAP_ANON,      -1, 0);
+# else
+  void* v = ::mmap(NULL, sizeof(ProfEntsPage), PROT_READ|PROT_WRITE, 
+                   MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+# endif
+  if (v == MAP_FAILED) {
+    return ProfEntsPage_INVALID;
+  } else {
+    return (ProfEntsPage*)v;
+  }
+}
+
+// Runs in the unwinder thread
+// Free a ProfEntsPage as allocated by mmap_anon_ProfEntsPage
+static void munmap_ProfEntsPage(ProfEntsPage* pep)
+{
+  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)
+{
+  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.");
+    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++;
+    return;
+  }
+
+  /* No.  Put it in the extras. */
+  uintptr_t i     = utb->entsUsed;
+  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.");
+      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)
+{
+  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 */
+    MOZ_ASSERT(buff->entsPages[j_div] != ProfEntsPage_INVALID);
+    return buff->entsPages[j_div]->ents[j_mod];
+  }
+}
+
+
+// Runs in the unwinder thread -- well, this _is_ the unwinder thread.
+static void* unwind_thr_fn(void* exit_nowV)
+{
+  /* If we're the first thread in, we'll need to allocate the buffer
+     array g_buffers plus the Buffer structs that it points at. */
+  spinLock_acquire(&g_spinLock);
+  if (g_buffers == NULL) {
+    /* Drop the lock, make a complete copy in memory, reacquire the
+       lock, and try to install it -- which might fail, if someone
+       else beat us to it. */
+    spinLock_release(&g_spinLock);
+    UnwinderThreadBuffer** buffers
+      = (UnwinderThreadBuffer**)malloc(N_UNW_THR_BUFFERS
+                                        * sizeof(UnwinderThreadBuffer*));
+    MOZ_ASSERT(buffers);
+    int i;
+    for (i = 0; i < N_UNW_THR_BUFFERS; i++) {
+      buffers[i] = (UnwinderThreadBuffer*)
+                   calloc(sizeof(UnwinderThreadBuffer), 1);
+      MOZ_ASSERT(buffers[i]);
+      buffers[i]->state = S_EMPTY;
+    }
+    /* Try to install it */
+    spinLock_acquire(&g_spinLock);
+    if (g_buffers == NULL) {
+      g_buffers = buffers;
+      spinLock_release(&g_spinLock);
+    } else {
+      /* Someone else beat us to it.  Release what we just allocated
+         so as to avoid a leak. */
+      spinLock_release(&g_spinLock);
+      for (i = 0; i < N_UNW_THR_BUFFERS; i++) {
+        free(buffers[i]);
+      }
+      free(buffers);
+    }
+  } else {
+    /* They are already allocated, so just drop the lock and continue. */
+    spinLock_release(&g_spinLock);
+  }
+
+  /* 
+    while (1) {
+      acq lock
+      scan to find oldest full
+         if none { rel lock; sleep; continue }
+      set buff state to emptying
+      rel lock
+      acq MLock // implicitly
+      process buffer
+      rel MLock // implicitly
+      acq lock
+      set buff state to S_EMPTY
+      rel lock
+    }
+  */
+  int* exit_now = (int*)exit_nowV;
+  int ms_to_sleep_if_empty = 1;
+  while (1) {
+
+    if (*exit_now != 0) break;
+
+    spinLock_acquire(&g_spinLock);
+
+    /* Find the oldest filled buffer, if any. */
+    uint64_t oldest_seqNo = ~0ULL; /* infinity */
+    int      oldest_ix    = -1;
+    int      i;
+    for (i = 0; i < N_UNW_THR_BUFFERS; i++) {
+      UnwinderThreadBuffer* buff = g_buffers[i];
+      if (buff->state != S_FULL) continue;
+      if (buff->seqNo < oldest_seqNo) {
+        oldest_seqNo = buff->seqNo;
+        oldest_ix    = i;
+      }
+    }
+    if (oldest_ix == -1) {
+      /* We didn't find a full buffer.  Snooze and try again later. */
+      MOZ_ASSERT(oldest_seqNo == ~0ULL);
+      spinLock_release(&g_spinLock);
+      if (ms_to_sleep_if_empty > 100 && LOGLEVEL >= 2) {
+        LOGF("BPUnw: unwinder: sleep for %d ms", ms_to_sleep_if_empty);
+      }
+      sleep_ms(ms_to_sleep_if_empty);
+      if (ms_to_sleep_if_empty < 20) {
+        ms_to_sleep_if_empty += 2;
+      } else {
+        ms_to_sleep_if_empty = (15 * ms_to_sleep_if_empty) / 10;
+        if (ms_to_sleep_if_empty > 1000)
+          ms_to_sleep_if_empty = 1000;
+      }
+      continue;
+    }
+
+    /* We found a full a buffer.  Mark it as 'ours' and drop the
+       lock; then we can safely throw breakpad at it. */
+    UnwinderThreadBuffer* buff = g_buffers[oldest_ix];
+    MOZ_ASSERT(buff->state == S_FULL);
+    buff->state = S_EMPTYING;
+    spinLock_release(&g_spinLock);
+
+    /* unwind .. in which we can do anything we like, since any
+       resource stalls that we may encounter (eg malloc locks) in
+       competition with signal handler instances, will be short
+       lived since the signal handler is guaranteed nonblocking. */
+    if (0) LOGF("BPUnw: unwinder: seqNo %llu: emptying buf %d\n",
+                (unsigned long long int)oldest_seqNo, oldest_ix);
+
+    /* Copy ProfileEntries presented to us by the sampling thread.
+       Most of them are copied verbatim into |buff->aProfile|,
+       except for 'hint' tags, which direct us to do something
+       different. */
+
+    /* Need to lock |aProfile| so nobody tries to copy out entries
+       whilst we are putting them in. */
+    buff->aProfile->GetMutex()->Lock();
+
+    /* The buff is a sequence of ProfileEntries (ents).  It has
+       this grammar:
+
+       | --pre-tags-- | (h 'P' .. h 'Q')* | --post-tags-- |
+                        ^               ^
+                        ix_first_hP     ix_last_hQ
+
+       Each (h 'P' .. h 'Q') subsequence represents one pseudostack
+       entry.  These, if present, are in the order
+       outermost-frame-first, and that is the order that they should
+       be copied into aProfile.  The --pre-tags-- and --post-tags--
+       are to be copied into the aProfile verbatim, except that they
+       may contain the hints "h 'F'" for a flush and "h 'N'" to
+       indicate that a native unwind is also required, and must be
+       interleaved with the pseudostack entries.
+
+       The hint tags that bound each pseudostack entry, "h 'P'" and "h
+       'Q'", are not to be copied into the aProfile -- they are
+       present only to make parsing easy here.  Also, the pseudostack
+       entries may contain an "'S' (void*)" entry, which is the stack
+       pointer value for that entry, and these are also not to be
+       copied.
+    */
+    /* The first thing to do is therefore to find the pseudostack
+       entries, if any, and to find out also whether a native unwind
+       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);
+      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;
+      }
+    }
+
+    if (0) LOGF("BPUnw: ix_first_hP %llu  ix_last_hQ %llu  need_native_unw %llu",
+                (unsigned long long int)ix_first_hP,
+                (unsigned long long int)ix_last_hQ,
+                (unsigned long long int)need_native_unw);
+
+    /* There are four possibilities: native-only, pseudostack-only,
+       combined (both), and neither.  We handle all four cases. */
+
+    MOZ_ASSERT( (ix_first_hP == infUW && ix_last_hQ == infUW) ||
+                (ix_first_hP != infUW && ix_last_hQ != infUW) );
+    bool have_P = ix_first_hP != infUW;
+    if (have_P) {
+      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);
+        // 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);
+        // 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)") );
+          for (unsigned int i = 0; i < nPairs; i++) {
+            buff->aProfile
+                ->addTag( ProfileEntry2('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
+        if (ent.is_ent_hint() || ent.is_ent('S')) { continue; }
+        // and copy everything else
+        buff->aProfile->addTag( ent );
+      }
+    }
+    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);
+        // We need to insert a sample-start tag before the first frame
+        if (k == ix_first_hP) {
+          buff->aProfile->addTag( ProfileEntry2('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 );
+      }
+    }
+    else /* Combined case */
+    if (need_native_unw && have_P)
+    {
+      /* We need to get a native stacktrace and merge it with the
+         pseudostack entries.  This isn't too simple.  First, copy all
+         the tags up to the start of the pseudostack tags.  Then
+         generate a combined set of tags by native unwind and
+         pseudostack.  Then, copy all the stuff after the pseudostack
+         tags. */
+      MOZ_ASSERT(buff->haveNativeInfo);
+
+      // 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);
+        // 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)") );
+      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) {
+          // Assert that next_P points at the start of an P entry
+          MOZ_ASSERT(utb_get_profent(buff, next_P).is_ent_hint('P'));
+        }
+        if (next_N >= n_pairs && next_P > ix_last_hQ) {
+          // both stacks empty
+          break;
+        }
+        /* Decide which entry to use next:
+           If N is empty, must use P, and vice versa
+           else
+           If the last was P and current P has zero SP, use P
+           else
+           we assume that both P and N have valid SP, in which case
+              use the one with the larger value
+        */
+        bool use_P = true;
+        if (next_N >= n_pairs) {
+          // N empty, use P
+          use_P = true;
+          if (0) LOG("  P  <=  no remaining N entries");
+        }
+        else if (next_P > ix_last_hQ) {
+          // P empty, use N
+          use_P = false;
+          if (0) LOG("  N  <=  no remaining P entries");
+        }
+        else {
+          // We have at least one N and one P entry available.
+          // 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);
+            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++;
+          }
+          if (last_was_P && sp_cur_P == 0) {
+            if (0) LOG("  P  <=  last_was_P && sp_cur_P == 0");
+            use_P = true;
+          } else {
+            u_int64_t sp_cur_N = pairs[next_N].sp;
+            use_P = (sp_cur_P > sp_cur_N);
+            if (0) LOGF("  %s  <=  sps P %p N %p",
+                        use_P ? "P" : "N", (void*)(intptr_t)sp_cur_P, 
+                                           (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);
+            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)) );
+          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);
+        // 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 );
+      }
+
+      // free native unwind info
+      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);
+      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)") );
+        for (unsigned int i = 0; i < nPairs; i++) {
+          buff->aProfile
+              ->addTag( ProfileEntry2('l', reinterpret_cast<void*>(pairs[i].pc)) );
+        }
+        if (pairs)
+          free(pairs);
+      } else {
+        /* Copy in verbatim */
+        buff->aProfile->addTag( ent );
+      }
+    }
+#endif
+
+    buff->aProfile->GetMutex()->Unlock();
+
+    /* And .. we're done.  Mark the buffer as empty so it can be
+       reused.  First though, unmap any of the entsPages that got
+       mapped during filling. */
+    for (i = 0; i < N_PROF_ENT_PAGES; i++) {
+      if (buff->entsPages[i] == ProfEntsPage_INVALID)
+        continue;
+      munmap_ProfEntsPage(buff->entsPages[i]);
+      buff->entsPages[i] = ProfEntsPage_INVALID;
+    }
+
+    (void)VALGRIND_MAKE_MEM_UNDEFINED(&buff->stackImg[0], N_STACK_BYTES);
+    spinLock_acquire(&g_spinLock);
+    MOZ_ASSERT(buff->state == S_EMPTYING);
+    buff->state = S_EMPTY;
+    spinLock_release(&g_spinLock);
+    ms_to_sleep_if_empty = 1;
+  }
+  return NULL;
+}
+
+
+////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////
+
+/* After this point, we have some classes that interface with
+   breakpad, that allow us to pass in a Buffer and get an unwind of
+   it. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+#include <fstream>
+#include <sstream>
+
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "local_debug_info_symbolizer.h"
+#include "processor/stackwalker_amd64.h"
+#include "processor/stackwalker_arm.h"
+#include "processor/stackwalker_x86.h"
+#include "processor/logging.h"
+#include "common/linux/dump_symbols.h"
+
+#include "google_breakpad/processor/memory_region.h"
+#include "google_breakpad/processor/code_modules.h"
+
+google_breakpad::MemoryRegion* foo = NULL;
+
+using std::string;
+
+///////////////////////////////////////////////////////////////////
+/* Implement MemoryRegion, so that it hauls stack image data out of
+   the stack top snapshots that the signal handler has so carefully
+   snarfed. */
+
+// BEGIN: DERIVED FROM src/processor/stackwalker_selftest.cc
+//
+class BufferMemoryRegion : public google_breakpad::MemoryRegion {
+ public:
+  // We just keep hold of the Buffer* we're given, but make no attempt
+  // to take allocation-ownership of it.
+  BufferMemoryRegion(UnwinderThreadBuffer* buff) : buff_(buff) { }
+  ~BufferMemoryRegion() { }
+
+  u_int64_t GetBase() const { return (uintptr_t)buff_->stackImgAddr; }
+  u_int32_t GetSize() const { return (uintptr_t)buff_->stackImgUsed; }
+
+  bool GetMemoryAtAddress(u_int64_t address, u_int8_t*  value) const {
+      return GetMemoryAtAddressInternal(address, value); }
+  bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const {
+      return GetMemoryAtAddressInternal(address, value); }
+  bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const {
+      return GetMemoryAtAddressInternal(address, value); }
+  bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const {
+      return GetMemoryAtAddressInternal(address, value); }
+
+ private:
+  template<typename T> bool GetMemoryAtAddressInternal (
+                               u_int64_t address, T* value) const {
+    /* Range check .. */
+    if ( buff_->stackImgUsed >= sizeof(T)
+         && ((uintptr_t)address) >= ((uintptr_t)buff_->stackImgAddr)
+         && ((uintptr_t)address) <= ((uintptr_t)buff_->stackImgAddr)
+                                     + buff_->stackImgUsed
+                                     - sizeof(T) ) {
+      uintptr_t offset = (uintptr_t)address - (uintptr_t)buff_->stackImgAddr;
+      if (0) LOGF("GMAA %llx ok", (unsigned long long int)address);
+      *value = *reinterpret_cast<const T*>(&buff_->stackImg[offset]);
+      return true;
+    } else {
+      if (0) LOGF("GMAA %llx failed", (unsigned long long int)address);
+      return false;
+    }
+  }
+
+  // where this all comes from
+  UnwinderThreadBuffer* buff_;
+};
+//
+// END: DERIVED FROM src/processor/stackwalker_selftest.cc
+
+
+///////////////////////////////////////////////////////////////////
+/* Implement MyCodeModule and MyCodeModules, so they pull the relevant
+   information about which modules are loaded where out of
+   /proc/self/maps. */
+
+class MyCodeModule : public google_breakpad::CodeModule {
+public:
+  MyCodeModule(u_int64_t x_start, u_int64_t x_len, string filename)
+    : x_start_(x_start), x_len_(x_len), filename_(filename) {
+    MOZ_ASSERT(x_len > 0);
+  }
+
+  ~MyCodeModule() {}
+
+  // The base address of this code module as it was loaded by the process.
+  // (u_int64_t)-1 on error.
+  u_int64_t base_address() const { return x_start_; }
+
+  // The size of the code module.  0 on error.
+  u_int64_t size() const { return x_len_; }
+
+  // The path or file name that the code module was loaded from.  Empty on
+  // error.
+  string code_file() const { return filename_; }
+
+  // An identifying string used to discriminate between multiple versions and
+  // builds of the same code module.  This may contain a uuid, timestamp,
+  // version number, or any combination of this or other information, in an
+  // implementation-defined format.  Empty on error.
+  string code_identifier() const { MOZ_CRASH(); return ""; }
+
+  // The filename containing debugging information associated with the code
+  // module.  If debugging information is stored in a file separate from the
+  // code module itself (as is the case when .pdb or .dSYM files are used),
+  // this will be different from code_file.  If debugging information is
+  // stored in the code module itself (possibly prior to stripping), this
+  // will be the same as code_file.  Empty on error.
+  string debug_file() const { MOZ_CRASH(); return ""; }
+
+  // An identifying string similar to code_identifier, but identifies a
+  // specific version and build of the associated debug file.  This may be
+  // the same as code_identifier when the debug_file and code_file are
+  // identical or when the same identifier is used to identify distinct
+  // debug and code files.
+  string debug_identifier() const { MOZ_CRASH(); return ""; }
+
+  // A human-readable representation of the code module's version.  Empty on
+  // error.
+  string version() const { MOZ_CRASH(); return ""; }
+
+  // Creates a new copy of this CodeModule object, which the caller takes
+  // ownership of.  The new CodeModule may be of a different concrete class
+  // than the CodeModule being copied, but will behave identically to the
+  // copied CodeModule as far as the CodeModule interface is concerned.
+  const CodeModule* Copy() const { MOZ_CRASH(); return NULL; }
+
+ private:
+  // record info for a file backed executable mapping
+  // snarfed from /proc/self/maps
+  u_int64_t x_start_;
+  u_int64_t x_len_;    // may not be zero
+  string    filename_; // of the mapped file
+};
+
+
+/* Find out, in a platform-dependent way, where the code modules got
+   mapped in the process' virtual address space, and add them to
+   |mods_|. */
+static void read_procmaps(std::vector<MyCodeModule*>& mods_)
+{
+  MOZ_ASSERT(mods_.size() == 0);
+
+#if defined(SPS_OS_linux) || defined(SPS_OS_android)
+  // read /proc/self/maps and create a vector of CodeModule*
+  FILE* f = fopen("/proc/self/maps", "r");
+  MOZ_ASSERT(f);
+  while (!feof(f)) {
+    unsigned long long int start = 0;
+    unsigned long long int end   = 0;
+    char rr = ' ', ww = ' ', xx = ' ', pp = ' ';
+    unsigned long long int offset = 0, inode = 0;
+    unsigned int devMaj = 0, devMin = 0;
+    int nItems = fscanf(f, "%llx-%llx %c%c%c%c %llx %x:%x %llu",
+                        &start, &end, &rr, &ww, &xx, &pp,
+                        &offset, &devMaj, &devMin, &inode);
+    if (nItems == EOF && feof(f)) break;
+    MOZ_ASSERT(nItems == 10);
+    MOZ_ASSERT(start < end);
+    // read the associated file name, if it is present
+    int ch;
+    // find '/' or EOL
+    while (1) {
+      ch = fgetc(f);
+      MOZ_ASSERT(ch != EOF);
+      if (ch == '\n' || ch == '/') break;
+    }
+    string fname("");
+    if (ch == '/') {
+      fname += (char)ch;
+      while (1) {
+        ch = fgetc(f);
+        MOZ_ASSERT(ch != EOF);
+        if (ch == '\n') break;
+        fname += (char)ch;
+      }
+    }
+    MOZ_ASSERT(ch == '\n');
+    if (0) LOGF("SEG %llx %llx %c %c %c %c %s",
+                start, end, rr, ww, xx, pp, fname.c_str() );
+    if (xx == 'x' && fname != "") {
+      MyCodeModule* cm = new MyCodeModule( start, end-start, fname );
+      mods_.push_back(cm);
+    }
+  }
+  fclose(f);
+
+#elif defined(SPS_OS_darwin)
+
+# if defined(SPS_PLAT_amd64_darwin)
+  typedef mach_header_64 breakpad_mach_header;
+  typedef segment_command_64 breakpad_mach_segment_command;
+  const int LC_SEGMENT_XX = LC_SEGMENT_64;
+# else // SPS_PLAT_x86_darwin
+  typedef mach_header breakpad_mach_header;
+  typedef segment_command breakpad_mach_segment_command;
+  const int LC_SEGMENT_XX = LC_SEGMENT;
+# endif
+
+  uint32_t n_images = _dyld_image_count();
+  for (uint32_t ix = 0; ix < n_images; ix++) {
+
+    MyCodeModule* cm = NULL;
+    unsigned long slide = _dyld_get_image_vmaddr_slide(ix);
+    const char* name = _dyld_get_image_name(ix);
+    const breakpad_mach_header* header
+      = (breakpad_mach_header*)_dyld_get_image_header(ix);
+    if (!header)
+      continue;
+
+    const struct load_command *cmd =
+      reinterpret_cast<const struct load_command *>(header + 1);
+
+    /* Look through the MachO headers to find out the module's stated
+       VMA, so we can add it to the slide to find its actual VMA.
+       Copied from MinidumpGenerator::WriteModuleStream
+       src/client/mac/handler/minidump_generator.cc. */
+    for (unsigned int i = 0; cmd && (i < header->ncmds); i++) {
+      if (cmd->cmd == LC_SEGMENT_XX) {
+
+        const breakpad_mach_segment_command *seg =
+          reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
+
+        if (!strcmp(seg->segname, "__TEXT")) {
+          cm = new MyCodeModule( seg->vmaddr + slide,
+                                 seg->vmsize, string(name) );
+          break;
+        }
+      }
+      cmd = reinterpret_cast<struct load_command*>((char *)cmd + cmd->cmdsize);
+    }
+    if (cm) {
+      mods_.push_back(cm);
+      if (0) LOGF("SEG %llx %llx %s",
+                  cm->base_address(), cm->base_address() + cm->size(),
+                  cm->code_file().c_str());
+    }
+  }
+
+#else
+# error "Unknown platform"
+#endif
+
+  if (0) LOGF("got %d mappings\n", (int)mods_.size());
+}
+
+
+class MyCodeModules : public google_breakpad::CodeModules
+{
+ public:
+  MyCodeModules() {
+    read_procmaps(mods_);
+  }
+
+  ~MyCodeModules() {
+    std::vector<MyCodeModule*>::const_iterator it;
+    for (it = mods_.begin(); it < mods_.end(); it++) {
+      MyCodeModule* cm = *it;
+      delete cm;
+    }
+  }
+
+ private:
+  std::vector<MyCodeModule*> mods_;
+
+  unsigned int module_count() const { MOZ_CRASH(); return 1; }
+
+  const google_breakpad::CodeModule*
+                GetModuleForAddress(u_int64_t address) const
+  {
+    if (0) printf("GMFA %llx\n", (unsigned long long int)address);
+    std::vector<MyCodeModule*>::const_iterator it;
+    for (it = mods_.begin(); it < mods_.end(); it++) {
+       MyCodeModule* cm = *it;
+       if (0) printf("considering %p  %llx +%llx\n",
+                     (void*)cm, (unsigned long long int)cm->base_address(),
+                                (unsigned long long int)cm->size());
+       if (cm->base_address() <= address
+           && address < cm->base_address() + cm->size())
+          return cm;
+    }
+    return NULL;
+  }
+
+  const google_breakpad::CodeModule* GetMainModule() const {
+    MOZ_CRASH(); return NULL; return NULL;
+  }
+
+  const google_breakpad::CodeModule* GetModuleAtSequence(
+                unsigned int sequence) const {
+    MOZ_CRASH(); return NULL;
+  }
+
+  const google_breakpad::CodeModule* GetModuleAtIndex(unsigned int index) const {
+    MOZ_CRASH(); return NULL;
+  }
+
+  const CodeModules* Copy() const {
+    MOZ_CRASH(); return NULL;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+/* Top level interface to breakpad.  Given a Buffer* as carefully
+   acquired by the signal handler and later handed to this thread,
+   unwind it.
+
+   The first time in, read /proc/self/maps.  TODO: what about if it
+   changes as we go along?
+
+   Dump the result (PC, SP) pairs in a malloc-allocated array of
+   PCandSPs, and return that and its length to the caller.  Caller is
+   responsible for deallocating it.
+
+   The first pair is for the outermost frame, the last for the
+   innermost frame.
+*/
+
+MyCodeModules* sModules = NULL;
+google_breakpad::LocalDebugInfoSymbolizer* sSymbolizer = NULL;
+
+void do_breakpad_unwind_Buffer(/*OUT*/PCandSP** pairs,
+                               /*OUT*/unsigned int* nPairs,
+                               UnwinderThreadBuffer* buff,
+                               int buffNo /* for debug printing only */)
+{
+# if defined(SPS_ARCH_amd64)
+  MDRawContextAMD64* context = new MDRawContextAMD64();
+  memset(context, 0, sizeof(*context));
+
+  context->rip = buff->regs.rip;
+  context->rbp = buff->regs.rbp;
+  context->rsp = buff->regs.rsp;
+
+  if (0) {
+    LOGF("Initial RIP = 0x%llx", (unsigned long long int)context->rip);
+    LOGF("Initial RSP = 0x%llx", (unsigned long long int)context->rsp);
+    LOGF("Initial RBP = 0x%llx", (unsigned long long int)context->rbp);
+  }
+
+# elif defined(SPS_ARCH_arm)
+  MDRawContextARM* context = new MDRawContextARM();
+  memset(context, 0, sizeof(*context));
+
+  context->iregs[7]                     = buff->regs.r7;
+  context->iregs[12]                    = buff->regs.r12;
+  context->iregs[MD_CONTEXT_ARM_REG_PC] = buff->regs.r15;
+  context->iregs[MD_CONTEXT_ARM_REG_LR] = buff->regs.r14;
+  context->iregs[MD_CONTEXT_ARM_REG_SP] = buff->regs.r13;
+  context->iregs[MD_CONTEXT_ARM_REG_FP] = buff->regs.r11;
+
+  if (0) {
+    LOGF("Initial R15 = 0x%x",
+         context->iregs[MD_CONTEXT_ARM_REG_PC]);
+    LOGF("Initial R13 = 0x%x",
+         context->iregs[MD_CONTEXT_ARM_REG_SP]);
+  }
+
+# elif defined(SPS_ARCH_x86)
+  MDRawContextX86* context = new MDRawContextX86();
+  memset(context, 0, sizeof(*context));
+
+  context->eip = buff->regs.eip;
+  context->ebp = buff->regs.ebp;
+  context->esp = buff->regs.esp;
+
+  if (0) {
+    LOGF("Initial EIP = 0x%x", context->eip);
+    LOGF("Initial ESP = 0x%x", context->esp);
+    LOGF("Initial EBP = 0x%x", context->ebp);
+  }
+
+# else
+#   error "Unknown plat"
+# endif
+
+  BufferMemoryRegion* memory = new BufferMemoryRegion(buff);
+
+  if (!sModules) {
+     sModules = new MyCodeModules();
+  }
+
+  if (!sSymbolizer) {
+    /* Make up a list of places where the debug objects might be. */
+    std::vector<std::string> debug_dirs;
+#   if defined(SPS_OS_linux)
+    debug_dirs.push_back("/usr/lib/debug/lib");
+    debug_dirs.push_back("/usr/lib/debug/usr/lib");
+    debug_dirs.push_back("/usr/lib/debug/lib/x86_64-linux-gnu");
+    debug_dirs.push_back("/usr/lib/debug/usr/lib/x86_64-linux-gnu");
+#   elif defined(SPS_OS_android)
+    debug_dirs.push_back("/sdcard/symbols/system/lib");
+    debug_dirs.push_back("/sdcard/symbols/system/bin");
+#   elif defined(SPS_OS_darwin)
+    /* Nothing */
+#   else
+#     error "Unknown plat"
+#   endif
+    sSymbolizer = new google_breakpad::LocalDebugInfoSymbolizer(debug_dirs);
+  }
+
+# if defined(SPS_ARCH_amd64)
+  google_breakpad::StackwalkerAMD64* sw
+   = new google_breakpad::StackwalkerAMD64(NULL, context,
+                                           memory, sModules,
+                                           sSymbolizer);
+# elif defined(SPS_ARCH_arm)
+  google_breakpad::StackwalkerARM* sw
+   = new google_breakpad::StackwalkerARM(NULL, context,
+                                         -1/*FP reg*/,
+                                         memory, sModules,
+                                         sSymbolizer);
+# elif defined(SPS_ARCH_x86)
+  google_breakpad::StackwalkerX86* sw
+   = new google_breakpad::StackwalkerX86(NULL, context,
+                                         memory, sModules,
+                                         sSymbolizer);
+# else
+#   error "Unknown plat"
+# endif
+
+  google_breakpad::CallStack* stack = new google_breakpad::CallStack();
+
+  std::vector<const google_breakpad::CodeModule*>* modules_without_symbols
+    = new std::vector<const google_breakpad::CodeModule*>();
+  bool b = sw->Walk(stack, modules_without_symbols);
+  (void)b;
+  delete modules_without_symbols;
+
+  unsigned int n_frames = stack->frames()->size();
+  unsigned int n_frames_good = 0;
+
+  *pairs  = (PCandSP*)malloc(n_frames * sizeof(PCandSP));
+  *nPairs = n_frames;
+  if (*pairs == NULL) {
+    *nPairs = 0;
+    return;
+  }
+
+  if (n_frames > 0) {
+    for (unsigned int frame_index = 0; 
+         frame_index < n_frames; ++frame_index) {
+      google_breakpad::StackFrame *frame = stack->frames()->at(frame_index);
+
+      if (frame->trust == google_breakpad::StackFrame::FRAME_TRUST_CFI
+          || frame->trust == google_breakpad::StackFrame::FRAME_TRUST_CONTEXT) {
+        n_frames_good++;
+      }
+
+#     if defined(SPS_ARCH_amd64)
+      google_breakpad::StackFrameAMD64* frame_amd64
+        = reinterpret_cast<google_breakpad::StackFrameAMD64*>(frame);
+      if (LOGLEVEL >= 4) {
+        LOGF("frame %d   rip=0x%016llx rsp=0x%016llx    %s", 
+             frame_index,
+             (unsigned long long int)frame_amd64->context.rip, 
+             (unsigned long long int)frame_amd64->context.rsp, 
+             frame_amd64->trust_description().c_str());
+      }
+      (*pairs)[n_frames-1-frame_index].pc = frame_amd64->context.rip;
+      (*pairs)[n_frames-1-frame_index].sp = frame_amd64->context.rsp;
+
+#     elif defined(SPS_ARCH_arm)
+      google_breakpad::StackFrameARM* frame_arm
+        = reinterpret_cast<google_breakpad::StackFrameARM*>(frame);
+      if (LOGLEVEL >= 4) {
+        LOGF("frame %d   0x%08x   %s",
+             frame_index,
+             frame_arm->context.iregs[MD_CONTEXT_ARM_REG_PC],
+             frame_arm->trust_description().c_str());
+      }
+      (*pairs)[n_frames-1-frame_index].pc
+        = frame_arm->context.iregs[MD_CONTEXT_ARM_REG_PC];
+      (*pairs)[n_frames-1-frame_index].sp
+        = frame_arm->context.iregs[MD_CONTEXT_ARM_REG_SP];
+
+#     elif defined(SPS_ARCH_x86)
+      google_breakpad::StackFrameX86* frame_x86
+        = reinterpret_cast<google_breakpad::StackFrameX86*>(frame);
+      if (LOGLEVEL >= 4) {
+        LOGF("frame %d   eip=0x%08x rsp=0x%08x    %s", 
+             frame_index,
+             frame_x86->context.eip, frame_x86->context.esp, 
+             frame_x86->trust_description().c_str());
+      }
+      (*pairs)[n_frames-1-frame_index].pc = frame_x86->context.eip;
+      (*pairs)[n_frames-1-frame_index].sp = frame_x86->context.esp;
+
+#     else
+#       error "Unknown plat"
+#     endif
+    }
+  }
+
+  if (LOGLEVEL >= 3) {
+    LOGF("BPUnw: unwinder: seqNo %llu, buf %d: got %u frames "
+         "(%u trustworthy)", 
+         (unsigned long long int)buff->seqNo, buffNo, n_frames, n_frames_good);
+  }
+
+  if (LOGLEVEL >= 2) {
+    if (0 == (g_stats_totalSamples % 1000))
+      LOGF("BPUnw: %llu total samples, %llu failed due to buffer unavail",
+           (unsigned long long int)g_stats_totalSamples,
+           (unsigned long long int)g_stats_noBuffAvail);
+  }
+
+  delete stack;
+  delete sw;
+  delete memory;
+  delete context;
+}
+
+#endif /* defined(SPS_OS_windows) */
new file mode 100644
--- /dev/null
+++ b/tools/profiler/UnwinderThread2.h
@@ -0,0 +1,60 @@
+/* -*- 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 MOZ_UNWINDER_THREAD_2_H
+#define MOZ_UNWINDER_THREAD_2_H
+
+#include "sps_sampler.h"
+#include "ProfileEntry2.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
+// 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);
+
+// 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
+// before they are allowed to call utb__acquire_empty_buffer or
+// utb__release_full_buffer.
+void uwt__register_thread_for_profiling(void* stackTop);
+
+// RUNS IN SIGHANDLER CONTEXT
+// Called in the sampled thread (signal) context.  Get an empty buffer
+// into which ProfileEntries can be put.  It may return NULL if no
+// empty buffers can be found, which will be the case if the unwinder
+// thread(s) have fallen behind for some reason.  In this case the
+// sampled thread must simply give up and return from the signal handler
+// immediately, else it risks deadlock.
+UnwinderThreadBuffer* uwt__acquire_empty_buffer();
+
+// RUNS IN SIGHANDLER CONTEXT
+// 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,
+                              UnwinderThreadBuffer* utb,
+                              void* /* ucontext_t*, really */ ucV);
+
+#endif /* ndef MOZ_UNWINDER_THREAD_2_H */
new file mode 100644
--- /dev/null
+++ b/tools/profiler/local_debug_info_symbolizer.cc
@@ -0,0 +1,160 @@
+
+#include "PlatformMacros.h"
+
+#if !defined(SPS_OS_windows)
+# include "common/module.h"
+# include "processor/cfi_frame_info.h"
+#endif
+#include "google_breakpad/processor/code_module.h"
+#include "google_breakpad/processor/code_modules.h"
+#include "google_breakpad/processor/stack_frame.h"
+#include "processor/logging.h"
+#include "common/scoped_ptr.h"
+
+#if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
+    || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
+# include "common/linux/dump_symbols.h"
+#elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin)
+# include "shim_mac_dump_syms.h"
+#elif defined(SPS_OS_windows)
+  /* This is all stubbed out anyway, so don't do anything. */
+#else
+# error "Unknown platform"
+#endif
+
+#include "platform.h"
+#include "local_debug_info_symbolizer.h"
+
+namespace google_breakpad {
+
+LocalDebugInfoSymbolizer::~LocalDebugInfoSymbolizer() {
+# if !defined(SPS_OS_windows)
+  for (SymbolMap::iterator it = symbols_.begin();
+       it != symbols_.end();
+       ++it) {
+    delete it->second;
+  }
+# endif
+}
+
+StackFrameSymbolizer::SymbolizerResult
+LocalDebugInfoSymbolizer::FillSourceLineInfo(const CodeModules* modules,
+                                             const SystemInfo* system_info,
+                                             StackFrame* frame) {
+  if (!modules) {
+    return kError;
+  }
+  const CodeModule* module = modules->GetModuleForAddress(frame->instruction);
+  if (!module) {
+    return kError;
+  }
+  frame->module = module;
+
+# if !defined(SPS_OS_windows)
+  Module* debug_info_module = NULL;
+  SymbolMap::const_iterator it = symbols_.find(module->code_file());
+  if (it == symbols_.end()) {
+    if (no_symbol_modules_.find(module->code_file()) !=
+        no_symbol_modules_.end()) {
+      return kNoError;
+    }
+    LOG("BPUnw:");
+    LOGF("BPUnw: ReadSymbolData: BEGIN   %s", module->code_file().c_str());
+    if (!ReadSymbolData(module->code_file(),
+                        debug_dirs_,
+                        ONLY_CFI,
+                        &debug_info_module)) {
+      BPLOG(ERROR) << "ReadSymbolData failed for " << module->code_file();
+      LOGF("BPUnw: ReadSymbolData: FAIL    %s", module->code_file().c_str());
+      if (debug_info_module)
+        delete debug_info_module;
+      no_symbol_modules_.insert(module->code_file());
+      return kNoError;
+    }
+
+    LOGF("BPUnw: ReadSymbolData: SUCCESS %s", module->code_file().c_str());
+    symbols_[module->code_file()] = debug_info_module;
+  } else {
+    debug_info_module = it->second;
+  }
+
+  u_int64_t address = frame->instruction - frame->module->base_address();
+  Module::Function* function =
+      debug_info_module->FindFunctionByAddress(address);
+  if (function) {
+    frame->function_name = function->name;
+    //TODO: line info: function->lines
+  } else {
+    Module::Extern* ex = debug_info_module->FindExternByAddress(address);
+    if (ex) {
+      frame->function_name = ex->name;
+    }
+  }
+# endif /* !defined(SPS_OS_windows) */
+  return kNoError;
+}
+
+
+WindowsFrameInfo* LocalDebugInfoSymbolizer::FindWindowsFrameInfo(
+    const StackFrame* frame) {
+  // Not currently implemented, would require PDBSourceLineWriter to
+  // implement an API to return symbol data.
+  return NULL;
+}
+
+#if !defined(SPS_OS_windows)
+// Taken wholesale from source_line_resolver_base.cc
+bool ParseCFIRuleSet(const string& rule_set, CFIFrameInfo* frame_info) {
+  CFIFrameInfoParseHandler handler(frame_info);
+  CFIRuleParser parser(&handler);
+  return parser.Parse(rule_set);
+}
+
+static void ConvertCFI(const UniqueString* name, const Module::Expr& rule,
+                       CFIFrameInfo* frame_info) {
+  if (name == ustr__ZDcfa()) frame_info->SetCFARule(rule);
+  else if (name == ustr__ZDra()) frame_info->SetRARule(rule);
+  else frame_info->SetRegisterRule(name, rule);
+}
+
+
+static void ConvertCFI(const Module::RuleMap& rule_map,
+                       CFIFrameInfo* frame_info) {
+  for (Module::RuleMap::const_iterator it = rule_map.begin();
+       it != rule_map.end(); ++it) {
+    ConvertCFI(it->first, it->second, frame_info);
+  }
+}
+#endif
+
+CFIFrameInfo* LocalDebugInfoSymbolizer::FindCFIFrameInfo(
+    const StackFrame* frame) {
+#if defined(SPS_OS_windows)
+  return NULL;
+#else
+  if (!frame || !frame->module) return NULL;
+
+  SymbolMap::const_iterator it = symbols_.find(frame->module->code_file());
+  if (it == symbols_.end()) return NULL;
+
+  Module* module = it->second;
+  u_int64_t address = frame->instruction - frame->module->base_address();
+  Module::StackFrameEntry* entry =
+      module->FindStackFrameEntryByAddress(address);
+  if (!entry)
+    return NULL;
+
+  //TODO: can we cache this data per-address? does that make sense?
+  scoped_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
+  ConvertCFI(entry->initial_rules, rules.get());
+  for (Module::RuleChangeMap::const_iterator delta_it =
+           entry->rule_changes.begin();
+       delta_it != entry->rule_changes.end() && delta_it->first < address;
+       ++delta_it) {
+    ConvertCFI(delta_it->second, rules.get());
+  }
+  return rules.release();
+#endif /* defined(SPS_OS_windows) */
+}
+
+}  // namespace google_breakpad
new file mode 100644
--- /dev/null
+++ b/tools/profiler/local_debug_info_symbolizer.h
@@ -0,0 +1,40 @@
+#ifndef PROCESSOR_LOCAL_DEBUG_INFO_SYMBOLIZER_H_
+#define PROCESSOR_LOCAL_DEBUG_INFO_SYMBOLIZER_H_
+
+#include "google_breakpad/processor/stack_frame_symbolizer.h"
+
+#include <map>
+#include <vector>
+
+namespace google_breakpad {
+
+class Module;
+
+class LocalDebugInfoSymbolizer : public StackFrameSymbolizer {
+ public:
+  using StackFrameSymbolizer::SymbolizerResult;
+  LocalDebugInfoSymbolizer(const std::vector<string>& debug_dirs) :
+      StackFrameSymbolizer(NULL, NULL),
+      debug_dirs_(debug_dirs) {}
+  virtual ~LocalDebugInfoSymbolizer();
+
+  virtual SymbolizerResult FillSourceLineInfo(const CodeModules* modules,
+                                              const SystemInfo* system_info,
+                                              StackFrame* stack_frame);
+
+  virtual WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame);
+
+  virtual CFIFrameInfo* FindCFIFrameInfo(const StackFrame* frame);
+
+  // Lie to the stackwalker to short-circuit stack-scanning heuristics.
+  virtual bool HasImplementation() { return false; }
+
+ private:
+  typedef std::map<string, Module*> SymbolMap;
+  SymbolMap symbols_;
+  std::vector<string> debug_dirs_;
+};
+
+}  // namespace google_breakpad
+
+#endif  // PROCESSOR_LOCAL_DEBUG_INFO_SYMBOLIZER_H_
--- a/tools/profiler/nsProfiler.cpp
+++ b/tools/profiler/nsProfiler.cpp
@@ -55,21 +55,21 @@ nsProfiler::Observe(nsISupports *aSubjec
                     const PRUnichar *aData)
 {
   if (strcmp(aTopic, "chrome-document-global-created") == 0) {
     nsCOMPtr<nsIInterfaceRequestor> requestor = do_QueryInterface(aSubject);
     nsCOMPtr<nsIWebNavigation> parentWebNav = do_GetInterface(requestor);
     nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(parentWebNav);
     if (loadContext && loadContext->UsePrivateBrowsing() && !mLockedForPrivateBrowsing) {
       mLockedForPrivateBrowsing = true;
-      mozilla_sampler_lock();
+      SAMPLER_LOCK();
     }
   } else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
     mLockedForPrivateBrowsing = false;
-    mozilla_sampler_unlock();
+    SAMPLER_UNLOCK();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::StartProfiler(uint32_t aEntries, uint32_t aInterval,
                           const char** aFeatures, uint32_t aFeatureCount)
 {
--- a/tools/profiler/platform-linux.cc
+++ b/tools/profiler/platform-linux.cc
@@ -219,17 +219,17 @@ Sampler::Sampler(int interval, bool prof
 
 Sampler::~Sampler() {
   ASSERT(!data_->signal_sender_launched_);
   delete data_;
 }
 
 
 void Sampler::Start() {
-  LOG("Sampler Started");
+  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;
@@ -286,18 +286,18 @@ void Sampler::Stop() {
   sActiveSampler = NULL;
 }
 
 #ifdef ANDROID
 static struct sigaction old_sigstart_signal_handler;
 const int SIGSTART = SIGUSR1;
 
 static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
-  mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
-                        PROFILE_DEFAULT_FEATURES, PROFILE_DEFAULT_FEATURE_COUNT);
+  SAMPLER_START(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
+                PROFILE_DEFAULT_FEATURES, PROFILE_DEFAULT_FEATURE_COUNT);
 }
 
 void OS::RegisterStartHandler()
 {
   LOG("Registering start signal");
   struct sigaction sa;
   sa.sa_sigaction = StartSignalHandler;
   sigemptyset(&sa.sa_mask);
--- a/tools/profiler/platform-macos.cc
+++ b/tools/profiler/platform-macos.cc
@@ -23,18 +23,18 @@
 #include <sys/resource.h>
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 
-
 #include "platform.h"
+#include "UnwinderThread2.h"  /* uwt__register_thread_for_profiling */
 
 // this port is based off of v8 svn revision 9837
 
 // XXX: this is a very stubbed out implementation
 // that only supports a single Sampler
 struct SamplerRegistry {
   static void AddActiveSampler(Sampler *sampler) {
     ASSERT(!SamplerRegistry::sampler);
@@ -124,16 +124,26 @@ static void SetThreadName(const char* na
 }
 
 
 static void* ThreadEntry(void* arg) {
   Thread* thread = reinterpret_cast<Thread*>(arg);
   // This is also initialized by the first argument to pthread_create() but we
   // don't know which thread will run first (the original thread or the new
   // one) so we initialize it here too.
+
+  // BEGIN temp hack for SPS v1-vs-v2
+  extern bool sps_version2();
+  if (sps_version2()) {
+    // Register this thread for profiling.
+    int aLocal;
+    uwt__register_thread_for_profiling( &aLocal );
+  }
+  // END temp hack for SPS v1-vs-v2
+
   thread->data()->thread_ = pthread_self();
   SetThreadName(thread->name());
   ASSERT(thread->data()->thread_ != kNoThread);
   thread->Run();
   return NULL;
 }
 
 
--- a/tools/profiler/platform.h
+++ b/tools/profiler/platform.h
@@ -21,16 +21,19 @@
 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 // SUCH DAMAGE.
 
+#ifndef TOOLS_PLATFORM_H_
+#define TOOLS_PLATFORM_H_
+
 #ifdef ANDROID
 #include <android/log.h>
 #else
 #define __android_log_print(a, ...)
 #endif
 
 #include "mozilla/StandardInteger.h"
 #include "mozilla/Util.h"
@@ -39,21 +42,21 @@
 #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
-#define LOG(text) __android_log_write(ANDROID_LOG_ERROR, "profiler", text)
-#define LOGF(format, ...) __android_log_print(ANDROID_LOG_ERROR, "profiler", format, __VA_ARGS__)
+#define LOG(text) __android_log_write(ANDROID_LOG_ERROR, "Profiler", text)
+#define LOGF(format, ...) __android_log_print(ANDROID_LOG_ERROR, "Profiler", format, __VA_ARGS__)
 #else
-#define LOG(text) printf("Profiler: %s\n", text)
-#define LOGF(format, ...) printf("Profiler: " format "\n", __VA_ARGS__)
+#define LOG(text) fprintf(stderr, "Profiler: %s\n", text)
+#define LOGF(format, ...) fprintf(stderr, "Profiler: " format "\n", __VA_ARGS__)
 #endif
 
 #if defined(XP_MACOSX) || defined(XP_WIN)
 #define ENABLE_SPS_LEAF_DATA
 #endif
 
 typedef uint8_t* Address;
 
@@ -271,8 +274,9 @@ class Sampler {
 
   const int interval_;
   const bool profiling_;
   Atomic32 paused_;
   Atomic32 active_;
   PlatformData* data_;  // Platform specific data.
 };
 
+#endif /* ndef TOOLS_PLATFORM_H_ */
new file mode 100644
--- /dev/null
+++ b/tools/profiler/shim_mac_dump_syms.h
@@ -0,0 +1,8 @@
+
+// Read debug info from |obj_file| and park it in a Module, returned
+// via |module|.  Caller owns the Module and is responsible for
+// deallocating it.  Note that |debug_dirs| is ignored.
+bool ReadSymbolData(const string& obj_file,
+                    const std::vector<string> &debug_dirs,
+                    SymbolData symbol_data,
+                    google_breakpad::Module** module);
new file mode 100644
--- /dev/null
+++ b/tools/profiler/shim_mac_dump_syms.mm
@@ -0,0 +1,20 @@
+// -*- mode: c++ -*-
+
+#include "common/mac/dump_syms.h"
+#include "shim_mac_dump_syms.h"
+
+bool ReadSymbolData(const string& obj_file,
+                    const std::vector<string> &debug_dirs,
+                    SymbolData symbol_data,
+                    google_breakpad::Module** module)
+{
+  google_breakpad::DumpSymbols ds(symbol_data);
+
+  NSString* obj_file_ns = [NSString stringWithUTF8String:obj_file.c_str()];
+  // TODO: remember to [obj_file_ns release] this at the exit points
+
+  if (!ds.Read(obj_file_ns))
+    return false;
+
+  return ds.ReadSymbolData(module);
+}
--- a/tools/profiler/sps_sampler.h
+++ b/tools/profiler/sps_sampler.h
@@ -1,13 +1,16 @@
 /* -*- 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 TOOLS_SPS_SAMPLER_H_
+#define TOOLS_SPS_SAMPLER_H_
+
 #include <stdlib.h>
 #include <signal.h>
 #include <stdarg.h>
 #include "mozilla/ThreadLocal.h"
 #include "nscore.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Util.h"
@@ -21,61 +24,131 @@
 #ifdef MOZ_WIDGET_QT
 #undef slots
 #endif
 #include "jsfriendapi.h"
 
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
-struct ProfileStack;
+struct PseudoStack;
 class TableTicker;
 class JSCustomObject;
 
-extern mozilla::ThreadLocal<ProfileStack *> tlsStack;
-extern mozilla::ThreadLocal<TableTicker *> tlsTicker;
+extern mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
 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
 
-#define SAMPLER_INIT() mozilla_sampler_init()
-#define SAMPLER_SHUTDOWN() mozilla_sampler_shutdown()
-#define SAMPLER_START(entries, interval, features, featureCount) mozilla_sampler_start(entries, interval, features, featureCount)
-#define SAMPLER_STOP() mozilla_sampler_stop()
-#define SAMPLER_IS_ACTIVE() mozilla_sampler_is_active()
-#define SAMPLER_RESPONSIVENESS(time) mozilla_sampler_responsiveness(time)
-#define SAMPLER_GET_RESPONSIVENESS() mozilla_sampler_get_responsiveness()
-#define SAMPLER_FRAME_NUMBER(frameNumber) mozilla_sampler_frame_number(frameNumber)
-#define SAMPLER_SAVE() mozilla_sampler_save()
-#define SAMPLER_GET_PROFILE() mozilla_sampler_get_profile()
-#define SAMPLER_GET_PROFILE_DATA(ctx) mozilla_sampler_get_profile_data(ctx)
-#define SAMPLER_GET_FEATURES() mozilla_sampler_get_features()
+/* Returns true if env var SPS_NEW is set to anything, else false. */
+extern bool sps_version2();
+
+#define SAMPLER_INIT() \
+  do { \
+    if (!sps_version2()) mozilla_sampler_init1(); \
+                    else mozilla_sampler_init2(); \
+  } while (0)
+
+#define SAMPLER_SHUTDOWN() \
+  do { \
+    if (!sps_version2()) mozilla_sampler_shutdown1(); \
+                    else mozilla_sampler_shutdown2(); \
+  } while (0)
+
+#define SAMPLER_START(entries, interval, features, featureCount) \
+  do { \
+    if (!sps_version2()) \
+      mozilla_sampler_start1(entries, interval, features, featureCount); \
+    else \
+      mozilla_sampler_start2(entries, interval, features, featureCount); \
+  } while (0)
+
+#define SAMPLER_STOP() \
+  do { \
+    if (!sps_version2()) mozilla_sampler_stop1(); \
+                    else mozilla_sampler_stop2(); \
+  } while (0)
+
+#define SAMPLER_IS_ACTIVE() \
+    (!sps_version2() ? mozilla_sampler_is_active1() \
+                     : mozilla_sampler_is_active2() )
+
+#define SAMPLER_RESPONSIVENESS(time) \
+  do { \
+    if (!sps_version2()) mozilla_sampler_responsiveness1(time); \
+                    else mozilla_sampler_responsiveness2(time); \
+  } while (0)
+
+#define SAMPLER_GET_RESPONSIVENESS() \
+    (!sps_version2() ? mozilla_sampler_get_responsiveness1() \
+                     : mozilla_sampler_get_responsiveness2() )
+
+#define SAMPLER_FRAME_NUMBER(frameNumber) \
+  do { \
+    if (!sps_version2()) mozilla_sampler_frame_number1(frameNumber); \
+                    else mozilla_sampler_frame_number2(frameNumber); \
+  } while (0)
+
+#define SAMPLER_SAVE() \
+  do { \
+    if (!sps_version2()) mozilla_sampler_save1(); \
+                    else mozilla_sampler_save2(); \
+  } while (0)
+
+#define SAMPLER_GET_PROFILE() \
+   (!sps_version2() ? mozilla_sampler_get_profile1() \
+                    : mozilla_sampler_get_profile2() )
+
+#define SAMPLER_GET_PROFILE_DATA(ctx) \
+   (!sps_version2() ? mozilla_sampler_get_profile_data1(ctx) \
+                    : mozilla_sampler_get_profile_data2(ctx) )
+
+#define SAMPLER_GET_FEATURES() \
+   (!sps_version2() ? mozilla_sampler_get_features1() \
+                    : mozilla_sampler_get_features2() )
+
 // 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__)
 
 #define SAMPLE_LABEL(name_space, info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__)
 #define SAMPLE_LABEL_PRINTF(name_space, info, ...) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__)
 #define SAMPLE_MARKER(info) mozilla_sampler_add_marker(info)
 #define SAMPLE_MAIN_THREAD_LABEL(name_space, info)  MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__)
 #define SAMPLE_MAIN_THREAD_LABEL_PRINTF(name_space, info, ...)  MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__)
 #define SAMPLE_MAIN_THREAD_MARKER(info)  MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info)
 
-#define SAMPLER_PRINT_LOCATION() mozilla_sampler_print_location()
+#define SAMPLER_PRINT_LOCATION() \
+    do { \
+      if (!sps_version2()) mozilla_sampler_print_location1(); \
+                      else mozilla_sampler_print_location2(); \
+    } while (0)
+
+#define SAMPLER_LOCK() \
+    do { \
+      if (!sps_version2()) mozilla_sampler_lock1(); \
+                      else mozilla_sampler_lock2(); \
+    } while (0)
+
+#define SAMPLER_UNLOCK() \
+    do { \
+      if (!sps_version2()) mozilla_sampler_unlock1(); \
+                      else mozilla_sampler_unlock2(); \
+    } while (0)
 
 /* we duplicate this code here to avoid header dependencies
  * which make it more difficult to include in other places */
 #if defined(_M_X64) || defined(__x86_64__)
 #define V8_HOST_ARCH_X64 1
 #elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
 #define V8_HOST_ARCH_IA32 1
 #elif defined(__ARMEL__)
@@ -160,41 +233,73 @@ LinuxKernelMemoryBarrierFunc pLinuxKerne
 #  define STORE_SEQUENCER() asm volatile("" ::: "memory");
 # else
 #  error "Memory clobber not supported for your compiler."
 # endif
 #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
+// 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_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_start(int aEntries, int aInterval, const char** aFeatures, uint32_t aFeatureCount);
-void mozilla_sampler_stop();
-bool mozilla_sampler_is_active();
-void mozilla_sampler_responsiveness(TimeStamp time);
-void mozilla_sampler_frame_number(int frameNumber);
-const double* mozilla_sampler_get_responsiveness();
-void mozilla_sampler_save();
-char* mozilla_sampler_get_profile();
-JSObject *mozilla_sampler_get_profile_data(JSContext *aCx);
-const char** mozilla_sampler_get_features();
-void mozilla_sampler_init();
-void mozilla_sampler_shutdown();
-void mozilla_sampler_print_location();
+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,
+                            uint32_t aFeatureCount);
+
+void mozilla_sampler_stop1();
+void mozilla_sampler_stop2();
+
+bool mozilla_sampler_is_active1();
+bool mozilla_sampler_is_active2();
+
+void mozilla_sampler_responsiveness1(TimeStamp time);
+void mozilla_sampler_responsiveness2(TimeStamp time);
+
+void mozilla_sampler_frame_number1(int frameNumber);
+void mozilla_sampler_frame_number2(int frameNumber);
+
+const double* mozilla_sampler_get_responsiveness1();
+const double* mozilla_sampler_get_responsiveness2();
+
+void mozilla_sampler_save1();
+void mozilla_sampler_save2();
+
+char* mozilla_sampler_get_profile1();
+char* mozilla_sampler_get_profile2();
+
+JSObject *mozilla_sampler_get_profile_data1(JSContext *aCx);
+JSObject *mozilla_sampler_get_profile_data2(JSContext *aCx);
+
+const char** mozilla_sampler_get_features1();
+const char** mozilla_sampler_get_features2();
+
+void mozilla_sampler_init1();
+void mozilla_sampler_init2();
+
+void mozilla_sampler_shutdown1();
+void mozilla_sampler_shutdown2();
+
+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_lock();
+void mozilla_sampler_lock1();
+void mozilla_sampler_lock2();
+
 // Unlock the profiler, leaving it stopped and fires profiler-unlocked.
-void mozilla_sampler_unlock();
+void mozilla_sampler_unlock1();
+void mozilla_sampler_unlock2();
 
 namespace mozilla {
 
 class NS_STACK_CLASS SamplerStackFrameRAII {
 public:
   // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
   SamplerStackFrameRAII(const char *aInfo, uint32_t line) {
     mHandle = mozilla_sampler_call_enter(aInfo, this, false, line);
@@ -206,17 +311,17 @@ private:
   void* mHandle;
 };
 
 static const int SAMPLER_MAX_STRING = 128;
 class NS_STACK_CLASS SamplerStackFramePrintfRAII {
 public:
   // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
   SamplerStackFramePrintfRAII(const char *aDefault, uint32_t line, const char *aFormat, ...) {
-    if (mozilla_sampler_is_active()) {
+    if (SAMPLER_IS_ACTIVE()) {
       va_list args;
       va_start(args, aFormat);
       char buff[SAMPLER_MAX_STRING];
 
       // We have to use seperate printf's because we're using
       // the vargs.
 #if _MSC_VER
       _vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args);
@@ -268,23 +373,24 @@ public:
                         reinterpret_cast<uintptr_t>(sparg) & ~0x1));
     } else {
       setStackAddress(reinterpret_cast<void*>(
                         reinterpret_cast<uintptr_t>(sparg) | 0x1));
     }
   }
 };
 
-// the SamplerStack members are read by signal
+// the PseudoStack members are read by signal
 // handlers, so the mutation of them needs to be signal-safe.
-struct ProfileStack
+struct PseudoStack
 {
 public:
-  ProfileStack()
+  PseudoStack()
     : mStackPointer(0)
+    , mSignalLock(false)
     , mMarkerPointer(0)
     , mQueueClearMarker(false)
     , mRuntime(NULL)
     , mStartJSSampling(false)
   { }
 
   void addMarker(const char *aMarker)
   {
@@ -415,32 +521,32 @@ public:
   // it to queue a clear operation.
   volatile mozilla::sig_safe_t mQueueClearMarker;
   // The runtime which is being sampled
   JSRuntime *mRuntime;
   // Start JS Profiling when possible
   bool mStartJSSampling;
 };
 
-inline ProfileStack* mozilla_profile_stack(void)
+inline PseudoStack* mozilla_get_pseudo_stack(void)
 {
   if (!stack_key_initialized)
     return NULL;
-  return tlsStack.get();
+  return tlsPseudoStack.get();
 }
 
 inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress,
                                         bool aCopy, uint32_t line)
 {
   // check if we've been initialized to avoid calling pthread_getspecific
   // with a null tlsStack which will return undefined results.
   if (!stack_key_initialized)
     return NULL;
 
-  ProfileStack *stack = tlsStack.get();
+  PseudoStack *stack = tlsPseudoStack.get();
   // we can't infer whether 'stack' has been initialized
   // based on the value of stack_key_intiailized because
   // 'stack' is only intialized when a thread is being
   // profiled.
   if (!stack) {
     return stack;
   }
   stack->push(aInfo, aFrameAddress, aCopy, line);
@@ -453,30 +559,31 @@ inline void* mozilla_sampler_call_enter(
   return stack;
 }
 
 inline void mozilla_sampler_call_exit(void *aHandle)
 {
   if (!aHandle)
     return;
 
-  ProfileStack *stack = (ProfileStack*)aHandle;
+  PseudoStack *stack = (PseudoStack*)aHandle;
   stack->pop();
 }
 
 inline void mozilla_sampler_add_marker(const char *aMarker)
 {
   if (!stack_key_initialized)
     return;
 
   // Don't insert a marker if we're not profiling to avoid
   // the heap copy (malloc).
-  if (!mozilla_sampler_is_active()) {
+  if (!SAMPLER_IS_ACTIVE()) {
     return;
   }
 
-  ProfileStack *stack = tlsStack.get();
+  PseudoStack *stack = tlsPseudoStack.get();
   if (!stack) {
     return;
   }
   stack->addMarker(aMarker);
 }
 
+#endif /* ndef TOOLS_SPS_SAMPLER_H_ */