Bug 1021990 - Migrate to xpcom based EventTracer. r=ehsan
authorBenoit Girard <b56girard@gmail.com>
Fri, 06 Jun 2014 17:53:42 -0400
changeset 188740 14950c172444160c21691193fddd2599b05ce097
parent 188739 3601d3753bd296196dcb3da32f0853397cc08102
child 188741 ac74a1815c6676bfdc338e0241ea086fcfb0cb22
push id44897
push userb56girard@gmail.com
push dateSat, 14 Jun 2014 02:33:32 +0000
treeherdermozilla-inbound@14950c172444 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1021990
milestone33.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 1021990 - Migrate to xpcom based EventTracer. r=ehsan
browser/devtools/profiler/test/browser_profiler_io.js
toolkit/devtools/server/tests/unit/test_profiler_actor.js
toolkit/xre/nsAppRunner.cpp
tools/profiler/ProfileEntry.cpp
tools/profiler/ProfileEntry.h
tools/profiler/SyncProfile.cpp
tools/profiler/SyncProfile.h
tools/profiler/TableTicker.cpp
tools/profiler/TableTicker.h
tools/profiler/ThreadResponsiveness.cpp
tools/profiler/ThreadResponsiveness.h
tools/profiler/moz.build
tools/profiler/nsProfiler.cpp
tools/profiler/platform-linux.cc
tools/profiler/platform-macos.cc
tools/profiler/platform-win32.cc
tools/profiler/platform.cpp
tools/profiler/platform.h
tools/profiler/tests/gtest/ThreadProfileTest.cpp
xpcom/glue/MainThreadUtils.h
--- a/browser/devtools/profiler/test/browser_profiler_io.js
+++ b/browser/devtools/profiler/test/browser_profiler_io.js
@@ -72,9 +72,9 @@ function checkData() {
   let data = profile.data;
 
   is(item.attachment.state, PROFILE_COMPLETED, "Profile is COMPLETED");
   is(gData.meta.oscpu, data.meta.oscpu, "Meta data is correct");
   is(gData.threads[0].samples.length, 1, "There's one sample");
   is(gData.threads[0].samples[0].name, "(root)", "Sample is correct");
 
   tearDown(gTab, () => { gPanel = null; gTab = null; });
-}
\ No newline at end of file
+}
--- a/toolkit/devtools/server/tests/unit/test_profiler_actor.js
+++ b/toolkit/devtools/server/tests/unit/test_profiler_actor.js
@@ -27,17 +27,17 @@ function test_profiler_actor(aClient, aP
     do_check_false(aResponse.isActive);
 
     aClient.request({ to: aProfiler, type: "getFeatures" }, function (aResponse) {
       var features = Profiler.GetFeatures([]);
       do_check_eq(aResponse.features.length, features.length);
       for (var i = 0; i < features.length; i++)
         do_check_eq(aResponse.features[i], features[i]);
 
-      aClient.request({ to: aProfiler, type: "startProfiler", features: ['jank', 'js'] }, function (aResponse) {
+      aClient.request({ to: aProfiler, type: "startProfiler", features: ['js'] }, function (aResponse) {
         do_check_eq(typeof aResponse.msg, "string");
         aClient.request({ to: aProfiler, type: "isActive" }, function (aResponse) {
           do_check_true(aResponse.isActive);
 
           aClient.request({ to: aProfiler, type: "getSharedLibraryInformation" }, function (aResponse) {
             do_check_eq(typeof aResponse.sharedLibraryInformation, "string");
             try {
               JSON.parse(aResponse.sharedLibraryInformation);
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3997,18 +3997,18 @@ XREMain::XRE_mainRun()
     if (mRemoteService)
       mRemoteService->Startup(mAppData->name, mProfileName.get());
 #endif /* MOZ_ENABLE_XREMOTE */
 
     mNativeApp->Enable();
   }
 
 #ifdef MOZ_INSTRUMENT_EVENT_LOOP
-  if (PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP") || profiler_is_active()) {
-    bool logToConsole = !!PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP");
+  if (PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP")) {
+    bool logToConsole = true;
     mozilla::InitEventTracing(logToConsole);
   }
 #endif /* MOZ_INSTRUMENT_EVENT_LOOP */
 
   {
     rv = appStartup->Run();
     if (NS_FAILED(rv)) {
       NS_ERROR("failed to run appstartup");
--- a/tools/profiler/ProfileEntry.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -138,44 +138,43 @@ std::ostream& operator<<(std::ostream& s
 ////////////////////////////////////////////////////////////////////////
 
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN ThreadProfile
 
 #define DYNAMIC_MAX_STRING 512
 
-ThreadProfile::ThreadProfile(const char* aName, int aEntrySize,
-                             PseudoStack *aStack, Thread::tid_t aThreadId,
-                             PlatformData* aPlatform,
-                             bool aIsMainThread, void *aStackTop)
-  : mWritePos(0)
+ThreadProfile::ThreadProfile(ThreadInfo* aInfo, int aEntrySize)
+  : mThreadInfo(aInfo)
+  , mWritePos(0)
   , mLastFlushPos(0)
   , mReadPos(0)
   , mEntrySize(aEntrySize)
-  , mPseudoStack(aStack)
+  , mPseudoStack(aInfo->Stack())
   , mMutex("ThreadProfile::mMutex")
-  , mName(strdup(aName))
-  , mThreadId(aThreadId)
-  , mIsMainThread(aIsMainThread)
-  , mPlatformData(aPlatform)
+  , mThreadId(aInfo->ThreadId())
+  , mIsMainThread(aInfo->IsMainThread())
+  , mPlatformData(aInfo->GetPlatformData())
   , mGeneration(0)
   , mPendingGenerationFlush(0)
-  , mStackTop(aStackTop)
+  , mStackTop(aInfo->StackTop())
+  , mRespInfo(MOZ_THIS_IN_INITIALIZER_LIST())
 #ifdef XP_LINUX
   , mRssMemory(0)
   , mUssMemory(0)
 #endif
 {
+  MOZ_COUNT_CTOR(ThreadProfile);
   mEntries = new ProfileEntry[mEntrySize];
 }
 
 ThreadProfile::~ThreadProfile()
 {
-  free(mName);
+  MOZ_COUNT_DTOR(ThreadProfile);
   delete[] mEntries;
 }
 
 void ThreadProfile::addTag(ProfileEntry aTag)
 {
   // Called from signal, call only reentrant functions
   mEntries[mWritePos] = aTag;
   mWritePos = mWritePos + 1;
@@ -320,17 +319,17 @@ void ThreadProfile::ToStreamAsJSON(std::
 void ThreadProfile::StreamJSObject(JSStreamWriter& b)
 {
   b.BeginObject();
     // Thread meta data
     if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
       // TODO Add the proper plugin name
       b.NameValue("name", "Plugin");
     } else {
-      b.NameValue("name", mName);
+      b.NameValue("name", Name());
     }
     b.NameValue("tid", static_cast<int>(mThreadId));
 
     b.Name("samples");
     b.BeginArray();
 
       bool sample = false;
       int readPos = mReadPos;
--- a/tools/profiler/ProfileEntry.h
+++ b/tools/profiler/ProfileEntry.h
@@ -69,19 +69,17 @@ private:
 
 #pragma pack(pop)
 
 typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagStringData);
 
 class ThreadProfile
 {
 public:
-  ThreadProfile(const char* aName, int aEntrySize, PseudoStack *aStack,
-                Thread::tid_t aThreadId, PlatformData* aPlatformData,
-                bool aIsMainThread, void *aStackTop);
+  ThreadProfile(ThreadInfo* aThreadInfo, int aEntrySize);
   virtual ~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);
@@ -90,48 +88,52 @@ public:
   PseudoStack* GetPseudoStack();
   mozilla::Mutex* GetMutex();
   void StreamJSObject(JSStreamWriter& b);
   void BeginUnwind();
   virtual void EndUnwind();
   virtual SyncProfile* AsSyncProfile() { return nullptr; }
 
   bool IsMainThread() const { return mIsMainThread; }
-  const char* Name() const { return mName; }
+  const char* Name() const { return mThreadInfo->Name(); }
   Thread::tid_t ThreadId() const { return mThreadId; }
 
-  PlatformData* GetPlatformData() { return mPlatformData; }
+  PlatformData* GetPlatformData() const { return mPlatformData; }
   int GetGenerationID() const { return mGeneration; }
-  bool HasGenerationExpired(int aGenID) {
+  bool HasGenerationExpired(int aGenID) const {
     return aGenID + 2 <= mGeneration;
   }
   void* GetStackTop() const { return mStackTop; }
   void DuplicateLastSample();
+
+  ThreadInfo* GetThreadInfo() const { return mThreadInfo; }
+  ThreadResponsiveness* GetThreadResponsiveness() { return &mRespInfo; }
 private:
   FRIEND_TEST(ThreadProfile, InsertOneTag);
   FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
   FRIEND_TEST(ThreadProfile, InsertTagsNoWrap);
   FRIEND_TEST(ThreadProfile, InsertTagsWrap);
   FRIEND_TEST(ThreadProfile, MemoryMeasure);
+  ThreadInfo* mThreadInfo;
   // 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;
   Thread::tid_t  mThreadId;
   bool           mIsMainThread;
   PlatformData*  mPlatformData;  // Platform specific data.
   int            mGeneration;
   int            mPendingGenerationFlush;
   void* const    mStackTop;
+  ThreadResponsiveness mRespInfo;
 
   // Only Linux is using a signal sender, instead of stopping the thread, so we
   // need some space to store the data which cannot be collected in the signal
   // handler code.
 #ifdef XP_LINUX
 public:
   int64_t        mRssMemory;
   int64_t        mUssMemory;
--- a/tools/profiler/SyncProfile.cpp
+++ b/tools/profiler/SyncProfile.cpp
@@ -2,28 +2,27 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "SyncProfile.h"
 #include "UnwinderThread2.h"
 
-SyncProfile::SyncProfile(const char* aName, int aEntrySize, PseudoStack *aStack,
-                         Thread::tid_t aThreadId, bool aIsMainThread)
-  : ThreadProfile(aName, aEntrySize, aStack, aThreadId,
-                 Sampler::AllocPlatformData(aThreadId), aIsMainThread,
-                 tlsStackTop.get()),
-    mOwnerState(REFERENCED),
-    mUtb(nullptr)
+SyncProfile::SyncProfile(ThreadInfo* aInfo, int aEntrySize)
+  : ThreadProfile(aInfo, aEntrySize)
+  , mOwnerState(REFERENCED)
+  , mUtb(nullptr)
 {
+  MOZ_COUNT_CTOR(SyncProfile);
 }
 
 SyncProfile::~SyncProfile()
 {
+  MOZ_COUNT_DTOR(SyncProfile);
   if (mUtb) {
     utb__release_sync_buffer(mUtb);
   }
   Sampler::FreePlatformData(GetPlatformData());
 }
 
 bool
 SyncProfile::SetUWTBuffer(LinkedUWTBuffer* aBuff)
--- a/tools/profiler/SyncProfile.h
+++ b/tools/profiler/SyncProfile.h
@@ -9,18 +9,17 @@
 
 #include "ProfileEntry.h"
 
 struct LinkedUWTBuffer;
 
 class SyncProfile : public ThreadProfile
 {
 public:
-  SyncProfile(const char* aName, int aEntrySize, PseudoStack *aStack,
-              Thread::tid_t aThreadId, bool aIsMainThread);
+  SyncProfile(ThreadInfo* aInfo, int aEntrySize);
   ~SyncProfile();
 
   bool SetUWTBuffer(LinkedUWTBuffer* aBuff);
   LinkedUWTBuffer* GetUWTBuffer() { return mUtb; }
 
   virtual void EndUnwind();
   virtual SyncProfile* AsSyncProfile() { return this; }
 
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -652,18 +652,18 @@ void TableTicker::InplaceTick(TickSample
   }
 #else
   doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr);
 #endif
 
   if (recordSample)
     currThreadProfile.flush();
 
-  if (!sLastTracerEvent.IsNull() && sample && currThreadProfile.IsMainThread()) {
-    TimeDuration delta = sample->timestamp - sLastTracerEvent;
+  if (sample && currThreadProfile.GetThreadResponsiveness()->HasData()) {
+    TimeDuration delta = currThreadProfile.GetThreadResponsiveness()->GetUnresponsiveDuration(sample->timestamp);
     currThreadProfile.addTag(ProfileEntry('r', static_cast<float>(delta.ToMilliseconds())));
   }
 
   if (sample) {
     TimeDuration delta = sample->timestamp - sStartTime;
     currThreadProfile.addTag(ProfileEntry('t', static_cast<float>(delta.ToMilliseconds())));
   }
 
@@ -695,19 +695,18 @@ SyncProfile* NewSyncProfile()
 {
   PseudoStack* stack = tlsPseudoStack.get();
   if (!stack) {
     MOZ_ASSERT(stack);
     return nullptr;
   }
   Thread::tid_t tid = Thread::GetCurrentId();
 
-  SyncProfile* profile = new SyncProfile("SyncProfile",
-                                         GET_BACKTRACE_DEFAULT_ENTRY,
-                                         stack, tid, NS_IsMainThread());
+  ThreadInfo* info = new ThreadInfo("SyncProfile", tid, NS_IsMainThread(), stack, nullptr);
+  SyncProfile* profile = new SyncProfile(info, GET_BACKTRACE_DEFAULT_ENTRY);
   return profile;
 }
 
 } // anonymous namespace
 
 SyncProfile* TableTicker::GetBacktrace()
 {
   SyncProfile* profile = NewSyncProfile();
@@ -760,12 +759,14 @@ void mozilla_sampler_print_location1()
   }
 
   syncProfile->BeginUnwind();
   doSampleStackTrace(syncProfile->GetPseudoStack(), *syncProfile, nullptr);
   syncProfile->EndUnwind();
 
   printf_stderr("Backtrace:\n");
   syncProfile->IterateTags(print_callback);
+  ThreadInfo* info = syncProfile->GetThreadInfo();
   delete syncProfile;
+  delete info;
 }
 
 
--- a/tools/profiler/TableTicker.h
+++ b/tools/profiler/TableTicker.h
@@ -131,23 +131,17 @@ class TableTicker: public Sampler {
     if (!aInfo->IsMainThread() && !mProfileThreads) {
       return;
     }
 
     if (!threadSelected(aInfo, mThreadNameFilters, mFilterCount)) {
       return;
     }
 
-    ThreadProfile* profile = new ThreadProfile(aInfo->Name(),
-                                               EntrySize(),
-                                               aInfo->Stack(),
-                                               aInfo->ThreadId(),
-                                               aInfo->GetPlatformData(),
-                                               aInfo->IsMainThread(),
-                                               aInfo->StackTop());
+    ThreadProfile* profile = new ThreadProfile(aInfo, EntrySize());
     aInfo->SetProfile(profile);
   }
 
   // Called within a signal. This function must be reentrant
   virtual void Tick(TickSample* sample);
 
   // Immediately captures the calling thread's call stack and returns it.
   virtual SyncProfile* GetBacktrace();
new file mode 100644
--- /dev/null
+++ b/tools/profiler/ThreadResponsiveness.cpp
@@ -0,0 +1,114 @@
+/* -*- 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 "ThreadResponsiveness.h"
+#include "platform.h"
+#include "nsComponentManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsITimer.h"
+#include "mozilla/Monitor.h"
+#include "ProfileEntry.h"
+
+using mozilla::Monitor;
+using mozilla::MonitorAutoLock;
+
+class CheckResponsivenessTask : public nsRunnable,
+                                public nsITimerCallback {
+public:
+  CheckResponsivenessTask()
+    : mLastTracerTime(TimeStamp::Now())
+    , mMonitor("CheckResponsivenessTask")
+    , mTimer(nullptr)
+    , mStop(false)
+  {
+    MOZ_COUNT_CTOR(CheckResponsivenessTask);
+  }
+
+  ~CheckResponsivenessTask()
+  {
+    MOZ_COUNT_DTOR(CheckResponsivenessTask);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MonitorAutoLock mon(mMonitor);
+    if (mStop)
+      return NS_OK;
+
+    // This is raced on because we might pause the thread here
+    // for profiling so if we tried to use a monitor to protect
+    // mLastTracerTime we could deadlock. We're risking seeing
+    // a partial write which will show up as an outlier in our
+    // performance data.
+    mLastTracerTime = TimeStamp::Now();
+    if (!mTimer) {
+      mTimer = do_CreateInstance("@mozilla.org/timer;1");
+    }
+    mTimer->InitWithCallback(this, 16, nsITimer::TYPE_ONE_SHOT);
+
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL
+  {
+    NS_DispatchToMainThread(this);
+    return NS_OK;
+  }
+
+  void Terminate() {
+    MonitorAutoLock mon(mMonitor);
+    mStop = true;
+  }
+
+  const TimeStamp& GetLastTracerTime() const {
+    return mLastTracerTime;
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  TimeStamp mLastTracerTime;
+  Monitor mMonitor;
+  nsCOMPtr<nsITimer> mTimer;
+  bool mStop;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(CheckResponsivenessTask, nsRunnable, nsITimerCallback)
+
+ThreadResponsiveness::ThreadResponsiveness(ThreadProfile *aThreadProfile)
+  : mThreadProfile(aThreadProfile)
+  , mActiveTracerEvent(nullptr)
+{
+  MOZ_COUNT_CTOR(ThreadResponsiveness);
+}
+
+ThreadResponsiveness::~ThreadResponsiveness()
+{
+  MOZ_COUNT_DTOR(ThreadResponsiveness);
+  if (mActiveTracerEvent) {
+    mActiveTracerEvent->Terminate();
+  }
+}
+
+void
+ThreadResponsiveness::Update()
+{
+  return;
+  if (!mActiveTracerEvent) {
+    if (mThreadProfile->GetThreadInfo()->IsMainThread()) {
+      mActiveTracerEvent = new CheckResponsivenessTask();
+      NS_DispatchToMainThread(mActiveTracerEvent);
+    } else if (mThreadProfile->GetThreadInfo()->GetThread()) {
+      mActiveTracerEvent = new CheckResponsivenessTask();
+      mThreadProfile->GetThreadInfo()->
+        GetThread()->Dispatch(mActiveTracerEvent, NS_DISPATCH_NORMAL);
+    }
+  }
+
+  if (mActiveTracerEvent) {
+    mLastTracerTime = mActiveTracerEvent->GetLastTracerTime();
+  }
+}
+
new file mode 100644
--- /dev/null
+++ b/tools/profiler/ThreadResponsiveness.h
@@ -0,0 +1,38 @@
+/* -*- 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 ThreadResponsiveness_h
+#define ThreadResponsiveness_h
+
+#include "nsAutoPtr.h"
+#include "nsISupports.h"
+#include "mozilla/TimeStamp.h"
+
+class ThreadProfile;
+class CheckResponsivenessTask;
+
+class ThreadResponsiveness {
+public:
+  explicit ThreadResponsiveness(ThreadProfile *aThreadProfile);
+
+  ~ThreadResponsiveness();
+
+  void Update();
+
+  mozilla::TimeDuration GetUnresponsiveDuration(const mozilla::TimeStamp& now) const {
+    return now - mLastTracerTime;
+  }
+
+  bool HasData() const {
+    return !mLastTracerTime.IsNull();
+  }
+private:
+  ThreadProfile* mThreadProfile;
+  nsRefPtr<CheckResponsivenessTask> mActiveTracerEvent;
+  mozilla::TimeStamp mLastTracerTime;
+};
+
+#endif
+
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -30,16 +30,17 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
         'platform.cpp',
         'ProfileEntry.cpp',
         'ProfilerBacktrace.cpp',
         'ProfilerIOInterposeObserver.cpp',
         'ProfilerMarkers.cpp',
         'SaveProfileTask.cpp',
         'SyncProfile.cpp',
         'TableTicker.cpp',
+        'ThreadResponsiveness.cpp',
         'UnwinderThread2.cpp',
     ]
 
     # This file cannot be built in unified mode because of name clashes with mozglue headers on Android.
     SOURCES += [
         'local_debug_info_symbolizer.cc',
     ]
 
--- a/tools/profiler/nsProfiler.cpp
+++ b/tools/profiler/nsProfiler.cpp
@@ -1,18 +1,15 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 <string>
 #include <sstream>
-#ifdef MOZ_INSTRUMENT_EVENT_LOOP
-#include "EventTracer.h"
-#endif
 #include "GeckoProfiler.h"
 #include "nsProfiler.h"
 #include "nsMemory.h"
 #include "nsString.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsILoadContext.h"
@@ -76,20 +73,16 @@ nsProfiler::StartProfiler(uint32_t aEntr
 {
   if (mLockedForPrivateBrowsing) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   profiler_start(aEntries, aInterval,
                  aFeatures, aFeatureCount,
                  aThreadNameFilters, aFilterCount);
-#ifdef MOZ_INSTRUMENT_EVENT_LOOP
-  bool printToConsole = false;
-  mozilla::InitEventTracing(printToConsole);
-#endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::StopProfiler()
 {
   profiler_stop();
   return NS_OK;
--- a/tools/profiler/platform-linux.cc
+++ b/tools/profiler/platform-linux.cc
@@ -65,16 +65,17 @@
 #include <stdarg.h>
 #include "platform.h"
 #include "GeckoProfiler.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Atomics.h"
 #include "ProfileEntry.h"
 #include "nsThreadUtils.h"
 #include "TableTicker.h"
+#include "ThreadResponsiveness.h"
 #include "UnwinderThread2.h"
 #if defined(__ARM_EABI__) && defined(MOZ_WIDGET_GONK)
  // Should also work on other Android and ARM Linux, but not tested there yet.
 #define USE_EHABI_STACKWALK
 #include "EHABIStackWalk.h"
 #endif
 
 // Memory profile
@@ -312,16 +313,18 @@ static void* SignalSender(void* arg) {
         PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
         if (sleeping == PseudoStack::SLEEPING_AGAIN) {
           info->Profile()->DuplicateLastSample();
           //XXX: This causes flushes regardless of jank-only mode
           info->Profile()->flush();
           continue;
         }
 
+        info->Profile()->GetThreadResponsiveness()->Update();
+
         // We use sCurrentThreadProfile the ThreadProfile for the
         // thread we're profiling to the signal handler
         sCurrentThreadProfile = info->Profile();
 
         int threadId = info->ThreadId();
 
         // Profile from the signal sender for information which is not signal
         // safe, and will have low variation between the emission of the signal
--- a/tools/profiler/platform-macos.cc
+++ b/tools/profiler/platform-macos.cc
@@ -24,16 +24,17 @@
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <math.h>
 
+#include "ThreadResponsiveness.h"
 #include "nsThreadUtils.h"
 
 #include "platform.h"
 #include "TableTicker.h"
 #include "UnwinderThread2.h"  /* uwt__register_thread_for_profiling */
 
 // Memory profile
 #include "nsMemoryReporterManager.h"
@@ -216,16 +217,18 @@ class SamplerThread : public Thread {
           PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
           if (sleeping == PseudoStack::SLEEPING_AGAIN) {
             info->Profile()->DuplicateLastSample();
             //XXX: This causes flushes regardless of jank-only mode
             info->Profile()->flush();
             continue;
           }
 
+          info->Profile()->GetThreadResponsiveness()->Update();
+
           ThreadProfile* thread_profile = info->Profile();
 
           SampleContext(SamplerRegistry::sampler, thread_profile,
                         isFirstProfiledThread);
           isFirstProfiledThread = false;
         }
       }
       OS::SleepMicro(intervalMicro_);
--- a/tools/profiler/platform-win32.cc
+++ b/tools/profiler/platform-win32.cc
@@ -26,16 +26,17 @@
 // 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 "TableTicker.h"
+#include "ThreadResponsiveness.h"
 #include "ProfileEntry.h"
 #include "UnwinderThread2.h"
 
 // Memory profile
 #include "nsMemoryReporterManager.h"
 
 class PlatformData : public Malloced {
  public:
@@ -134,16 +135,18 @@ class SamplerThread : public Thread {
           PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
           if (sleeping == PseudoStack::SLEEPING_AGAIN) {
             info->Profile()->DuplicateLastSample();
             //XXX: This causes flushes regardless of jank-only mode
             info->Profile()->flush();
             continue;
           }
 
+          info->Profile()->GetThreadResponsiveness()->Update();
+
           ThreadProfile* thread_profile = info->Profile();
 
           SampleContext(sampler_, thread_profile, isFirstProfiledThread);
           isFirstProfiledThread = false;
         }
       }
       OS::Sleep(interval_);
     }
--- a/tools/profiler/platform.cpp
+++ b/tools/profiler/platform.cpp
@@ -94,16 +94,30 @@ void Sampler::Shutdown() {
   delete sRegisteredThreads;
 
   // UnregisterThread can be called after shutdown in XPCShell. Thus
   // we need to point to null to ignore such a call after shutdown.
   sRegisteredThreadsMutex = nullptr;
   sRegisteredThreads = nullptr;
 }
 
+ThreadInfo::ThreadInfo(const char* aName, int aThreadId,
+                       bool aIsMainThread, PseudoStack* aPseudoStack,
+                       void* aStackTop)
+  : mName(strdup(aName))
+  , mThreadId(aThreadId)
+  , mIsMainThread(aIsMainThread)
+  , mPseudoStack(aPseudoStack)
+  , mPlatformData(Sampler::AllocPlatformData(aThreadId))
+  , mProfile(nullptr)
+  , mStackTop(aStackTop)
+{
+  mThread = NS_GetCurrentThread();
+}
+
 ThreadInfo::~ThreadInfo() {
   free(mName);
 
   if (mProfile)
     delete mProfile;
 
   Sampler::FreePlatformData(mPlatformData);
 }
--- a/tools/profiler/platform.h
+++ b/tools/profiler/platform.h
@@ -36,21 +36,22 @@
 #endif
 
 #ifdef XP_UNIX
 #include <pthread.h>
 #endif
 
 #include <stdint.h>
 #include <math.h>
+#include "MainThreadUtils.h"
 #include "mozilla/unused.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Mutex.h"
-#include "MainThreadUtils.h"
 #include "PlatformMacros.h"
+#include "ThreadResponsiveness.h"
 #include "v8-support.h"
 #include <vector>
 
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 #define ASSERT(a) MOZ_ASSERT(a)
@@ -387,41 +388,40 @@ class Sampler {
   struct sigaction old_sigsave_signal_handler_;
   bool signal_sender_launched_;
   pthread_t signal_sender_thread_;
 #endif
 };
 
 class ThreadInfo {
  public:
-  ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack, void* aStackTop)
-    : mName(strdup(aName))
-    , mThreadId(aThreadId)
-    , mIsMainThread(aIsMainThread)
-    , mPseudoStack(aPseudoStack)
-    , mPlatformData(Sampler::AllocPlatformData(aThreadId))
-    , mProfile(NULL)
-    , mStackTop(aStackTop) {}
+  ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack, void* aStackTop);
 
   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; }
 
   PlatformData* GetPlatformData() const { return mPlatformData; }
   void* StackTop() const { return mStackTop; }
+
+  /**
+   * May be null for the main thread if the profiler was started during startup
+   */
+  nsIThread* GetThread() const { return mThread.get(); }
  private:
   char* mName;
   int mThreadId;
   const bool mIsMainThread;
   PseudoStack* mPseudoStack;
   PlatformData* mPlatformData;
   ThreadProfile* mProfile;
   void* const mStackTop;
+  nsCOMPtr<nsIThread> mThread;
 };
 
 #endif /* ndef TOOLS_PLATFORM_H_ */
--- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp
+++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp
@@ -6,35 +6,38 @@
 #include "gtest/gtest.h"
 
 #include "ProfileEntry.h"
 
 // Make sure we can initialize our ThreadProfile
 TEST(ThreadProfile, Initialization) {
   PseudoStack stack;
   Thread::tid_t tid = 1000;
-  ThreadProfile tp("testThread", 10, &stack, tid, nullptr, true, nullptr);
+  ThreadInfo info("testThread", tid, true, &stack, nullptr);
+  ThreadProfile tp(&info, 10);
 }
 
 // Make sure we can record one tag and read it
 TEST(ThreadProfile, InsertOneTag) {
   PseudoStack stack;
   Thread::tid_t tid = 1000;
-  ThreadProfile tp("testThread", 10, &stack, tid, nullptr, true, nullptr);
+  ThreadInfo info("testThread", tid, true, &stack, nullptr);
+  ThreadProfile tp(&info, 10);
   tp.addTag(ProfileEntry('t', 123.1f));
   ASSERT_TRUE(tp.mEntries != nullptr);
   ASSERT_TRUE(tp.mEntries[tp.mReadPos].mTagName == 't');
   ASSERT_TRUE(tp.mEntries[tp.mReadPos].mTagFloat == 123.1f);
 }
 
 // See if we can insert some tags
 TEST(ThreadProfile, InsertTagsNoWrap) {
   PseudoStack stack;
   Thread::tid_t tid = 1000;
-  ThreadProfile tp("testThread", 100, &stack, tid, nullptr, true, nullptr);
+  ThreadInfo info("testThread", tid, true, &stack, nullptr);
+  ThreadProfile tp(&info, 100);
   int test_size = 50;
   for (int i = 0; i < test_size; i++) {
     tp.addTag(ProfileEntry('t', i));
   }
   ASSERT_TRUE(tp.mEntries != nullptr);
   int readPos = tp.mReadPos;
   while (readPos != tp.mWritePos) {
     ASSERT_TRUE(tp.mEntries[readPos].mTagName == 't');
@@ -45,17 +48,18 @@ TEST(ThreadProfile, InsertTagsNoWrap) {
 
 // See if wrapping works as it should in the basic case
 TEST(ThreadProfile, InsertTagsWrap) {
   PseudoStack stack;
   Thread::tid_t tid = 1000;
   // we can fit only 24 tags in this buffer because of the empty slot
   int tags = 24;
   int buffer_size = tags + 1;
-  ThreadProfile tp("testThread", buffer_size, &stack, tid, nullptr, true, nullptr);
+  ThreadInfo info("testThread", tid, true, &stack, nullptr);
+  ThreadProfile tp(&info, buffer_size);
   int test_size = 43;
   for (int i = 0; i < test_size; i++) {
     tp.addTag(ProfileEntry('t', i));
   }
   ASSERT_TRUE(tp.mEntries != nullptr);
   int readPos = tp.mReadPos;
   int ctr = 0;
   while (readPos != tp.mWritePos) {
--- a/xpcom/glue/MainThreadUtils.h
+++ b/xpcom/glue/MainThreadUtils.h
@@ -16,16 +16,24 @@ class nsIThread;
  * Get a reference to the main thread.
  *
  * @param result
  *   The resulting nsIThread object.
  */
 extern NS_COM_GLUE NS_METHOD
 NS_GetMainThread(nsIThread **result);
 
+#ifdef MOZILLA_INTERNAL_API
+// Fast access to the current thread.  Do not release the returned pointer!  If
+// you want to use this pointer from some other thread, then you will need to
+// AddRef it.  Otherwise, you should only consider this pointer valid from code
+// running on the current thread.
+extern NS_COM_GLUE nsIThread *NS_GetCurrentThread();
+#endif
+
 #if defined(MOZILLA_INTERNAL_API) && defined(XP_WIN)
 bool NS_IsMainThread();
 #elif defined(MOZILLA_INTERNAL_API) && defined(NS_TLS)
 // This is defined in nsThreadManager.cpp and initialized to `Main` for the
 // main thread by nsThreadManager::Init.
 extern NS_TLS mozilla::threads::ID gTLSThreadID;
 #ifdef MOZ_ASAN
 // Temporary workaround, see bug 895845