Bug 734691 - Add multi-thread support to profiler. r=benwa
☠☠ backed out by 53b183fe1d62 ☠ ☠
authorJames Willcox <snorp@snorp.net>
Thu, 28 Mar 2013 19:51:15 -0400
changeset 126666 89e99ecdf29f75eca977a57c62810f3c495d71bf
parent 126665 231da3d51bf92acce23a1ac2ecf28e00b6936727
child 126667 77deab8dff65f9ca7fe79ca3904bd865a1bb5fcf
push id25583
push userb56girard@gmail.com
push dateFri, 29 Mar 2013 02:54:29 +0000
treeherdermozilla-inbound@89e99ecdf29f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbenwa
bugs734691
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 734691 - Add multi-thread support to profiler. r=benwa
dom/workers/RuntimeService.cpp
ipc/chromium/src/base/thread.cc
tools/profiler/BreakpadSampler.cpp
tools/profiler/GeckoProfiler.h
tools/profiler/GeckoProfilerFunc.h
tools/profiler/GeckoProfilerImpl.h
tools/profiler/ProfileEntry.cpp
tools/profiler/ProfileEntry.h
tools/profiler/TableTicker.cpp
tools/profiler/TableTicker.h
tools/profiler/platform-linux.cc
tools/profiler/platform-macos.cc
tools/profiler/platform-win32.cc
tools/profiler/platform.cpp
tools/profiler/platform.h
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -37,16 +37,18 @@
 
 #include "Events.h"
 #include "Worker.h"
 #include "WorkerPrivate.h"
 
 #include "OSFileConstants.h"
 #include <algorithm>
 
+#include "GeckoProfiler.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_WORKERS_NAMESPACE
 
 using mozilla::MutexAutoLock;
 using mozilla::MutexAutoUnlock;
 using mozilla::Preferences;
@@ -505,16 +507,18 @@ public:
 
     JSContext* cx = CreateJSContextForWorker(workerPrivate);
     if (!cx) {
       // XXX need to fire an error at parent.
       NS_ERROR("Failed to create runtime and context!");
       return NS_ERROR_FAILURE;
     }
 
+    profiler_register_thread("WebWorker");
+
     {
       JSAutoRequest ar(cx);
       workerPrivate->DoRunLoop(cx);
     }
 
     JSRuntime* rt = JS_GetRuntime(cx);
 
     // XXX Bug 666963 - CTypes can create another JSContext for use with
@@ -533,16 +537,17 @@ public:
     else {
       NS_WARNING("Failed to create dummy context!");
       JS_DestroyContext(cx);
     }
 
     JS_DestroyRuntime(rt);
 
     workerPrivate->ScheduleDeletion(false);
+    profiler_unregister_thread();
     return NS_OK;
   }
 };
 
 } /* anonymous namespace */
 
 BEGIN_WORKERS_NAMESPACE
 
--- a/ipc/chromium/src/base/thread.cc
+++ b/ipc/chromium/src/base/thread.cc
@@ -3,16 +3,17 @@
 // found in the LICENSE file.
 
 #include "base/thread.h"
 
 #include "base/lazy_instance.h"
 #include "base/string_util.h"
 #include "base/thread_local.h"
 #include "base/waitable_event.h"
