Bug 1323100 - Register most of the remaining threadfunc threads with the profiler. r?froydnj
As far as I can tell, this covers all the remaining threads which we start
using PR_CreateThread, except the ones that are created inside NSPR or NSS,
and except for the Shutdown Watchdog thread in nsTerminator.cpp and the
CacheIO thread. The Shutdown Watchdog thread stays alive past leak detection
during shutdown (by design), so we'd report leaks if we profiled it. The
CacheIO thread seems to stay alive past shutdown leak detection sometimes as
well.
This adds a AutoProfilerRegister stack class for easy registering and
unregistering. There are a few places where we still call
profiler_register_thread() and profiler_unregister_thread() manually, either
because registration happens conditionally, or because there is a variable that
gets put on the stack before the AutoProfilerRegister (e.g. a dynamically
generated thread name). AutoProfilerRegister needs to be the first object on
the stack because it uses its own `this` pointer as the stack top address.
MozReview-Commit-ID: 3vwhS55Yzt
--- a/dom/media/webaudio/blink/HRTFDatabaseLoader.cpp
+++ b/dom/media/webaudio/blink/HRTFDatabaseLoader.cpp
@@ -23,16 +23,17 @@
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "HRTFDatabaseLoader.h"
#include "HRTFDatabase.h"
+#include "GeckoProfiler.h"
using namespace mozilla;
namespace WebCore {
// Singleton
nsTHashtable<HRTFDatabaseLoader::LoaderByRateEntry>*
HRTFDatabaseLoader::s_loaderMap = nullptr;
@@ -146,16 +147,17 @@ void HRTFDatabaseLoader::MainThreadRelea
// on this (main) thread.
delete this;
}
}
// Asynchronously load the database in this thread.
static void databaseLoaderEntry(void* threadData)
{
+ AutoProfilerRegister registerThread("HRTFDatabaseLdr");
PR_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
@@ -22,16 +22,17 @@
#include "mozIStorageValueArray.h"
#include "mozIStorageFunction.h"
#include "mozilla/BasePrincipal.h"
#include "nsIObserverService.h"
#include "nsVariant.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/Services.h"
#include "mozilla/Tokenizer.h"
+#include "GeckoProfiler.h"
// How long we collect write oprerations
// before they are flushed to the database
// In milliseconds.
#define FLUSHING_INTERVAL_MS 5000
// Write Ahead Log's maximum size is 512KB
#define MAX_WAL_SIZE_BYTES 512 * 1024
@@ -334,16 +335,17 @@ StorageDBThread::SetDefaultPriority()
if (--mPriorityCounter <= 0) {
PR_SetThreadPriority(mThread, PR_PRIORITY_LOW);
}
}
void
StorageDBThread::ThreadFunc(void* aArg)
{
+ AutoProfilerRegister registerThread("localStorage DB");
PR_SetCurrentThreadName("localStorage DB");
mozilla::IOInterposer::RegisterCurrentThread();
StorageDBThread* thread = static_cast<StorageDBThread*>(aArg);
thread->ThreadFunc();
mozilla::IOInterposer::UnregisterCurrentThread();
}
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -1161,16 +1161,17 @@ AutoLockWatchdog::AutoLockWatchdog(Watch
AutoLockWatchdog::~AutoLockWatchdog()
{
PR_Unlock(mWatchdog->GetLock());
}
static void
WatchdogMain(void* arg)
{
+ mozilla::AutoProfilerRegister registerThread("JS Watchdog");
PR_SetCurrentThreadName("JS Watchdog");
Watchdog* self = static_cast<Watchdog*>(arg);
WatchdogManager* manager = self->Manager();
// Lock lasts until we return
AutoLockWatchdog lock(self);
--- a/netwerk/cache2/CacheIOThread.cpp
+++ b/netwerk/cache2/CacheIOThread.cpp
@@ -5,16 +5,17 @@
#include "CacheIOThread.h"
#include "CacheFileIOManager.h"
#include "nsIRunnable.h"
#include "nsISupportsImpl.h"
#include "nsPrintfCString.h"
#include "nsThreadUtils.h"
#include "mozilla/IOInterposer.h"
+#include "GeckoProfiler.h"
#ifdef XP_WIN
#include <windows.h>
#endif
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
#include "TracedTaskCommon.h"
@@ -432,16 +433,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");
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
@@ -24,16 +24,17 @@
#include "prerror.h"
#include "prtime.h"
#include "mozilla/Logging.h"
#include "PLDHashTable.h"
#include "plstr.h"
#include "nsURLHelper.h"
#include "nsThreadUtils.h"
#include "GetAddrInfo.h"
+#include "GeckoProfiler.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Telemetry.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Preferences.h"
using namespace mozilla;
@@ -1430,22 +1431,25 @@ nsHostResolver::SizeOfIncludingThis(Mall
// |mDB| is measured.
return n;
}
void
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());
+ 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;
@@ -1506,16 +1510,18 @@ nsHostResolver::ThreadFunc(void *arg)
LOG_HOST(rec->host, rec->netInterface)));
} else {
rec = nullptr;
}
}
resolver->mThreadCount--;
NS_RELEASE(resolver);
LOG(("DNS lookup thread - queue empty, thread finished.\n"));
+
+ profiler_unregister_thread();
}
nsresult
nsHostResolver::Create(uint32_t maxCacheEntries,
uint32_t defaultCacheEntryLifetime,
uint32_t defaultGracePeriod,
nsHostResolver **result)
{
--- a/security/manager/ssl/nsKeygenThread.cpp
+++ b/security/manager/ssl/nsKeygenThread.cpp
@@ -1,20 +1,23 @@
/* -*- 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 "PSMRunnable.h"
+#include "nsKeygenThread.h"
+
#include "mozilla/Assertions.h"
#include "mozilla/DebugOnly.h"
+
+#include "GeckoProfiler.h"
+#include "PSMRunnable.h"
#include "nsCOMPtr.h"
#include "nsIObserver.h"
-#include "nsKeygenThread.h"
#include "nsNSSShutDown.h"
#include "nsThreadUtils.h"
#include "pk11func.h"
using namespace mozilla;
using namespace mozilla::psm;
NS_IMPL_ISUPPORTS(nsKeygenThread, nsIKeygenThread)
@@ -110,16 +113,17 @@ nsresult nsKeygenThread::ConsumeResult(
rv = NS_ERROR_FAILURE;
}
return rv;
}
static void nsKeygenThreadRunner(void *arg)
{
+ AutoProfilerRegister registerThread("Keygen");
PR_SetCurrentThreadName("Keygen");
nsKeygenThread *self = static_cast<nsKeygenThread *>(arg);
self->Run();
}
nsresult nsKeygenThread::StartKeyGeneration(nsIObserver* aObserver)
{
if (!NS_IsMainThread()) {
--- a/security/manager/ssl/nsProtectedAuthThread.cpp
+++ b/security/manager/ssl/nsProtectedAuthThread.cpp
@@ -2,29 +2,31 @@
* 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 "PSMRunnable.h"
#include "mozilla/Assertions.h"
#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 "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");
nsProtectedAuthThread *self = static_cast<nsProtectedAuthThread *>(arg);
self->Run();
}
nsProtectedAuthThread::nsProtectedAuthThread()
: mMutex("nsProtectedAuthThread.mMutex")
--- a/security/manager/ssl/nsSmartCardMonitor.cpp
+++ b/security/manager/ssl/nsSmartCardMonitor.cpp
@@ -5,16 +5,17 @@
#include "nsSmartCardMonitor.h"
#include "ScopedNSSTypes.h"
#include "mozilla/Services.h"
#include "mozilla/Unused.h"
#include "nsIObserverService.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
+#include "GeckoProfiler.h"
#include "nspr.h"
#include "pk11func.h"
using namespace mozilla;
//
// The SmartCard monitoring thread should start up for each module we load
// that has removable tokens. This code calls an NSS function which waits
@@ -385,12 +386,13 @@ void SmartCardMonitoringThread::Execute(
const SECMODModule* SmartCardMonitoringThread::GetModule()
{
return mModule;
}
// C-like calling sequence to glue into PR_CreateThread.
void SmartCardMonitoringThread::LaunchExecute(void* arg)
{
+ AutoProfilerRegister registerThread("SmartCard");
PR_SetCurrentThreadName("SmartCard");
((SmartCardMonitoringThread*)arg)->Execute();
}
--- a/startupcache/StartupCache.cpp
+++ b/startupcache/StartupCache.cpp
@@ -31,16 +31,17 @@
#include "nsWeakReference.h"
#include "nsZipArchive.h"
#include "mozilla/Omnijar.h"
#include "prenv.h"
#include "mozilla/Telemetry.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "nsIProtocolHandler.h"
+#include "GeckoProfiler.h"
#ifdef IS_BIG_ENDIAN
#define SC_ENDIAN "big"
#else
#define SC_ENDIAN "little"
#endif
#if PR_BYTES_PER_WORD == 4
@@ -498,16 +499,17 @@ StartupCache::WaitOnWriteThread()
PR_JoinThread(mWriteThread);
mWriteThread = nullptr;
}
void
StartupCache::ThreadedWrite(void *aClosure)
{
+ AutoProfilerRegister registerThread("StartupCache");
PR_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
@@ -27,16 +27,17 @@
#include "nsDirectoryServiceUtils.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIObserverService.h"
#include "nsIPrefService.h"
#if defined(MOZ_CRASHREPORTER)
#include "nsExceptionHandler.h"
#endif
+#include "GeckoProfiler.h"
#if defined(XP_WIN)
#include <windows.h>
#else
#include <unistd.h>
#endif
#include "mozilla/ArrayUtils.h"
@@ -209,16 +210,17 @@ public:
// The data written by the writer thread will be read by another
// 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");
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
--- a/toolkit/xre/EventTracer.cpp
+++ b/toolkit/xre/EventTracer.cpp
@@ -117,16 +117,17 @@ class EventLoopLagDispatcher : public Ru
* it will not send another event until the previous response is received.
*
* 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");
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;
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -243,16 +243,24 @@ static inline void profiler_js_operation
static inline double profiler_time() { return 0; }
static inline double profiler_time(const mozilla::TimeStamp& aTime) { return 0; }
static inline bool profiler_in_privacy_mode() { return false; }
static inline void profiler_log(const char *str) {}
static inline void profiler_log(const char *fmt, va_list args) {}
+class AutoProfilerRegister final MOZ_STACK_CLASS
+{
+ AutoProfilerRegister(const char* aName) {}
+private:
+ AutoProfilerRegister(const AutoProfilerRegister&) = delete;
+ AutoProfilerRegister& operator=(const AutoProfilerRegister&) = delete;
+};
+
#else
#include "GeckoProfilerImpl.h"
#endif
class MOZ_RAII GeckoProfilerInitRAII {
public:
--- a/tools/profiler/public/GeckoProfilerImpl.h
+++ b/tools/profiler/public/GeckoProfilerImpl.h
@@ -456,16 +456,36 @@ public:
~SamplerStackFramePrintfRAII() {
mozilla_sampler_call_exit(mHandle);
}
private:
char mDest[SAMPLER_MAX_STRING];
void* mHandle;
};
+/**
+ * Convenience class to register and unregister a thread with the profiler.
+ * Needs to be the first object on the stack of the thread.
+ */
+class MOZ_STACK_CLASS AutoProfilerRegister final
+{
+public:
+ explicit AutoProfilerRegister(const char* aName)
+ {
+ profiler_register_thread(aName, this);
+ }
+ ~AutoProfilerRegister()
+ {
+ profiler_unregister_thread();
+ }
+private:
+ AutoProfilerRegister(const AutoProfilerRegister&) = delete;
+ AutoProfilerRegister& operator=(const AutoProfilerRegister&) = delete;
+};
+
} // namespace mozilla
inline PseudoStack* mozilla_get_pseudo_stack(void)
{
if (!stack_key_initialized)
return nullptr;
return tlsPseudoStack.get();
}
--- a/xpcom/build/MainThreadIOLogger.cpp
+++ b/xpcom/build/MainThreadIOLogger.cpp
@@ -109,16 +109,17 @@ MainThreadIOLoggerImpl::Init()
return false;
}
return true;
}
/* static */ void
MainThreadIOLoggerImpl::sIOThreadFunc(void* aArg)
{
+ AutoProfilerRegister registerThread("MainThreadIOLogger");
PR_SetCurrentThreadName("MainThreadIOLogger");
MainThreadIOLoggerImpl* obj = static_cast<MainThreadIOLoggerImpl*>(aArg);
obj->IOThreadFunc();
}
void
MainThreadIOLoggerImpl::IOThreadFunc()
{
--- a/xpcom/threads/BackgroundHangMonitor.cpp
+++ b/xpcom/threads/BackgroundHangMonitor.cpp
@@ -17,16 +17,17 @@
#include "prinrval.h"
#include "prthread.h"
#include "ThreadStackHelper.h"
#include "nsIObserverService.h"
#include "nsIObserver.h"
#include "mozilla/Services.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
#define BHR_BETA_MOD 1;
@@ -49,16 +50,17 @@ namespace mozilla {
* manages all instances of BackgroundHangThread.
*/
class BackgroundHangManager : public nsIObserver
{
private:
// Background hang monitor thread function
static void MonitorThread(void* aData)
{
+ AutoProfilerRegister registerThread("BgHangMonitor");
PR_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
@@ -16,16 +16,17 @@
#include "mozilla/UniquePtr.h"
#include "nsReadableUtils.h"
#include "mozilla/StackWalk.h"
#ifdef _WIN64
#include "mozilla/StackWalk_windows.h"
#endif
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
+#include "GeckoProfiler.h"
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#endif
#ifdef XP_WIN
#include <windows.h>
#endif
@@ -204,16 +205,17 @@ GetChromeHangReport(Telemetry::Processed
}
}
#endif
void
ThreadMain(void*)
{
+ AutoProfilerRegister registerThread("Hang Monitor");
PR_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;
--- a/xpcom/threads/nsProcessCommon.cpp
+++ b/xpcom/threads/nsProcessCommon.cpp
@@ -20,16 +20,17 @@
#include "nsProcess.h"
#include "prio.h"
#include "prenv.h"
#include "nsCRT.h"
#include "nsThreadUtils.h"
#include "nsIObserverService.h"
#include "nsXULAppAPI.h"
#include "mozilla/Services.h"
+#include "GeckoProfiler.h"
#include <stdlib.h>
#if defined(PROCESSMODEL_WINAPI)
#include "prmem.h"
#include "nsString.h"
#include "nsLiteralString.h"
#include "nsReadableUtils.h"
@@ -230,20 +231,23 @@ assembleCmdLine(char* const* aArgv, wcha
PR_Free(cmdLine);
return 0;
}
#endif
void
nsProcess::Monitor(void* aArg)
{
+ char stackBaseGuess;
+
RefPtr<nsProcess> process = dont_AddRef(static_cast<nsProcess*>(aArg));
if (!process->mBlocking) {
PR_SetCurrentThreadName("RunProcess");
+ profiler_register_thread("RunProcess", &stackBaseGuess);
}
#if defined(PROCESSMODEL_WINAPI)
DWORD dwRetVal;
unsigned long exitCode = -1;
dwRetVal = WaitForSingleObject(process->mProcess, INFINITE);
if (dwRetVal != WAIT_FAILED) {
@@ -299,16 +303,20 @@ nsProcess::Monitor(void* aArg)
// If we ran a background thread for the monitor then notify on the main
// thread
if (NS_IsMainThread()) {
process->ProcessComplete();
} else {
NS_DispatchToMainThread(NewRunnableMethod(process, &nsProcess::ProcessComplete));
}
+
+ if (!process->mBlocking) {
+ profiler_unregister_thread();
+ }
}
void
nsProcess::ProcessComplete()
{
if (mThread) {
nsCOMPtr<nsIObserverService> os =
mozilla::services::GetObserverService();