Bug 1024669 - Part 1: Annotate crash reports with thread names. r=gsvelto
authorCervantes Yu <cyu@mozilla.com>
Tue, 07 Feb 2017 18:57:23 +0800
changeset 566361 fcd98542af390cfb873aee712e43388c779b07e6
parent 566360 9fd7a385411f928649da4e53446f0c2327d72d9c
child 566362 1105f666b01948b7bac4bf3566402baf2359fdb4
push id55180
push userjjong@mozilla.com
push dateFri, 21 Apr 2017 09:36:13 +0000
reviewersgsvelto
bugs1024669
milestone55.0a1
Bug 1024669 - Part 1: Annotate crash reports with thread names. r=gsvelto NS_SetCurrentThreadName() is added as an alternative to PR_SetCurrentThreadName() inside libxul. The thread names are collected in the form of crash annotation to be processed on socorro. MozReview-Commit-ID: 4RpAWzTuvPs
dom/media/webaudio/blink/HRTFDatabaseLoader.cpp
dom/storage/StorageDBThread.cpp
dom/workers/RuntimeService.cpp
js/xpconnect/src/XPCJSContext.cpp
netwerk/cache2/CacheIOThread.cpp
netwerk/dns/nsHostResolver.cpp
security/manager/ssl/nsKeygenThread.cpp
security/manager/ssl/nsProtectedAuthThread.cpp
security/manager/ssl/nsSmartCardMonitor.cpp
startupcache/StartupCache.cpp
toolkit/components/terminator/nsTerminator.cpp
toolkit/crashreporter/ThreadAnnotation.cpp
toolkit/crashreporter/ThreadAnnotation.h
toolkit/crashreporter/moz.build
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
toolkit/xre/EventTracer.cpp
toolkit/xre/nsEmbedFunctions.cpp
widget/cocoa/nsChildView.mm
widget/windows/LSPAnnotator.cpp
widget/windows/nsSound.cpp
xpcom/build/MainThreadIOLogger.cpp
xpcom/threads/BackgroundHangMonitor.cpp
xpcom/threads/HangMonitor.cpp
xpcom/threads/TimerThread.cpp
xpcom/threads/nsProcessCommon.cpp
xpcom/threads/nsThread.cpp
xpcom/threads/nsThreadUtils.cpp
xpcom/threads/nsThreadUtils.h
--- a/dom/media/webaudio/blink/HRTFDatabaseLoader.cpp
+++ b/dom/media/webaudio/blink/HRTFDatabaseLoader.cpp
@@ -24,16 +24,17 @@
  * 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 "HRTFDatabaseLoader.h"
 #include "HRTFDatabase.h"
 #include "GeckoProfiler.h"