+#include "GeckoProfiler.h"
 
 namespace base {
 
 // This task is used to trigger the message loop to exit.
 class ThreadQuitTask : public Task {
  public:
   virtual void Run() {
     MessageLoop::current()->Quit();
@@ -131,16 +132,18 @@ void Thread::StopSoon() {
   // most likely means that the thread terminated unexpectedly, probably due
   // to someone calling Quit() on our message loop directly.
   DCHECK(message_loop_);
 
   message_loop_->PostTask(FROM_HERE, new ThreadQuitTask());
 }
 
 void Thread::ThreadMain() {
+  profiler_register_thread(name_.c_str());
+
   // The message loop for this thread.
   MessageLoop message_loop(startup_data_->options.message_loop_type);
 
   // Complete the initialization of our Thread object.
   thread_id_ = PlatformThread::CurrentId();
   PlatformThread::SetName(name_.c_str());
   message_loop.set_thread_name(name_);
   message_loop_ = &message_loop;
@@ -156,14 +159,16 @@ void Thread::ThreadMain() {
   message_loop.Run();
 
   // Let the thread do extra cleanup.
   CleanUp();
 
   // Assert that MessageLoop::Quit was called by ThreadQuitTask.
   DCHECK(GetThreadWasQuitProperly());
 
+  profiler_unregister_thread();
+
   // We can't receive messages anymore.
   message_loop_ = NULL;
   thread_id_ = 0;
 }
 
 }  // namespace base
--- a/tools/profiler/BreakpadSampler.cpp
+++ b/tools/profiler/BreakpadSampler.cpp
@@ -152,45 +152,52 @@ void genPseudoBacktraceEntries(/*MODIFIE
 #   endif
   }
 # endif
 }
 
 // RUNS IN SIGHANDLER CONTEXT
 void BreakpadSampler::Tick(TickSample* sample)
 {
+  if (!sample->threadProfile) {
+    // Platform doesn't support multithread, so use the main thread profile we created
+    sample->threadProfile = GetPrimaryThreadProfile();
+  }
+
+  ThreadProfile& currThreadProfile = *sample->threadProfile;
+
   /* 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();
+  PseudoStack* stack = currThreadProfile.GetPseudoStack();
   for (int i = 0; stack->getMarker(i) != NULL; i++) {
     utb__addEntry( utb, ProfileEntry('m', stack->getMarker(i)) );
   }
   stack->mQueueClearMarker = true;
 
   bool recordSample = true;
   if (mJankOnly) {
     // if we are on a different event we can discard any temporary samples
     // we've kept around
     if (sLastSampledEventGeneration != sCurrentEventGeneration) {
       // 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();
+      currThreadProfile.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;
@@ -287,17 +294,17 @@ void BreakpadSampler::Tick(TickSample* s
     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);
+    uwt__release_full_buffer(&currThreadProfile, utb, ucV);
   } else {
-    uwt__release_full_buffer(&mPrimaryThreadProfile, utb, NULL);
+    uwt__release_full_buffer(&currThreadProfile, utb, NULL);
   }
 }
 
 // END take samples
 ////////////////////////////////////////////////////////////////////////
 
--- a/tools/profiler/GeckoProfiler.h
+++ b/tools/profiler/GeckoProfiler.h
@@ -130,15 +130,18 @@ static inline void profiler_print_locati
 // Discard the profile, throw away the profile and notify 'profiler-locked'.
 // This function is to be used when entering private browsing to prevent
 // the profiler from collecting sensitive data.
 static inline void profiler_lock() {}
 
 // Re-enable the profiler and notify 'profiler-unlocked'.
 static inline void profiler_unlock() {}
 
+static inline void profiler_register_thread(const char* name) {}
+static inline void profiler_unregister_thread() {}
+
 #else
 
 #include "GeckoProfilerImpl.h"
 
 #endif
 
 #endif // ifndef SAMPLER_H
--- a/tools/profiler/GeckoProfilerFunc.h
+++ b/tools/profiler/GeckoProfilerFunc.h
@@ -52,13 +52,17 @@ 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();
 
 // Unlock the profiler, leaving it stopped and fires profiler-unlocked.
 void mozilla_sampler_unlock();
 
+// Register/unregister threads with the profiler
+bool mozilla_sampler_register_thread(const char* name);
+void mozilla_sampler_unregister_thread();
+
 /* Returns true if env var SPS_NEW is set to anything, else false. */
 extern bool sps_version2();
 
 #endif
 
--- a/tools/profiler/GeckoProfilerImpl.h
+++ b/tools/profiler/GeckoProfilerImpl.h
@@ -135,30 +135,43 @@ void profiler_lock()
 }
 
 static inline
 void profiler_unlock()
 {
   return mozilla_sampler_unlock();
 }
 
+static inline
+void profiler_register_thread(const char* name)
+{
+  mozilla_sampler_register_thread(name);
+}
+
+static inline
+void profiler_unregister_thread()
+{
+  mozilla_sampler_unregister_thread();
+}
+
 // 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 PROFILER_LABEL(name_space, info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__)
 #define PROFILER_LABEL_PRINTF(name_space, info, ...) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__)
 #define PROFILER_MARKER(info) mozilla_sampler_add_marker(info)
 #define PROFILER_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 PROFILER_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 PROFILER_MAIN_THREAD_MARKER(info)  MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info)
 
+
 /* FIXME/bug 789667: memory constraints wouldn't much of a problem for
  * this small a sample buffer size, except that serializing the
  * profile data is extremely, unnecessarily memory intensive. */
 #ifdef MOZ_WIDGET_GONK
 # define PLATFORM_LIKELY_MEMORY_CONSTRAINED
 #endif
 
 #if !defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) && !defined(ARCH_ARMV6)
--- a/tools/profiler/ProfileEntry.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -129,29 +129,33 @@ std::ostream& operator<<(std::ostream& s
 ////////////////////////////////////////////////////////////////////////
 
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN ThreadProfile
 
 #define DYNAMIC_MAX_STRING 512
 
-ThreadProfile::ThreadProfile(int aEntrySize, PseudoStack *aStack)
+ThreadProfile::ThreadProfile(const char* aName, int aEntrySize, PseudoStack *aStack, int aThreadId, bool aIsMainThread)
   : mWritePos(0)
   , mLastFlushPos(0)
   , mReadPos(0)
   , mEntrySize(aEntrySize)
   , mPseudoStack(aStack)
   , mMutex("ThreadProfile::mMutex")
+  , mName(strdup(aName))
+  , mThreadId(aThreadId)
+  , mIsMainThread(aIsMainThread)
 {
   mEntries = new ProfileEntry[mEntrySize];
 }
 
 ThreadProfile::~ThreadProfile()
 {
+  free(mName);
   delete[] mEntries;
 }
 
 void ThreadProfile::addTag(ProfileEntry aTag)
 {
   // Called from signal, call only reentrant functions
   mEntries[mWritePos] = aTag;
   mWritePos = (mWritePos + 1) % mEntrySize;
@@ -294,16 +298,20 @@ JSCustomObject* ThreadProfile::ToJSObjec
   JSObjectBuilder b(aCx);
   JSCustomObject *profile = b.CreateObject();
   BuildJSObject(b, profile);
 
   return profile;
 }
 
 void ThreadProfile::BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile) {
+  // Thread meta data
+  b.DefineProperty(profile, "name", mName);
+  b.DefineProperty(profile, "tid", mThreadId);
+
   JSCustomArray *samples = b.CreateArray();
   b.DefineProperty(profile, "samples", samples);
 
   JSCustomObject *sample = nullptr;
   JSCustomArray *frames = nullptr;
   JSCustomArray *marker = nullptr;
 
   int readPos = mReadPos;
--- a/tools/profiler/ProfileEntry.h
+++ b/tools/profiler/ProfileEntry.h
@@ -1,16 +1,17 @@
 /* -*- 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 <ostream>
 #include "GeckoProfilerImpl.h"
 #include "JSAObjectBuilder.h"
 #include "platform.h"
 #include "mozilla/Mutex.h"
 
 class ThreadProfile;
 class ThreadProfile;
 
@@ -51,37 +52,45 @@ private:
   char mTagName;
 };
 
 typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagStringData);
 
 class ThreadProfile
 {
 public:
-  ThreadProfile(int aEntrySize, PseudoStack *aStack);
+  ThreadProfile(const char* aName, int aEntrySize, PseudoStack *aStack, int aThreadId, bool aIsMainThread);
   ~ThreadProfile();
   void addTag(ProfileEntry aTag);
   void flush();
   void erase();
   char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff);
   void IterateTags(IterateTagsCallback aCallback);
   friend std::ostream& operator<<(std::ostream& stream,
                                   const ThreadProfile& profile);
   void ToStreamAsJSON(std::ostream& stream);
   JSCustomObject *ToJSObject(JSContext *aCx);
   PseudoStack* GetPseudoStack();
   mozilla::Mutex* GetMutex();
   void BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile);
+
+  bool IsMainThread() const { return mIsMainThread; }
+  const char* Name() const { return mName; }
+  int ThreadId() const { return mThreadId; }
+
 private:
   // Circular buffer 'Keep One Slot Open' implementation
   // for simplicity
   ProfileEntry* mEntries;
   int            mWritePos; // points to the next entry we will write to
   int            mLastFlushPos; // points to the next entry since the last flush()
   int            mReadPos;  // points to the next entry we will read to
   int            mEntrySize;
   PseudoStack*   mPseudoStack;
   mozilla::Mutex mMutex;
+  char*          mName;
+  int            mThreadId;
+  bool           mIsMainThread;
 };
 
 std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile);
 
 #endif /* ndef MOZ_PROFILE_ENTRY_H */
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -168,26 +168,32 @@ void TableTicker::BuildJSObject(JSAObjec
   // Put meta data
   JSCustomObject *meta = GetMetaJSCustomObject(b);
   b.DefineProperty(profile, "meta", meta);
 
   // Lists the samples for each ThreadProfile
   JSCustomArray *threads = b.CreateArray();
   b.DefineProperty(profile, "threads", threads);
 
-  // For now we only have one thread
   SetPaused(true);
-  ThreadProfile* prof = GetPrimaryThreadProfile();
-  prof->GetMutex()->Lock();
-  JSCustomObject* threadSamples = b.CreateObject();
-  prof->BuildJSObject(b, threadSamples);
-  b.ArrayPush(threads, threadSamples);
-  prof->GetMutex()->Unlock();
+
+  {
+    mozilla::MutexAutoLock lock(sRegisteredThreadsMutex);
+
+    for (size_t i = 0; i < sRegisteredThreads.size(); i++) {
+      MutexAutoLock lock(*sRegisteredThreads[i]->Profile()->GetMutex());
+
+      JSCustomObject* threadSamples = b.CreateObject();
+      sRegisteredThreads[i]->Profile()->BuildJSObject(b, threadSamples);
+      b.ArrayPush(threads, threadSamples);
+    }
+  }
+
   SetPaused(false);
-}
+} 
 
 // END SaveProfileTask et al
 ////////////////////////////////////////////////////////////////////////
 
 static
 void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
 {
   aProfile.addTag(ProfileEntry(aTagName, ""));
@@ -378,70 +384,77 @@ void doSampleStackTrace(PseudoStack *aSt
     aProfile.addTag(ProfileEntry('L', (void*)sample->lr));
 #endif
   }
 #endif
 }
 
 void TableTicker::Tick(TickSample* sample)
 {
+  if (!sample->threadProfile) {
+    // Platform doesn't support multithread, so use the main thread profile we created
+    sample->threadProfile = GetPrimaryThreadProfile();
+  }
+
+  ThreadProfile& currThreadProfile = *sample->threadProfile;
+
   // Marker(s) come before the sample
-  PseudoStack* stack = mPrimaryThreadProfile.GetPseudoStack();
+  PseudoStack* stack = currThreadProfile.GetPseudoStack();
   for (int i = 0; stack->getMarker(i) != NULL; i++) {
-    addDynamicTag(mPrimaryThreadProfile, 'm', stack->getMarker(i));
+    addDynamicTag(currThreadProfile, '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();
+      currThreadProfile.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;
       }
     }
   }
 
 #if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK)
   if (mUseStackWalk) {
-    doNativeBacktrace(mPrimaryThreadProfile, sample);
+    doNativeBacktrace(currThreadProfile, sample);
   } else {
-    doSampleStackTrace(stack, mPrimaryThreadProfile, mAddLeafAddresses ? sample : nullptr);
+    doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr);
   }
 #else
-  doSampleStackTrace(stack, mPrimaryThreadProfile, mAddLeafAddresses ? sample : nullptr);
+  doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr);
 #endif
 
   if (recordSample)
-    mPrimaryThreadProfile.flush();
+    currThreadProfile.flush();
 
-  if (!sLastTracerEvent.IsNull() && sample) {
+  if (!sLastTracerEvent.IsNull() && sample && currThreadProfile.IsMainThread()) {
     TimeDuration delta = sample->timestamp - sLastTracerEvent;
-    mPrimaryThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
+    currThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
   }
 
   if (sample) {
     TimeDuration delta = sample->timestamp - mStartTime;
-    mPrimaryThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds()));
+    currThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds()));
   }
 
   if (sLastFrameNumber != sFrameNumber) {
-    mPrimaryThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
+    currThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
     sLastFrameNumber = sFrameNumber;
   }
 }
 
 static void print_callback(const ProfileEntry& entry, const char* tagStringData) {
   switch (entry.getTagName()) {
     case 's':
     case 'c':
@@ -455,17 +468,18 @@ void mozilla_sampler_print_location1()
     profiler_init();
 
   PseudoStack *stack = tlsPseudoStack.get();
   if (!stack) {
     MOZ_ASSERT(false);
     return;
   }
 
-  ThreadProfile threadProfile(1000, stack);
+  ThreadProfile threadProfile("Temp", PROFILE_DEFAULT_ENTRY, stack,
+                              0, false);
   doSampleStackTrace(stack, threadProfile, NULL);
 
   threadProfile.flush();
 
   printf_stderr("Backtrace:\n");
   threadProfile.IterateTags(print_callback);
 }
 
--- a/tools/profiler/TableTicker.h
+++ b/tools/profiler/TableTicker.h
@@ -1,15 +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/. */
 
 #include "platform.h"
 #include "ProfileEntry.h"
+#include "mozilla/Mutex.h"
 
 static bool
 hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature) {
   for(size_t i = 0; i < aFeatureCount; i++) {
     if (strcmp(aFeatures[i], aFeature) == 0)
       return true;
   }
   return false;
@@ -27,48 +28,95 @@ extern unsigned int sCurrentEventGenerat
 extern unsigned int sLastSampledEventGeneration;
 
 class BreakpadSampler;
 
 class TableTicker: public Sampler {
  public:
   TableTicker(int aInterval, int aEntrySize, PseudoStack *aStack,
               const char** aFeatures, uint32_t aFeatureCount)
-    : Sampler(aInterval, true)
-    , mPrimaryThreadProfile(aEntrySize, aStack)
+    : Sampler(aInterval, true, aEntrySize)
+    , mPrimaryThreadProfile(nullptr)
     , mStartTime(TimeStamp::Now())
     , mSaveRequested(false)
   {
     mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
 
     //XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
     mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
     mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
     mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
-    mPrimaryThreadProfile.addTag(ProfileEntry('m', "Start"));
+
+    {
+      mozilla::MutexAutoLock lock(sRegisteredThreadsMutex);
+
+      // Create ThreadProfile for each registered thread
+      for (uint32_t i = 0; i < sRegisteredThreads.size(); i++) {
+        ThreadInfo* info = sRegisteredThreads[i];
+        ThreadProfile* profile = new ThreadProfile(info->Name(),
+                                                   aEntrySize,
+                                                   info->Stack(),
+                                                   info->ThreadId(),
+                                                   info->IsMainThread());
+        profile->addTag(ProfileEntry('m', "Start"));
+
+        info->SetProfile(profile);
+      }
+
+      SetActiveSampler(this);
+    }
   }
 
-  ~TableTicker() { if (IsActive()) Stop(); }
+  ~TableTicker() {
+    if (IsActive())
+      Stop();
+
+    SetActiveSampler(nullptr);
+
+    // Destroy ThreadProfile for all threads
+    {
+      mozilla::MutexAutoLock lock(sRegisteredThreadsMutex);
+
+      for (uint32_t i = 0; i < sRegisteredThreads.size(); i++) {
+        ThreadProfile* profile = sRegisteredThreads[i]->Profile();
+        if (profile) {
+          delete profile;
+          sRegisteredThreads[i]->SetProfile(nullptr);
+        }
+      }
+    }
+  }
 
   virtual void SampleStack(TickSample* sample) {}
 
   // Called within a signal. This function must be reentrant
   virtual void Tick(TickSample* sample);
 
   // Called within a signal. This function must be reentrant
   virtual void RequestSave()
   {
     mSaveRequested = true;
   }
 
   virtual void HandleSaveRequest();
 
   ThreadProfile* GetPrimaryThreadProfile()
   {
-    return &mPrimaryThreadProfile;
+    if (!mPrimaryThreadProfile) {
+      mozilla::MutexAutoLock lock(sRegisteredThreadsMutex);
+
+      for (uint32_t i = 0; i < sRegisteredThreads.size(); i++) {
+        if (sRegisteredThreads[i]->IsMainThread()) {
+          mPrimaryThreadProfile = sRegisteredThreads[i]->Profile();
+          break;
+        }
+      }
+    }
+
+    return mPrimaryThreadProfile;
   }
 
   void ToStreamAsJSON(std::ostream& stream);
   virtual JSObject *ToJSObject(JSContext *aCx);
   JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b);
 
   const bool ProfileJS() { return mProfileJS; }
 
@@ -76,17 +124,17 @@ class TableTicker: public Sampler {
 
 protected:
   // Not implemented on platforms which do not support backtracing
   void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
 
   void BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile);
 
   // This represent the application's main thread (SAMPLER_INIT)
-  ThreadProfile mPrimaryThreadProfile;
+  ThreadProfile* mPrimaryThreadProfile;
   TimeStamp mStartTime;
   bool mSaveRequested;
   bool mAddLeafAddresses;
   bool mUseStackWalk;
   bool mJankOnly;
   bool mProfileJS;
 };
 
