Bug 734691 - Add multi-thread support to profiler r=benwa
☠☠ backed out by ca1887b25c95 ☠ ☠
authorJames Willcox <snorp@snorp.net>
Fri, 29 Mar 2013 15:34:49 -0400
changeset 134850 025d24d7fee881901cfe4307e5cd2aca90a9c11a
parent 134849 434c6e6948135ece3d1c81c393f7d381114ff7fc
child 134851 ab64b048ad04f74a4d4fc1bec56fcc85c59ee93c
push idunknown
push userunknown
push dateunknown
reviewersbenwa
bugs734691
milestone23.0a1
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;
@@ -510,16 +512,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
@@ -538,16 +542,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
@@ -153,45 +153,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;
@@ -288,17 +295,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->at(i)->Profile()->GetMutex());
+
+      JSCustomObject* threadSamples = b.CreateObject();
+      sRegisteredThreads->at(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;
@@ -22,48 +23,97 @@ 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->at(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++) {
+        ThreadInfo* info = sRegisteredThreads->at(i);
+        ThreadProfile* profile = info->Profile();
+        if (profile) {
+          delete profile;
+          info->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++) {
+        ThreadInfo* info = sRegisteredThreads->at(i);
+        if (info->IsMainThread()) {
+          mPrimaryThreadProfile = info->Profile();
+          break;
+        }
+      }
+    }
+
+    return mPrimaryThreadProfile;
   }
 
   void ToStreamAsJSON(std::ostream& stream);
   virtual JSObject *ToJSObject(JSContext *aCx);
   JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b);
 
   const bool ProfileJS() { return mProfileJS; }
 
@@ -71,17 +121,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,51 @@ 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++) {
+    ThreadInfo* info = sRegisteredThreads->at(i);
+    if (info->ThreadId() == id) {
+      delete info;
+      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 +363,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,20 @@
 // 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 +63,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 +116,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 +152,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 +236,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
+}
\ No newline at end of file
--- 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;
@@ -30,24 +31,36 @@ bool stack_key_initialized;
 TimeStamp sLastTracerEvent; // is raced on
 int       sFrameNumber = 0;
 int       sLastFrameNumber = 0;
 
 /* used to keep track of the last event that we sampled during */
 unsigned int sLastSampledEventGeneration = 0;
 
 /* a counter that's incremented everytime we get responsiveness event
- * note: it might also be worth tracking everytime we go around
+ * note: it might also be worth trackplaing 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 = new std::vector<ThreadInfo*>();
+mozilla::Mutex* Sampler::sRegisteredThreadsMutex = new mozilla::Mutex("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)
@@ -288,16 +301,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::FreeRegisteredThreads();
+
   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()
 {
@@ -482,11 +497,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
@@ -227,16 +227,19 @@ extern int     sUnwindStackScan;  /* max
 
 // ----------------------------------------------------------------------------
 // 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),
@@ -250,26 +253,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
@@ -290,32 +321,63 @@ 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 FreeRegisteredThreads() {
+    while (sRegisteredThreads->size() > 0) {
+      sRegisteredThreads->pop_back();
+    }
+
+    delete sRegisteredThreadsMutex;
+    delete sRegisteredThreads;
+  }
+
+  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_ */