+#include "nsThreadUtils.h"
 
 using namespace mozilla;
 
 namespace WebCore {
 
 // Singleton
 nsTHashtable<HRTFDatabaseLoader::LoaderByRateEntry>*
     HRTFDatabaseLoader::s_loaderMap = nullptr;
@@ -148,17 +149,17 @@ void HRTFDatabaseLoader::MainThreadRelea
         delete this;
     }
 }
 
 // Asynchronously load the database in this thread.
 static void databaseLoaderEntry(void* threadData)
 {
     AutoProfilerRegister registerThread("HRTFDatabaseLdr");
-    PR_SetCurrentThreadName("HRTFDatabaseLdr");
+    NS_SetCurrentThreadName("HRTFDatabaseLdr");
 
     HRTFDatabaseLoader* loader = reinterpret_cast<HRTFDatabaseLoader*>(threadData);
     MOZ_ASSERT(loader);
     loader->load();
 }
 
 void HRTFDatabaseLoader::load()
 {
--- a/dom/storage/StorageDBThread.cpp
+++ b/dom/storage/StorageDBThread.cpp
@@ -336,17 +336,17 @@ StorageDBThread::SetDefaultPriority()
     PR_SetThreadPriority(mThread, PR_PRIORITY_LOW);
   }
 }
 
 void
 StorageDBThread::ThreadFunc(void* aArg)
 {
   AutoProfilerRegister registerThread("localStorage DB");
-  PR_SetCurrentThreadName("localStorage DB");
+  NS_SetCurrentThreadName("localStorage DB");
   mozilla::IOInterposer::RegisterCurrentThread();
 
   StorageDBThread* thread = static_cast<StorageDBThread*>(aArg);
   thread->ThreadFunc();
   mozilla::IOInterposer::UnregisterCurrentThread();
 }
 
 void
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -2820,17 +2820,17 @@ NS_IMPL_ISUPPORTS_INHERITED0(WorkerThrea
 
 NS_IMETHODIMP
 WorkerThreadPrimaryRunnable::Run()
 {
   using mozilla::ipc::BackgroundChild;
 
   char stackBaseGuess;
 
-  PR_SetCurrentThreadName("DOM Worker");
+  NS_SetCurrentThreadName("DOM Worker");
 
   nsAutoCString threadName;
   threadName.AssignLiteral("DOM Worker '");
   threadName.Append(NS_LossyConvertUTF16toASCII(mWorkerPrivate->ScriptURL()));
   threadName.Append('\'');
 
   profiler_register_thread(threadName.get(), &stackBaseGuess);
 
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -12,16 +12,17 @@
 #include "xpcprivate.h"
 #include "xpcpublic.h"
 #include "XPCWrapper.h"
 #include "XPCJSMemoryReporter.h"
 #include "WrapperFactory.h"
 #include "mozJSComponentLoader.h"
 #include "nsAutoPtr.h"
 #include "nsNetUtil.h"
+#include "nsThreadUtils.h"
 
 #include "nsIMemoryInfoDumper.h"
 #include "nsIMemoryReporter.h"
 #include "nsIObserverService.h"
 #include "nsIDebug2.h"
 #include "nsIDocShell.h"
 #include "nsIRunnable.h"
 #include "amIAddonManager.h"
@@ -374,17 +375,17 @@ AutoLockWatchdog::~AutoLockWatchdog()
 {
     PR_Unlock(mWatchdog->GetLock());
 }
 
 static void
 WatchdogMain(void* arg)
 {
     mozilla::AutoProfilerRegister registerThread("JS Watchdog");
-    PR_SetCurrentThreadName("JS Watchdog");
+    NS_SetCurrentThreadName("JS Watchdog");
 
     Watchdog* self = static_cast<Watchdog*>(arg);
     WatchdogManager* manager = self->Manager();
 
     // Lock lasts until we return
     AutoLockWatchdog lock(self);
 
     MOZ_ASSERT(self->Initialized());
--- a/netwerk/cache2/CacheIOThread.cpp
+++ b/netwerk/cache2/CacheIOThread.cpp
@@ -437,17 +437,18 @@ already_AddRefed<nsIEventTarget> CacheIO
   return target.forget();
 }
 
 // static
 void CacheIOThread::ThreadFunc(void* aClosure)
 {
   // XXXmstange We'd like to register this thread with the profiler, but doing
   // so causes leaks, see bug 1323100.
-  PR_SetCurrentThreadName("Cache2 I/O");
+  NS_SetCurrentThreadName("Cache2 I/O");
+
   mozilla::IOInterposer::RegisterCurrentThread();
   CacheIOThread* thread = static_cast<CacheIOThread*>(aClosure);
   thread->ThreadFunc();
   mozilla::IOInterposer::UnregisterCurrentThread();
 }
 
 void CacheIOThread::ThreadFunc()
 {
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -1456,17 +1456,17 @@ nsHostResolver::ThreadFunc(void *arg)
 {
     char stackTop;
 
     LOG(("DNS lookup thread - starting execution.\n"));
 
     static nsThreadPoolNaming naming;
     nsCString name = naming.GetNextThreadName("DNS Resolver");
 
-    PR_SetCurrentThreadName(name.BeginReading());
+    NS_SetCurrentThreadName(name.BeginReading());
     profiler_register_thread(name.BeginReading(), &stackTop);
 
 #if defined(RES_RETRY_ON_FAILURE)
     nsResState rs;
 #endif
     nsHostResolver *resolver = (nsHostResolver *)arg;
     nsHostRecord *rec  = nullptr;
     AddrInfo *ai = nullptr;
--- a/security/manager/ssl/nsKeygenThread.cpp
+++ b/security/manager/ssl/nsKeygenThread.cpp
@@ -114,17 +114,17 @@ nsresult nsKeygenThread::ConsumeResult(
     }
   
   return rv;
 }
 
 static void nsKeygenThreadRunner(void *arg)
 {
   AutoProfilerRegister registerThread("Keygen");
-  PR_SetCurrentThreadName("Keygen");
+  NS_SetCurrentThreadName("Keygen");
   nsKeygenThread *self = static_cast<nsKeygenThread *>(arg);
   self->Run();
 }
 
 nsresult nsKeygenThread::StartKeyGeneration(nsIObserver* aObserver)
 {
   if (!NS_IsMainThread()) {
     NS_ERROR("nsKeygenThread::StartKeyGeneration called off the main thread");
--- a/security/manager/ssl/nsProtectedAuthThread.cpp
+++ b/security/manager/ssl/nsProtectedAuthThread.cpp
@@ -7,27 +7,28 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "GeckoProfiler.h"
 #include "nsPKCS11Slot.h"
 #include "nsProtectedAuthThread.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
+#include "nsThreadUtils.h"
 #include "pk11func.h"
 
 using namespace mozilla;
 using namespace mozilla::psm;
 
 NS_IMPL_ISUPPORTS(nsProtectedAuthThread, nsIProtectedAuthThread)
 
 static void nsProtectedAuthThreadRunner(void *arg)
 {
     AutoProfilerRegister registerThread("Protected Auth");
-    PR_SetCurrentThreadName("Protected Auth");
+    NS_SetCurrentThreadName("Protected Auth");
 
     nsProtectedAuthThread *self = static_cast<nsProtectedAuthThread *>(arg);
     self->Run();
 }
 
 nsProtectedAuthThread::nsProtectedAuthThread()
 : mMutex("nsProtectedAuthThread.mMutex")
 , mIAmRunning(false)
--- a/security/manager/ssl/nsSmartCardMonitor.cpp
+++ b/security/manager/ssl/nsSmartCardMonitor.cpp
@@ -387,12 +387,12 @@ const SECMODModule* SmartCardMonitoringT
 {
   return mModule;
 }
 
 // C-like calling sequence to glue into PR_CreateThread.
 void SmartCardMonitoringThread::LaunchExecute(void* arg)
 {
   AutoProfilerRegister registerThread("SmartCard");
-  PR_SetCurrentThreadName("SmartCard");
+  NS_SetCurrentThreadName("SmartCard");
 
   ((SmartCardMonitoringThread*)arg)->Execute();
 }
--- a/startupcache/StartupCache.cpp
+++ b/startupcache/StartupCache.cpp
@@ -497,17 +497,17 @@ StartupCache::WaitOnWriteThread()
   PR_JoinThread(mWriteThread);
   mWriteThread = nullptr;
 }
 
 void
 StartupCache::ThreadedWrite(void *aClosure)
 {
   AutoProfilerRegister registerThread("StartupCache");
-  PR_SetCurrentThreadName("StartupCache");
+  NS_SetCurrentThreadName("StartupCache");
   mozilla::IOInterposer::RegisterCurrentThread();
   /*
    * It is safe to use the pointer passed in aClosure to reference the
    * StartupCache object because the thread's lifetime is tightly coupled to
    * the lifetime of the StartupCache object; this thread is joined in the
    * StartupCache destructor, guaranteeing that this function runs if and only
    * if the StartupCache object is valid.
    */
--- a/toolkit/components/terminator/nsTerminator.cpp
+++ b/toolkit/components/terminator/nsTerminator.cpp
@@ -28,16 +28,17 @@
 #include "nsAppDirectoryServiceDefs.h"
 
 #include "nsIObserverService.h"
 #include "nsIPrefService.h"
 #if defined(MOZ_CRASHREPORTER)
 #include "nsExceptionHandler.h"
 #endif
 #include "GeckoProfiler.h"
+#include "nsThreadUtils.h"
 
 #if defined(XP_WIN)
 #include <windows.h>
 #else
 #include <unistd.h>
 #endif
 
 #include "mozilla/ArrayUtils.h"
@@ -120,17 +121,17 @@ struct Options {
 };
 
 /**
  * Entry point for the watchdog thread
  */
 void
 RunWatchdog(void* arg)
 {
-  PR_SetCurrentThreadName("Shutdown Hang Terminator");
+  NS_SetCurrentThreadName("Shutdown Hang Terminator");
 
   // Let's copy and deallocate options, that's one less leak to worry
   // about.
   UniquePtr<Options> options((Options*)arg);
   uint32_t crashAfterTicks = options->crashAfterTicks;
   options = nullptr;
 
   const uint32_t timeToLive = crashAfterTicks;
@@ -211,17 +212,17 @@ public:
 // module upon the next restart and fed to Telemetry.
 //
 Atomic<nsCString*> gWriteData(nullptr);
 PRMonitor* gWriteReady = nullptr;
 
 void RunWriter(void* arg)
 {
   AutoProfilerRegister registerThread("Shutdown Statistics Writer");
-  PR_SetCurrentThreadName("Shutdown Statistics Writer");
+  NS_SetCurrentThreadName("Shutdown Statistics Writer");
 
   MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(arg);
   // Shutdown will generally complete before we have a chance to
   // deallocate. This is not a leak.
 
   // Setup destinationPath and tmpFilePath
 
   nsCString destinationPath(static_cast<char*>(arg));
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/ThreadAnnotation.cpp
@@ -0,0 +1,253 @@
+/* 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 "ThreadAnnotation.h"
+
+#include <stddef.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/UniquePtr.h"
+
+#include "prthread.h"
+#include "nsDebug.h"
+#include "nsExceptionHandler.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+using mozilla::StaticMutex;
+using mozilla::StaticMutexAutoLock;
+using mozilla::UniquePtr;
+
+namespace CrashReporter {
+
+namespace {
+
+// Protects access to sInitialized and sThreadAnnotations.
+static StaticMutex sMutex;
+
+class ThreadAnnotationSpan {
+public:
+  ThreadAnnotationSpan(uint32_t aBegin, uint32_t aEnd)
+    : mBegin(aBegin)
+    , mEnd(aEnd)
+  {
+    MOZ_ASSERT(mBegin < mEnd);
+  }
+
+  ~ThreadAnnotationSpan();
+
+  class Comparator {
+  public:
+     bool Equals(const ThreadAnnotationSpan* const& a,
+                 const ThreadAnnotationSpan* const& b) const
+     {
+       return a->mBegin == b->mBegin;
+     }
+
+     bool LessThan(const ThreadAnnotationSpan* const& a,
+                   const ThreadAnnotationSpan* const& b) const
+     {
+       return a->mBegin < b->mBegin;
+     }
+  };
+
+private:
+  // ~ThreadAnnotationSpan() does nontrivial thing. Make sure we don't
+  // instantiate accidentally.
+  ThreadAnnotationSpan(const ThreadAnnotationSpan& aOther) = delete;
+  ThreadAnnotationSpan& operator=(const ThreadAnnotationSpan& aOther) = delete;
+
+  friend class ThreadAnnotationData;
+  friend class Comparator;
+
+  uint32_t mBegin;
+  uint32_t mEnd;
+};
+
+// This class keeps the flat version of thread annotations for each thread.
+// When a thread calls CrashReporter::SetCurrentThreadName(), it adds
+// information about the calling thread (thread id and name) to this class.
+// When crash happens, the crash reporter gets flat representation and add to
+// the crash annotation file.
+class ThreadAnnotationData {
+public:
+  ThreadAnnotationData()
+  {}
+
+  ~ThreadAnnotationData()
+  {}
+
+  // Adds <pre> tid:"thread name",</pre> annotation to the current annotations.
+  // Returns an instance of ThreadAnnotationSpan for cleanup on thread
+  // termination.
+  ThreadAnnotationSpan*
+  AddThreadAnnotation(ThreadId aTid, const char* aThreadName)
+  {
+    if (!aTid || !aThreadName) {
+      return nullptr;
+    }
+
+    uint32_t oldLength = mData.Length();
+    mData.AppendPrintf("%u:\"%s\",", aTid, aThreadName);
+    uint32_t newLength = mData.Length();
+
+    ThreadAnnotationSpan* rv = new ThreadAnnotationSpan(oldLength, newLength);
+    mDataSpans.AppendElement(rv);
+    return rv;
+  }
+
+  // Called on thread termination. Removes the thread annotation, represented as
+  // ThreadAnnotationSpan, from the flat representation.
+  void EraseThreadAnnotation(const ThreadAnnotationSpan& aThreadInfo)
+  {
+    uint32_t begin = aThreadInfo.mBegin;
+    uint32_t end = aThreadInfo.mEnd;
+
+    if (!(begin < end &&
+          end <= mData.Length())) {
+      return;
+    }
+
+    uint32_t cutLength = end - begin;
+    mData.Cut(begin, cutLength);
+
+    // Adjust the ThreadAnnotationSpan affected by data shifting.
+    size_t index = mDataSpans.BinaryIndexOf(&aThreadInfo,
+                                            ThreadAnnotationSpan::Comparator());
+    for (size_t i = index + 1; i < mDataSpans.Length(); i++) {
+      ThreadAnnotationSpan* elem = mDataSpans[i];
+
+      MOZ_ASSERT(elem->mBegin >= cutLength);
+      MOZ_ASSERT(elem->mEnd > cutLength);
+
+      elem->mBegin -= cutLength;
+      elem->mEnd -= cutLength;
+    }
+
+    // No loner tracking aThreadInfo.
+    mDataSpans.RemoveElementAt(index);
+  }
+
+  // Gets the flat representation of thread annotations.
+  void GetData(const std::function<void(const char*)>& aCallback)
+  {
+    aCallback(mData.BeginReading());
+  }
+private:
+  // The flat representation of thread annotations.
+  nsCString mData;
+
+  // This array tracks the created ThreadAnnotationSpan instances so that we
+  // can make adjustments accordingly when we cut substrings from mData on
+  // thread exit.
+  nsTArray<ThreadAnnotationSpan*> mDataSpans;
+};
+
+template<typename T>
+class DeleteWithLock
+{
+public:
+  constexpr DeleteWithLock() {}
+
+  void operator()(T* aPtr) const
+  {
+    static_assert(sizeof(T) > 0, "T must be complete");
+    StaticMutexAutoLock lock(sMutex);
+
+    delete aPtr;
+  }
+};
+
+static bool sInitialized = false;
+static UniquePtr<ThreadAnnotationData> sThreadAnnotations;
+
+static unsigned sTLSThreadInfoKey = (unsigned) -1;
+void ThreadLocalDestructor(void* aUserData)
+{
+  MOZ_ASSERT(aUserData);
+
+  StaticMutexAutoLock lock(sMutex);
+
+  ThreadAnnotationSpan* aThreadInfo =
+    static_cast<ThreadAnnotationSpan*>(aUserData);
+  delete aThreadInfo;
+}
+
+// This is called on thread termination.
+ThreadAnnotationSpan::~ThreadAnnotationSpan()
+{
+  // Note that we can't lock the mutex here because this function may be called
+  // from SetCurrentThreadName().
+  sMutex.AssertCurrentThreadOwns();
+
+  if (sThreadAnnotations) {
+    sThreadAnnotations->EraseThreadAnnotation(*this);
+  }
+}
+
+} // Anonymous namespace.
+
+void InitThreadAnnotation()
+{
+  StaticMutexAutoLock lock(sMutex);
+
+  if (sInitialized) {
+    return;
+  }
+
+  PRStatus status = PR_NewThreadPrivateIndex(&sTLSThreadInfoKey,
+                                             &ThreadLocalDestructor);
+  if (status == PR_FAILURE) {
+    return;
+  }
+
+  sInitialized = true;
+
+  sThreadAnnotations = mozilla::MakeUnique<ThreadAnnotationData>();
+}
+
+void SetCurrentThreadName(const char* aName)
+{
+  if (PR_GetThreadPrivate(sTLSThreadInfoKey)) {
+    // Explicitly set TLS value to null (and call the dtor function ) before
+    // acquiring sMutex to avoid reentrant deadlock.
+    PR_SetThreadPrivate(sTLSThreadInfoKey, nullptr);
+  }
+
+  StaticMutexAutoLock lock(sMutex);
+
+  if (!sInitialized) {
+    return;
+  }
+
+  ThreadAnnotationSpan* threadInfo =
+    sThreadAnnotations->AddThreadAnnotation(CurrentThreadId(),
+                                            aName);
+  // This may destroy the old insatnce.
+  PR_SetThreadPrivate(sTLSThreadInfoKey, threadInfo);
+}
+
+void GetFlatThreadAnnotation(const std::function<void(const char*)>& aCallback)
+{
+  StaticMutexAutoLock lock(sMutex);
+
+  if (sThreadAnnotations) {
+    sThreadAnnotations->GetData(aCallback);
+  } else {
+    // Maybe already shutdown: call aCallback with empty annotation data.
+    aCallback("");
+  }
+}
+
+void ShutdownThreadAnnotation()
+{
+  StaticMutexAutoLock lock(sMutex);
+
+  sInitialized = false;
+  sThreadAnnotations.reset();
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/ThreadAnnotation.h
@@ -0,0 +1,39 @@
+/* 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 ThreadAnnotation_h
+#define ThreadAnnotation_h
+
+#include <functional>
+
+#include "nsExceptionHandler.h"
+
+// Thread annotation interfaces for the crash reporter.
+namespace CrashReporter {
+
+void InitThreadAnnotation();
+
+void ShutdownThreadAnnotation();
+
+void GetFlatThreadAnnotation(const std::function<void(const char*)>& aCallback);
+
+class InitThreadAnnotationRAII {
+public:
+  InitThreadAnnotationRAII()
+  {
+    if (GetEnabled()) {
+      InitThreadAnnotation();
+    }
+  }
+
+  ~InitThreadAnnotationRAII() {
+    if (GetEnabled()) {
+      ShutdownThreadAnnotation();
+    }
+  }
+};
+
+}
+
+#endif
--- a/toolkit/crashreporter/moz.build
+++ b/toolkit/crashreporter/moz.build
@@ -57,16 +57,17 @@ if CONFIG['MOZ_CRASHREPORTER_INJECTOR']:
 TEST_DIRS += ['test']
 
 EXPORTS += [
     'nsExceptionHandler.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsExceptionHandler.cpp',
+    'ThreadAnnotation.cpp',
 ]
 
 if CONFIG['OS_ARCH'] == 'Darwin':
     UNIFIED_SOURCES += [
         'mac_utils.mm',
     ]
 
 EXTRA_JS_MODULES += [
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/Sprintf.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ipc/CrashReporterClient.h"
 
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "jsfriendapi.h"
+#include "ThreadAnnotation.h"
 
 #ifdef XP_WIN
 #include "mozilla/TlsAllocationTracker.h"
 #endif
 
 #if defined(XP_WIN32)
 #ifdef WIN32_LEAN_AND_MEAN
 #undef WIN32_LEAN_AND_MEAN
@@ -1179,16 +1180,29 @@ bool MinidumpCallback(
         WriteAnnotation(eventFile, "TopPendingIPCType", topPendingIPCTypeBuffer);
       }
     }
 
     if (memoryReportPath) {
       WriteLiteral(apiData, "ContainsMemoryReport=1\n");
       WriteLiteral(eventFile, "ContainsMemoryReport=1\n");
     }
+
+    std::function<void(const char*)> getThreadAnnotationCB =
+      [&] (const char * aAnnotation) -> void {
+      if (aAnnotation) {
+        WriteLiteral(apiData, "ThreadIdNameMapping=");
+        WriteLiteral(eventFile, "ThreadIdNameMapping=");
+        WriteString(apiData, aAnnotation);
+        WriteString(eventFile, aAnnotation);
+        WriteLiteral(apiData, "\n");
+        WriteLiteral(eventFile, "\n");
+      }
+    };
+    GetFlatThreadAnnotation(getThreadAnnotationCB);
   }
 
   if (!doReport) {
 #ifdef XP_WIN
     TerminateProcess(GetCurrentProcess(), 1);
 #endif // XP_WIN
     return returnValue;
   }
@@ -1408,16 +1422,26 @@ PrepareChildExceptionTimeAnnotations()
   }
 
 #ifdef XP_WIN
   const char* tlsAllocations = mozilla::GetTlsAllocationStacks();
   if (tlsAllocations) {
     WriteAnnotation(apiData, "TlsAllocations", tlsAllocations);
   }
 #endif
+
+  std::function<void(const char*)> getThreadAnnotationCB =
+    [&] (const char * aAnnotation) -> void {
+    if (aAnnotation) {
+      WriteLiteral(apiData, "ThreadIdNameMapping=");
+      WriteString(apiData, aAnnotation);
+      WriteLiteral(apiData, "\n");
+    }
+  };
+  GetFlatThreadAnnotation(getThreadAnnotationCB);
 }
 
 #ifdef XP_WIN
 static void
 ReserveBreakpadVM()
 {
   if (!gBreakpadReservedVM) {
     gBreakpadReservedVM = VirtualAlloc(nullptr, kReserveSize, MEM_RESERVE,
@@ -1827,16 +1851,18 @@ nsresult SetExceptionHandler(nsIFile* aX
                                       library_mappings[i].file_offset);
   }
 #endif
 
   mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
 
   oldTerminateHandler = std::set_terminate(&TerminateHandler);
 
+  InitThreadAnnotation();
+
   return NS_OK;
 }
 
 bool GetEnabled()
 {
   return gExceptionHandler != nullptr;
 }
 
@@ -2197,16 +2223,18 @@ nsresult UnsetExceptionHandler()
     currentSessionId = nullptr;
   }
 
   if (memoryReportPath) {
     free(memoryReportPath);
     memoryReportPath = nullptr;
   }
 
+  ShutdownThreadAnnotation();
+
   if (!gExceptionHandler)
     return NS_ERROR_NOT_INITIALIZED;
 
   gExceptionHandler = nullptr;
 
   OOPDeinit();
 
   delete dumpSafetyLock;
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -274,11 +274,15 @@ void SetNotificationPipeForChild(int chi
 // info about the shared libraries that are mapped into these anonymous
 // mappings.
 void AddLibraryMapping(const char* library_name,
                        uintptr_t   start_address,
                        size_t      mapping_length,
                        size_t      file_offset);
 
 #endif
+
+// Annotates the crash report with the name of the calling thread.
+void SetCurrentThreadName(const char* aName);
+
 } // namespace CrashReporter
 
 #endif /* nsExceptionHandler_h__ */
--- a/toolkit/xre/EventTracer.cpp
+++ b/toolkit/xre/EventTracer.cpp
@@ -60,18 +60,18 @@
 #include "mozilla/WidgetTraceEvent.h"
 #include "nsDebug.h"
 #include <limits.h>
 #include <prenv.h>
 #include <prinrval.h>
 #include <prthread.h>
 #include <prtime.h>
 
+#include "nsThreadUtils.h"
 #ifdef MOZ_WIDGET_GONK
-#include "nsThreadUtils.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #endif
 
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 using mozilla::FireAndWaitForTracerEvent;
 
@@ -118,17 +118,17 @@ class EventLoopLagDispatcher : public Ru
  *
  * The output defaults to stdout, but can be redirected to a file by
  * settting the environment variable MOZ_INSTRUMENT_EVENT_LOOP_OUTPUT
  * to the name of a file to use.
  */
 void TracerThread(void *arg)
 {
   AutoProfilerRegister registerThread("Event Tracer");
-  PR_SetCurrentThreadName("Event Tracer");
+  NS_SetCurrentThreadName("Event Tracer");
 
   TracerStartClosure* threadArgs = static_cast<TracerStartClosure*>(arg);
 
   // These are the defaults. They can be overridden by environment vars.
   // This should be set to the maximum latency we'd like to allow
   // for responsiveness.
   int32_t thresholdInterval = threadArgs->mThresholdInterval;
   PRIntervalTime threshold = PR_MillisecondsToInterval(thresholdInterval);
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -34,16 +34,17 @@
 #include "nsAutoRef.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsExceptionHandler.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsJSUtils.h"
 #include "nsWidgetsCID.h"
 #include "nsXREDirProvider.h"
+#include "ThreadAnnotation.h"
 
 #include "mozilla/Omnijar.h"
 #if defined(XP_MACOSX)
 #include "nsVersionComparator.h"
 #include "chrome/common/mach_ipc_mac.h"
 #endif
 #include "nsX11ErrorHandler.h"
 #include "nsGDKErrorHandler.h"
@@ -522,16 +523,19 @@ XRE_InitChildProcess(int aArgc,
   if (0 != strcmp("false", crashReporterArg) &&
       !XRE_SetRemoteExceptionHandler(nullptr)) {
     // Bug 684322 will add better visibility into this condition
     NS_WARNING("Could not setup crash reporting\n");
   }
 #  else
 #    error "OOP crash reporting unsupported on this platform"
 #  endif
+
+  // For Init/Shutdown thread name annotations in the crash reporter.
+  CrashReporter::InitThreadAnnotationRAII annotation;
 #endif // if defined(MOZ_CRASHREPORTER)
 
   gArgv = aArgv;
   gArgc = aArgc;
 
 #ifdef MOZ_X11
   XInitThreads();
 #endif
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -16,16 +16,17 @@
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 
 #include "nsArrayUtils.h"
 #include "nsObjCExceptions.h"
 #include "nsCOMPtr.h"
+#include "nsThreadUtils.h"
 #include "nsToolkit.h"
 #include "nsCRT.h"
 
 #include "nsFontMetrics.h"
 #include "nsIRollupListener.h"
 #include "nsViewManager.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIFile.h"
@@ -6627,17 +6628,17 @@ HandleEvent(CGEventTapProxy aProxy, CGEv
   [(EventThreadRunner*)aClosure handleEvent:aEvent type:aType];
   return aEvent;
 }
 
 - (void)runEventThread
 {
   char aLocal;
   profiler_register_thread("APZC Event Thread", &aLocal);
-  PR_SetCurrentThreadName("APZC Event Thread");
+  NS_SetCurrentThreadName("APZC Event Thread");
 
   mThread = [NSThread currentThread];
   ProcessSerialNumber currentProcess;
   GetCurrentProcess(&currentProcess);
   CFMachPortRef eventPort =
     CGEventTapCreateForPSN(&currentProcess,
                            kCGHeadInsertEventTap,
                            kCGEventTapOptionListenOnly,
--- a/widget/windows/LSPAnnotator.cpp
+++ b/widget/windows/LSPAnnotator.cpp
@@ -46,17 +46,17 @@ LSPAnnotationGatherer::Annotate()
     cr->AnnotateCrashReport(NS_LITERAL_CSTRING("Winsock_LSP"), mString);
   }
   mThread->AsyncShutdown();
 }
 
 NS_IMETHODIMP
 LSPAnnotationGatherer::Run()
 {
-  PR_SetCurrentThreadName("LSP Annotator");
+  NS_SetCurrentThreadName("LSP Annotator");
 
   mThread = NS_GetCurrentThread();
 
   DWORD size = 0;
   int err;
   // Get the size of the buffer we need
   if (SOCKET_ERROR != WSCEnumProtocols(nullptr, nullptr, &size, &err) ||
       err != WSAENOBUFS) {
--- a/widget/windows/nsSound.cpp
+++ b/widget/windows/nsSound.cpp
@@ -71,17 +71,17 @@ protected:
   protected:
     nsSound *mSound;
   };
 };
 
 NS_IMETHODIMP
 nsSoundPlayer::Run()
 {
-  PR_SetCurrentThreadName("Play Sound");
+  NS_SetCurrentThreadName("Play Sound");
 
   NS_PRECONDITION(!mSoundName.IsEmpty(), "Sound name should not be empty");
   ::PlaySoundW(mSoundName.get(), nullptr,
                SND_NODEFAULT | SND_ALIAS | SND_ASYNC);
   nsCOMPtr<nsIRunnable> releaser = new SoundReleaser(mSound);
   // Don't release nsSound from here, because here is not an owning thread of
   // the nsSound. nsSound must be released in its owning thread.
   mThread->Dispatch(releaser, NS_DISPATCH_NORMAL);
--- a/xpcom/build/MainThreadIOLogger.cpp
+++ b/xpcom/build/MainThreadIOLogger.cpp
@@ -8,16 +8,17 @@
 
 #include "GeckoProfiler.h"
 #include "IOInterposerPrivate.h"
 #include "mozilla/IOInterposer.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "nsAutoPtr.h"
 #include "nsNativeCharsetUtils.h"
+#include "nsThreadUtils.h"
 
 /**
  * This code uses NSPR stuff and STL containers because it must be detached
  * from leak checking code; this observer runs until the process terminates.
  */
 
 #include <prenv.h>
 #include <prprf.h>
@@ -110,17 +111,17 @@ MainThreadIOLoggerImpl::Init()
   }
   return true;
 }
 
 /* static */ void
 MainThreadIOLoggerImpl::sIOThreadFunc(void* aArg)
 {
   AutoProfilerRegister registerThread("MainThreadIOLogger");
-  PR_SetCurrentThreadName("MainThreadIOLogger");
+  NS_SetCurrentThreadName("MainThreadIOLogger");
   MainThreadIOLoggerImpl* obj = static_cast<MainThreadIOLoggerImpl*>(aArg);
   obj->IOThreadFunc();
 }
 
 void
 MainThreadIOLoggerImpl::IOThreadFunc()
 {
   PRFileDesc* fd = PR_Open(mFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
--- a/xpcom/threads/BackgroundHangMonitor.cpp
+++ b/xpcom/threads/BackgroundHangMonitor.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/ThreadLocal.h"
 
 #include "prinrval.h"
 #include "prthread.h"
 #include "ThreadStackHelper.h"
 #include "nsIObserverService.h"
 #include "nsIObserver.h"
 #include "mozilla/Services.h"
+#include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "GeckoProfiler.h"
 
 #include <algorithm>
 
 // Activate BHR only for one every BHR_BETA_MOD users.
 // This is now 100% of Beta population for the Beta 45/46 e10s A/B trials
 // It can be scaled back again in the future
@@ -51,17 +52,17 @@ namespace mozilla {
  */
 class BackgroundHangManager : public nsIObserver
 {
 private:
   // Background hang monitor thread function
   static void MonitorThread(void* aData)
   {
     AutoProfilerRegister registerThread("BgHangMonitor");
-    PR_SetCurrentThreadName("BgHangManager");
+    NS_SetCurrentThreadName("BgHangManager");
 
     /* We do not hold a reference to BackgroundHangManager here
        because the monitor thread only exists as long as the
        BackgroundHangManager instance exists. We stop the monitor
        thread in the BackgroundHangManager destructor, and we can
        only get to the destructor if we don't hold a reference here. */
     static_cast<BackgroundHangManager*>(aData)->RunMonitorThread();
   }
--- a/xpcom/threads/HangMonitor.cpp
+++ b/xpcom/threads/HangMonitor.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessedStack.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "nsReadableUtils.h"
+#include "nsThreadUtils.h"
 #include "mozilla/StackWalk.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "GeckoProfiler.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
@@ -192,17 +193,17 @@ GetChromeHangReport(Telemetry::Processed
 }
 
 #endif
 
 void
 ThreadMain(void*)
 {
   AutoProfilerRegister registerThread("Hang Monitor");
-  PR_SetCurrentThreadName("Hang Monitor");
+  NS_SetCurrentThreadName("Hang Monitor");
 
   MonitorAutoLock lock(*gMonitor);
 
   // In order to avoid issues with the hang monitor incorrectly triggering
   // during a general system stop such as sleeping, the monitor thread must
   // run twice to trigger hang protection.
   PRIntervalTime lastTimestamp = 0;
   int waitCount = 0;
--- a/xpcom/threads/TimerThread.cpp
+++ b/xpcom/threads/TimerThread.cpp
@@ -399,17 +399,17 @@ struct IntervalComparator
   }
 };
 
 } // namespace
 
 NS_IMETHODIMP
 TimerThread::Run()
 {
-  PR_SetCurrentThreadName("Timer");
+  NS_SetCurrentThreadName("Timer");
 
   MonitorAutoLock lock(mMonitor);
 
   // We need to know how many microseconds give a positive PRIntervalTime. This
   // is platform-dependent and we calculate it at runtime, finding a value |v|
   // such that |PR_MicrosecondsToInterval(v) > 0| and then binary-searching in
   // the range [0, v) to find the ms-to-interval scale.
   uint32_t usForPosInterval = 1;
--- a/xpcom/threads/nsProcessCommon.cpp
+++ b/xpcom/threads/nsProcessCommon.cpp
@@ -236,17 +236,17 @@ assembleCmdLine(char* const* aArgv, wcha
 void
 nsProcess::Monitor(void* aArg)
 {
   char stackBaseGuess;
 
   RefPtr<nsProcess> process = dont_AddRef(static_cast<nsProcess*>(aArg));
 
   if (!process->mBlocking) {
-    PR_SetCurrentThreadName("RunProcess");
+    NS_SetCurrentThreadName("RunProcess");
     profiler_register_thread("RunProcess", &stackBaseGuess);
   }
 
 #if defined(PROCESSMODEL_WINAPI)
   DWORD dwRetVal;
   unsigned long exitCode = -1;
 
   dwRetVal = WaitForSingleObject(process->mProcess, INFINITE);
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -457,17 +457,17 @@ nsThread::ThreadFunc(void* aArg)
 
   ThreadInitData* initData = static_cast<ThreadInitData*>(aArg);
   nsThread* self = initData->thread;  // strong reference
 
   self->mThread = PR_GetCurrentThread();
   SetupCurrentThreadForChaosMode();
 
   if (!initData->name.IsEmpty()) {
-    PR_SetCurrentThreadName(initData->name.BeginReading());
+    NS_SetCurrentThreadName(initData->name.BeginReading());
   }
 
   // Inform the ThreadManager
   nsThreadManager::get().RegisterCurrentThread(*self);
 
   mozilla::IOInterposer::RegisterCurrentThread();
 
   // This must come after the call to nsThreadManager::RegisterCurrentThread(),
--- a/xpcom/threads/nsThreadUtils.cpp
+++ b/xpcom/threads/nsThreadUtils.cpp
@@ -19,16 +19,20 @@
 #endif
 
 #ifdef XP_WIN
 #include <windows.h>
 #elif defined(XP_MACOSX)
 #include <sys/resource.h>
 #endif
 
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
 using namespace mozilla;
 
 #ifndef XPCOM_GLUE_AVOID_NSPR
 
 NS_IMPL_ISUPPORTS(IdlePeriod, nsIIdlePeriod)
 
 NS_IMETHODIMP
 IdlePeriod::GetIdlePeriodHint(TimeStamp* aIdleDeadline)
@@ -384,16 +388,25 @@ NS_ProcessNextEvent(nsIThread* aThread, 
     }
     aThread = current.get();
   }
 #endif
   bool val;
   return NS_SUCCEEDED(aThread->ProcessNextEvent(aMayWait, &val)) && val;
 }
 
+void
+NS_SetCurrentThreadName(const char* aName)
+{
+  PR_SetCurrentThreadName(aName);
+#ifdef MOZ_CRASHREPORTER
+  CrashReporter::SetCurrentThreadName(aName);
+#endif
+}
+
 #ifdef MOZILLA_INTERNAL_API
 nsIThread*
 NS_GetCurrentThread()
 {
   return nsThreadManager::get().GetCurrentThread();
 }
 #endif
 
--- a/xpcom/threads/nsThreadUtils.h
+++ b/xpcom/threads/nsThreadUtils.h
@@ -206,16 +206,26 @@ do_GetMainThread()
 //-----------------------------------------------------------------------------
 
 #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 nsIThread* NS_GetCurrentThread();
+
+/**
+ * Set the name of the current thread. Prefer this function over
+ * PR_SetCurrentThreadName() if possible. The name will also be included in the
+ * crash report.
+ *
+ * @param aName
+ *   Name of the thread. A C language null-terminated string.
+ */
+extern void NS_SetCurrentThreadName(const char* aName);
 #endif
 
 //-----------------------------------------------------------------------------
 
 #ifndef XPCOM_GLUE_AVOID_NSPR
 
 namespace mozilla {