--- a/tools/profiler/platform-linux.cc
+++ b/tools/profiler/platform-linux.cc
@@ -34,16 +34,17 @@
 #include <pthread.h>
 #include <semaphore.h>
 #include <signal.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <stdlib.h>
+#include <sched.h>
 #ifdef ANDROID
 #include <android/log.h>
 #else
 #define __android_log_print(a, ...)
 #endif
 // Ubuntu Dapper requires memory pages to be marked as
 // executable. Otherwise, OS raises an exception when executing code
 // in that page.
@@ -55,60 +56,63 @@
 #ifdef __GLIBC__
 #include <execinfo.h>   // backtrace, backtrace_symbols
 #endif  // def __GLIBC__
 #include <strings.h>    // index
 #include <errno.h>
 #include <stdarg.h>
 #include "platform.h"
 #include "GeckoProfilerImpl.h"
+#include "mozilla/Mutex.h"
+#include "ProfileEntry.h"
+#include "nsThreadUtils.h"
 
 #include <string.h>
 #include <stdio.h>
+#include <list>
 
 #define SIGNAL_SAVE_PROFILE SIGUSR2
 
 #if defined(__GLIBC__)
 // glibc doesn't implement gettid(2).
 #include <sys/syscall.h>
 pid_t gettid()
 {
   return (pid_t) syscall(SYS_gettid);
 }
 #endif
 
-static Sampler* sActiveSampler = NULL;
-
-
 #ifdef ANDROID
 #include "android-signal-defs.h"
 #endif
 
+static ThreadProfile* sCurrentThreadProfile = NULL;
+
 static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) {
-  sActiveSampler->RequestSave();
+  Sampler::GetActiveSampler()->RequestSave();
 }
 
 #ifdef ANDROID
 #define V8_HOST_ARCH_ARM 1
 #define SYS_gettid __NR_gettid
 #define SYS_tgkill __NR_tgkill
 #else
 #define V8_HOST_ARCH_X64 1
 #endif
 static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
-  if (!sActiveSampler)
+  if (!Sampler::GetActiveSampler())
     return;
 
   TickSample sample_obj;
   TickSample* sample = &sample_obj;
   sample->context = context;
 
 #ifdef ENABLE_SPS_LEAF_DATA
   // If profiling, we extract the current pc and sp.
-  if (sActiveSampler->IsProfiling()) {
+  if (Sampler::GetActiveSampler()->IsProfiling()) {
     // Extracting the sample from the context is extremely machine dependent.
     ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
     mcontext_t& mcontext = ucontext->uc_mcontext;
 #if V8_HOST_ARCH_IA32
     sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
     sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
     sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
 #elif V8_HOST_ARCH_X64
@@ -133,24 +137,27 @@ static void ProfilerSignalHandler(int si
 #endif
 #endif
 #elif V8_HOST_ARCH_MIPS
     // Implement this on MIPS.
     UNIMPLEMENTED();
 #endif
   }
 #endif
+  sample->threadProfile = sCurrentThreadProfile;
   sample->timestamp = mozilla::TimeStamp::Now();
 
-  sActiveSampler->Tick(sample);
+  Sampler::GetActiveSampler()->Tick(sample);
+
+  sCurrentThreadProfile = NULL;
 }
 
 #ifndef XP_MACOSX
-void tgkill(pid_t tgid, pid_t tid, int signalno) {
-  syscall(SYS_tgkill, tgid, tid, signalno);
+int tgkill(pid_t tgid, pid_t tid, int signalno) {
+  return syscall(SYS_tgkill, tgid, tid, signalno);
 }
 #endif
 
 class Sampler::PlatformData : public Malloced {
  public:
   explicit PlatformData(Sampler* sampler)
       : sampler_(sampler),
         signal_handler_installed_(false),
@@ -168,18 +175,43 @@ class Sampler::PlatformData : public Mal
   void SignalSender() {
     while (sampler_->IsActive()) {
       sampler_->HandleSaveRequest();
 
       if (!sampler_->IsPaused()) {
 #ifdef XP_MACOSX
         pthread_kill(signal_receiver_, SIGPROF);
 #else
-        // Glibc doesn't provide a wrapper for tgkill(2).
-        tgkill(vm_tgid_, vm_tid_, SIGPROF);
+
+        std::vector<ThreadInfo*> threads = GetRegisteredThreads();
+
+        for (uint32_t i = 0; i < threads.size(); i++) {
+          ThreadInfo* info = threads[i];
+
+          // We use sCurrentThreadProfile the ThreadProfile for the
+          // thread we're profiling to the signal handler
+          sCurrentThreadProfile = info->Profile();
+
+          int threadId = info->ThreadId();
+          if (threadId == 0) {
+            threadId = vm_tid_;
+          }
+
+          if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) {
+            printf_stderr("profiler failed to signal tid=%d\n", threadId);
+#ifdef DEBUG
+            abort();
+#endif
+            continue;
+          }
+
+          // Wait for the signal handler to run before moving on to the next one
+          while (sCurrentThreadProfile)
+            sched_yield();
+        }
 #endif
       }
 
       // Convert ms to us and subtract 100 us to compensate delays
       // occuring during signal delivery.
       // TODO measure and confirm this.
       const useconds_t interval = sampler_->interval_ * 1000 - 100;
       //int result = usleep(interval);
@@ -204,33 +236,33 @@ class Sampler::PlatformData : public Mal
 static void* SenderEntry(void* arg) {
   Sampler::PlatformData* data =
       reinterpret_cast<Sampler::PlatformData*>(arg);
   data->SignalSender();
   return 0;
 }
 
 
-Sampler::Sampler(int interval, bool profiling)
+Sampler::Sampler(int interval, bool profiling, int entrySize)
     : interval_(interval),
       profiling_(profiling),
       paused_(false),
-      active_(false) {
+      active_(false),
+      entrySize_(entrySize) {
   data_ = new PlatformData(this);
 }
 
 Sampler::~Sampler() {
   ASSERT(!data_->signal_sender_launched_);
   delete data_;
 }
 
 
 void Sampler::Start() {
   LOG("Sampler started");
-  if (sActiveSampler != NULL) return;
 
   // Request profiling signals.
   LOG("Request signal");
   struct sigaction sa;
   sa.sa_sigaction = ProfilerSignalHandler;
   sigemptyset(&sa.sa_mask);
   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   if (sigaction(SIGPROF, &sa, &data_->old_sigprof_signal_handler_) != 0) {
@@ -254,19 +286,16 @@ void Sampler::Start() {
   // Sending the signal ourselves instead of relying on itimer provides
   // much better accuracy.
   SetActive(true);
   if (pthread_create(
           &data_->signal_sender_thread_, NULL, SenderEntry, data_) == 0) {
     data_->signal_sender_launched_ = true;
   }
   LOG("Profiler thread started");
-
-  // Set this sampler as the active sampler.
-  sActiveSampler = this;
 }
 
 
 void Sampler::Stop() {
   SetActive(false);
 
   // Wait for signal sender termination (it will exit after setting
   // active_ to false).
@@ -276,19 +305,50 @@ void Sampler::Stop() {
   }
 
   // Restore old signal handler
   if (data_->signal_handler_installed_) {
     sigaction(SIGNAL_SAVE_PROFILE, &data_->old_sigsave_signal_handler_, 0);
     sigaction(SIGPROF, &data_->old_sigprof_signal_handler_, 0);
     data_->signal_handler_installed_ = false;
   }
+}
 
-  // This sampler is no longer the active sampler.
-  sActiveSampler = NULL;
+bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
+{
+  mozilla::MutexAutoLock lock(sRegisteredThreadsMutex);
+
+  ThreadInfo* info = new ThreadInfo(aName, gettid(), aIsMainThread, aPseudoStack);
+
+  if (sActiveSampler) {
+    // We need to create the ThreadProfile now
+    info->SetProfile(new ThreadProfile(info->Name(),
+                                       sActiveSampler->EntrySize(),
+                                       info->Stack(),
+                                       info->ThreadId(),
+                                       aIsMainThread));
+  }
+
+  sRegisteredThreads.push_back(info);
+  return true;
+}
+
+void Sampler::UnregisterCurrentThread()
+{
+  mozilla::MutexAutoLock lock(sRegisteredThreadsMutex);
+
+  int id = gettid();
+
+  for (uint32_t i = 0; i < sRegisteredThreads.size(); i++) {
+    if (sRegisteredThreads[i]->ThreadId() == id) {
+      delete sRegisteredThreads[i];
+      sRegisteredThreads.erase(sRegisteredThreads.begin() + i);
+      break;
+    }
+  }
 }
 
 #ifdef ANDROID
 static struct sigaction old_sigstart_signal_handler;
 const int SIGSTART = SIGUSR1;
 
 static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
   profiler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
@@ -302,8 +362,9 @@ void OS::RegisterStartHandler()
   sa.sa_sigaction = StartSignalHandler;
   sigemptyset(&sa.sa_mask);
   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   if (sigaction(SIGSTART, &sa, &old_sigstart_signal_handler) != 0) {
     LOG("Error installing signal");
   }
 }
 #endif
+
--- a/tools/profiler/platform-macos.cc
+++ b/tools/profiler/platform-macos.cc
@@ -23,16 +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 "nsThreadUtils.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 {
@@ -194,18 +196,18 @@ class Sampler::PlatformData : public Mal
   // stack using frame pointers.
   pthread_t profiled_pthread_;
 };
 
 
 class SamplerThread : public Thread {
  public:
   explicit SamplerThread(int interval)
-      : Thread("SamplerThread"),
-        interval_(interval) {}
+      : Thread("SamplerThread")
+      , interval_(interval) {}
 
   static void AddActiveSampler(Sampler* sampler) {
     ScopedLock lock(mutex_);
     SamplerRegistry::AddActiveSampler(sampler);
     if (instance_ == NULL) {
       instance_ = new SamplerThread(sampler->interval());
       instance_->Start();
     } else {
@@ -274,16 +276,17 @@ class SamplerThread : public Thread {
                          flavor,
                          reinterpret_cast<natural_t*>(&state),
                          &count) == KERN_SUCCESS) {
       //sample->state = sampler->isolate()->current_vm_state();
       sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
       sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
       sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
       sample->timestamp = mozilla::TimeStamp::Now();
+      sample->threadProfile = NULL;
       sampler->SampleStack(sample);
       sampler->Tick(sample);
     }
     thread_resume(profiled_thread);
   }
 
   const int interval_;
   //RuntimeProfilerRateLimiter rate_limiter_;
@@ -297,22 +300,23 @@ class SamplerThread : public Thread {
 
 #undef REGISTER_FIELD
 
 
 Mutex* SamplerThread::mutex_ = OS::CreateMutex();
 SamplerThread* SamplerThread::instance_ = NULL;
 
 
-Sampler::Sampler(int interval, bool profiling)
+Sampler::Sampler(int interval, bool profiling, int entrySize)
     : // isolate_(isolate),
       interval_(interval),
       profiling_(profiling),
       paused_(false),
-      active_(false) /*,
+      active_(false),
+      entrySize_(entrySize) /*,
       samples_taken_(0)*/ {
   data_ = new PlatformData;
 }
 
 
 Sampler::~Sampler() {
   ASSERT(!IsActive());
   delete data_;
@@ -332,8 +336,35 @@ void Sampler::Stop() {
   SamplerThread::RemoveActiveSampler(this);
 }
 
 pthread_t
 Sampler::GetProfiledThread(Sampler::PlatformData* aData)
 {
   return aData->profiled_pthread();
 }
+
+bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
+{
+  mozilla::MutexAutoLock lock(sRegisteredThreadsMutex);
+
+  if (!aIsMainThread)
+    return false;
+
+  ThreadInfo* info = new ThreadInfo(aName, 0, true, aPseudoStack);
+
+  if (sActiveSampler) {
+    // We need to create the ThreadProfile now
+    info->SetProfile(new ThreadProfile(info->Name(),
+                                       sActiveSampler->EntrySize(),
+                                       info->Stack(),
+                                       info->ThreadId(),
+                                       true));
+  }
+
+  sRegisteredThreads.push_back(info);
+  return true;
+}
+
+void Sampler::UnregisterCurrentThread()
+{
+  // We only have the main thread currently and that will never be unregistered
+}
\ No newline at end of file
--- a/tools/profiler/platform-win32.cc
+++ b/tools/profiler/platform-win32.cc
@@ -23,19 +23,19 @@
 // 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.
 
 #include <windows.h>
 #include <mmsystem.h>
+#include <process.h>
 #include "platform.h"
-#include <process.h>
-
+#include "ProfileEntry.h"
 
 class Sampler::PlatformData : public Malloced {
  public:
   // Get a handle to the calling thread. This is the thread that we are
   // going to profile. We need to make a copy of the handle because we are
   // going to use it in the sampler thread. Using GetThreadHandle() will
   // not work in this case. We're using OpenThread because DuplicateHandle
   // for some reason doesn't work in Chrome's sandbox.
@@ -62,19 +62,19 @@ uintptr_t
 Sampler::GetThreadHandle(Sampler::PlatformData* aData)
 {
   return (uintptr_t) aData->profiled_thread();
 }
 
 class SamplerThread : public Thread {
  public:
   SamplerThread(int interval, Sampler* sampler)
-      : Thread("SamplerThread"),
-        interval_(interval),
-        sampler_(sampler) {}
+      : Thread("SamplerThread")
+      , interval_(interval)
+      , sampler_(sampler) {}
 
   static void StartSampler(Sampler* sampler) {
     if (instance_ == NULL) {
       instance_ = new SamplerThread(sampler->interval(), sampler);
       instance_->Start();
     } else {
       ASSERT(instance_->interval_ == sampler->interval());
     }
@@ -115,16 +115,17 @@ class SamplerThread : public Thread {
     CONTEXT context;
     memset(&context, 0, sizeof(context));
 
     TickSample sample_obj;
     TickSample* sample = &sample_obj;
 
     // Grab the timestamp before pausing the thread, to avoid deadlocks.
     sample->timestamp = mozilla::TimeStamp::Now();
+    sample->threadProfile = NULL;
 
     static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
     if (SuspendThread(profiled_thread) == kSuspendFailed)
       return;
 
     context.ContextFlags = CONTEXT_CONTROL;
     if (GetThreadContext(profiled_thread, &context) != 0) {
 #if V8_HOST_ARCH_X64
@@ -150,21 +151,22 @@ class SamplerThread : public Thread {
   static SamplerThread* instance_;
 
   DISALLOW_COPY_AND_ASSIGN(SamplerThread);
 };
 
 SamplerThread* SamplerThread::instance_ = NULL;
 
 
-Sampler::Sampler(int interval, bool profiling)
+Sampler::Sampler(int interval, bool profiling, int entrySize)
     : interval_(interval),
       profiling_(profiling),
       paused_(false),
       active_(false),
+      entrySize_(entrySize),
       data_(new PlatformData) {
 }
 
 Sampler::~Sampler() {
   ASSERT(!IsActive());
   delete data_;
 }
 
@@ -233,8 +235,35 @@ void Thread::Join() {
   if (data_->thread_id_ != GetCurrentThreadId()) {
     WaitForSingleObject(data_->thread_, INFINITE);
   }
 }
 
 void OS::Sleep(int milliseconds) {
   ::Sleep(milliseconds);
 }
+
+bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
+{
+  mozilla::MutexAutoLock lock(sRegisteredThreadsMutex);
+
+  if (!aIsMainThread)
+    return false;
+
+  ThreadInfo* info = new ThreadInfo(aName, 0, true, aPseudoStack);
+
+  if (sActiveSampler) {
+    // We need to create the ThreadProfile now
+    info->SetProfile(new ThreadProfile(info->Name(),
+                                       sActiveSampler->EntrySize(),
+                                       info->Stack(),
+                                       info->ThreadId(),
+                                       true));
+  }
+
+  sRegisteredThreads.push_back(info);
+  return true;
+}
+
+void Sampler::UnregisterCurrentThread()
+{
+  // We only have the main thread currently and that will never be unregistered
+}
--- a/tools/profiler/platform.cpp
+++ b/tools/profiler/platform.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/ThreadLocal.h"
 #include "PseudoStack.h"
 #include "TableTicker.h"
 #include "UnwinderThread2.h"
 #include "nsIObserverService.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "mozilla/Services.h"
+#include "nsThreadUtils.h"
 
 mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
 mozilla::ThreadLocal<TableTicker *> tlsTicker;
 // We need to track whether we've been initialized otherwise
 // we end up using tlsStack without initializing it.
 // Because tlsStack is totally opaque to us we can't reuse
 // it as the flag itself.
 bool stack_key_initialized;
@@ -38,16 +39,28 @@ unsigned int sLastSampledEventGeneration
  * note: it might also be worth tracking everytime we go around
  * the event loop */
 unsigned int sCurrentEventGeneration = 0;
 /* we don't need to worry about overflow because we only treat the
  * case of them being the same as special. i.e. we only run into
  * a problem if 2^32 events happen between samples that we need
  * to know are associated with different events */
 
+std::vector<ThreadInfo*> Sampler::sRegisteredThreads;
+mozilla::Mutex Sampler::sRegisteredThreadsMutex("sRegisteredThreads mutex");
+
+Sampler* Sampler::sActiveSampler;
+
+ThreadInfo::~ThreadInfo() {
+  free(mName);
+
+  if (mProfile)
+    delete mProfile;
+}
+
 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)
@@ -180,16 +193,18 @@ void mozilla_sampler_init()
     LOG("Failed to init.");
     return;
   }
   stack_key_initialized = true;
 
   PseudoStack *stack = new PseudoStack();
   tlsPseudoStack.set(stack);
 
+  Sampler::RegisterCurrentThread("Gecko", stack, true);
+
   if (sps_version2()) {
     // Read mode settings from MOZ_PROFILER_MODE and interval
     // settings from MOZ_PROFILER_INTERVAL.
     read_env_vars();
 
     // Create the unwinder thread.  ATM there is only one.
     uwt__init();
 
@@ -247,16 +262,18 @@ void mozilla_sampler_shutdown()
   // Shut down and reap the unwinder thread.  We have to do this
   // before stopping the sampler, so as to guarantee that the unwinder
   // thread doesn't try to access memory that the subsequent call to
   // mozilla_sampler_stop causes to be freed.
   if (sps_version2()) {
     uwt__deinit();
   }
 
+  Sampler::ClearRegisteredThreads();
+
   profiler_stop();
   // We can't delete the Stack because we can be between a
   // sampler call_enter/call_exit point.
   // TODO Need to find a safe time to delete Stack
 }
 
 void mozilla_sampler_save()
 {
@@ -441,11 +458,25 @@ void mozilla_sampler_lock()
 
 void mozilla_sampler_unlock()
 {
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os)
     os->NotifyObservers(nullptr, "profiler-unlocked", nullptr);
 }
 
+bool mozilla_sampler_register_thread(const char* aName)
+{
+  PseudoStack* stack = new PseudoStack();
+  tlsPseudoStack.set(stack);
+
+  return Sampler::RegisterCurrentThread(aName, stack, false);
+}
+
+void mozilla_sampler_unregister_thread()
+{
+  Sampler::UnregisterCurrentThread();
+}
+
 // END externally visible functions
 ////////////////////////////////////////////////////////////////////////
 
+
--- a/tools/profiler/platform.h
+++ b/tools/profiler/platform.h
@@ -38,16 +38,17 @@
 #define __android_log_print(a, ...)
 #endif
 
 #include "mozilla/StandardInteger.h"
 #include "mozilla/Util.h"
 #include "mozilla/unused.h"
 #include "mozilla/TimeStamp.h"
 #include "PlatformMacros.h"
+#include "mozilla/Mutex.h"
 #include "v8-support.h"
 #include <vector>
 #define ASSERT(a) MOZ_ASSERT(a)
 #ifdef ANDROID
 #if defined(__arm__) || defined(__thumb__)
 #define ENABLE_SPS_LEAF_DATA
 #define ENABLE_ARM_LR_SAVING
 #endif
@@ -210,16 +211,19 @@ class Thread {
 
 // ----------------------------------------------------------------------------
 // Sampler
 //
 // A sampler periodically samples the state of the VM and optionally
 // (if used for profiling) the program counter and stack pointer for
 // the thread that created it.
 
+class PseudoStack;
+class ThreadProfile;
+
 // TickSample captures the information collected for each sample.
 class TickSample {
  public:
   TickSample()
       :
         pc(NULL),
         sp(NULL),
         fp(NULL),
@@ -233,26 +237,54 @@ class TickSample {
   Address sp;  // Stack pointer.
   Address fp;  // Frame pointer.
 #ifdef ENABLE_ARM_LR_SAVING
   Address lr;  // ARM link register
 #endif
   Address function;  // The last called JS function.
   void*   context;   // The context from the signal handler, if available. On
                      // Win32 this may contain the windows thread context.
+  ThreadProfile* threadProfile;
   static const int kMaxFramesCount = 64;
-  Address stack[kMaxFramesCount];  // Call stack.
   int frames_count;  // Number of captured frames.
   mozilla::TimeStamp timestamp;
 };
 
+class ThreadInfo {
+ public:
+  ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack)
+    : mName(strdup(aName))
+    , mThreadId(aThreadId)
+    , mIsMainThread(aIsMainThread)
+    , mPseudoStack(aPseudoStack)
+    , mProfile(NULL) {}
+
+  virtual ~ThreadInfo();
+
+  const char* Name() const { return mName; }
+  int ThreadId() const { return mThreadId; }
+
+  bool IsMainThread() const { return mIsMainThread; }
+  PseudoStack* Stack() const { return mPseudoStack; }
+  
+  void SetProfile(ThreadProfile* aProfile) { mProfile = aProfile; }
+  ThreadProfile* Profile() const { return mProfile; }
+
+ private:
+  char* mName;
+  int mThreadId;
+  const bool mIsMainThread;
+  PseudoStack* mPseudoStack;
+  ThreadProfile* mProfile;
+};
+
 class Sampler {
  public:
   // Initialize sampler.
-  explicit Sampler(int interval, bool profiling);
+  explicit Sampler(int interval, bool profiling, int entrySize);
   virtual ~Sampler();
 
   int interval() const { return interval_; }
 
   // Performs stack sampling.
   virtual void SampleStack(TickSample* sample) = 0;
 
   // This method is called for each sampling period with the current
@@ -273,32 +305,60 @@ class Sampler {
 
   // Whether the sampler is running (that is, consumes resources).
   bool IsActive() const { return active_; }
 
   // Low overhead way to stop the sampler from ticking
   bool IsPaused() const { return paused_; }
   void SetPaused(bool value) { NoBarrier_Store(&paused_, value); }
 
+  int EntrySize() { return entrySize_; }
+
   class PlatformData;
 
   PlatformData* platform_data() { return data_; }
 
   // If we move the backtracing code into the platform files we won't
   // need to have these hacks
 #ifdef XP_WIN
   // xxxehsan sucky hack :(
   static uintptr_t GetThreadHandle(PlatformData*);
 #endif
 #ifdef XP_MACOSX
   static pthread_t GetProfiledThread(PlatformData*);
 #endif
+
+  static std::vector<ThreadInfo*> GetRegisteredThreads() {
+    mozilla::MutexAutoLock lock(sRegisteredThreadsMutex);
+
+    return sRegisteredThreads;
+  }
+
+  static bool RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread);
+  static void UnregisterCurrentThread();
+
+  // Should only be called on shutdown
+  static void ClearRegisteredThreads() {
+    while (sRegisteredThreads.size() > 0) {
+      sRegisteredThreads.pop_back();
+    }
+  }
+
+  static Sampler* GetActiveSampler() { return sActiveSampler; }
+  static void SetActiveSampler(Sampler* sampler) { sActiveSampler = sampler; }
+
+ protected:
+  static std::vector<ThreadInfo*> sRegisteredThreads;
+  static mozilla::Mutex sRegisteredThreadsMutex;
+  static Sampler* sActiveSampler;
+
  private:
   void SetActive(bool value) { NoBarrier_Store(&active_, value); }
 
   const int interval_;
   const bool profiling_;
   Atomic32 paused_;
   Atomic32 active_;
+  const int entrySize_;
   PlatformData* data_;  // Platform specific data.
 };
 
 #endif /* ndef TOOLS_PLATFORM_H_ */