Bug 862500 - Properly shutdown profiler in xpcshell, shutdown and error cases. r=snorp
authorBenoit Girard <b56girard@gmail.com>
Thu, 18 Apr 2013 11:34:49 -0400
changeset 141008 c0e86fd53a375f8e843a4d44017d4948a539d5e1
parent 141007 f2e44e02f8745de3e0c9c278d0d6913b46243f08
child 141009 2dd4a898a3534efc084885825dc218727c498bad
push id350
push userbbajaj@mozilla.com
push dateMon, 29 Jul 2013 23:00:49 +0000
treeherdermozilla-release@064965b37dbd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs862500
milestone23.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 862500 - Properly shutdown profiler in xpcshell, shutdown and error cases. r=snorp
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsEmbedFunctions.cpp
tools/profiler/GeckoProfiler.h
tools/profiler/platform-linux.cc
tools/profiler/platform-macos.cc
tools/profiler/platform-win32.cc
tools/profiler/platform.cpp
xpcom/build/nsXPComInit.cpp
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3889,17 +3889,17 @@ XREMain::XRE_mainRun()
 }
 
 /*
  * XRE_main - A class based main entry point used by most platforms.
  */
 int
 XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
 {
-  profiler_init();
+  GeckoProfilerInitRAII profilerGuard;
   PROFILER_LABEL("Startup", "XRE_Main");
 
   nsresult rv = NS_OK;
 
   gArgc = argc;
   gArgv = argv;
 
   NS_ENSURE_TRUE(aAppData, 2);
@@ -3990,17 +3990,16 @@ XREMain::XRE_main(int argc, char* argv[]
     SaveFileToEnvIfUnset("XRE_PROFILE_PATH", mProfD);
     SaveFileToEnvIfUnset("XRE_PROFILE_LOCAL_PATH", mProfLD);
     SaveWordToEnvIfUnset("XRE_PROFILE_NAME", mProfileName);
 
 #ifdef MOZ_WIDGET_GTK
     MOZ_gdk_display_close(mGdkDisplay);
 #endif
 
-    profiler_shutdown();
     rv = LaunchChild(mNativeApp, true);
 
 #ifdef MOZ_CRASHREPORTER
     if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
       CrashReporter::UnsetExceptionHandler();
 #endif
     return rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1;
   }
@@ -4013,18 +4012,16 @@ XREMain::XRE_main(int argc, char* argv[]
 
 #ifdef MOZ_CRASHREPORTER
   if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
       CrashReporter::UnsetExceptionHandler();
 #endif
 
   XRE_DeinitCommandLine();
 
-  profiler_shutdown();
-
   return NS_FAILED(rv) ? 1 : 0;
 }
 
 #if defined(MOZ_METRO) && defined(XP_WIN)
 extern bool XRE_MetroCoreApplicationRun();
 static XREMain* xreMainPtr;
 
 // must be called by the thread we want as the main thread
@@ -4087,17 +4084,17 @@ public:
     }
   }
   HRESULT mResult;
 };
 
 int
 XRE_mainMetro(int argc, char* argv[], const nsXREAppData* aAppData)
 {
-  profiler_init();
+  GeckoProfilerInitRAII profilerGuard;
   PROFILER_LABEL("Startup", "XRE_Main");
 
   nsresult rv = NS_OK;
 
   xreMainPtr = new XREMain();
   if (!xreMainPtr) {
     return 1;
   }
@@ -4128,17 +4125,16 @@ XRE_mainMetro(int argc, char* argv[], co
   if (!XRE_MetroCoreApplicationRun()) {
     return 1;
   }
 
   // XRE_metroShutdown should have already been called on the worker
   // thread that called XRE_metroStartup.
   NS_ASSERTION(!xreMainPtr->mScopedXPCom,
                "XPCOM Shutdown hasn't occured, and we are exiting.");
-  profiler_shutdown();
   return 0;
 }
 
 void SetWindowsEnvironment(WindowsEnvironmentType aEnvID);
 #endif // MOZ_METRO || !defined(XP_WIN)
 
 void
 XRE_DisableWritePoisoning(void) {
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -419,16 +419,17 @@ XRE_InitChildProcess(int aArgc,
 
   base::AtExitManager exitManager;
   NotificationService notificationService;
 
   NS_LogInit();
 
   nsresult rv = XRE_InitCommandLine(aArgc, aArgv);
   if (NS_FAILED(rv)) {
+    profiler_shutdown();
     NS_LogTerm();
     return NS_ERROR_FAILURE;
   }
 
   MessageLoop::Type uiLoopType;
   switch (aProcess) {
   case GeckoProcessType_Content:
       // Content processes need the XPCOM/chromium frankenventloop
@@ -481,30 +482,32 @@ XRE_InitChildProcess(int aArgc,
 #endif
         break;
 
       default:
         NS_RUNTIMEABORT("Unknown main thread class");
       }
 
       if (!process->Init()) {
+        profiler_shutdown();
         NS_LogTerm();
         return NS_ERROR_FAILURE;
       }
 
       // Run the UI event loop on the main thread.
       uiMessageLoop.MessageLoop::Run();
 
       // Allow ProcessChild to clean up after itself before going out of
       // scope and being deleted
       process->CleanUp();
       mozilla::Omnijar::CleanUp();
     }
   }
 
+  profiler_shutdown();
   NS_LogTerm();
   return XRE_DeinitCommandLine();
 }
 
 MessageLoop*
 XRE_GetIOMessageLoop()
 {
   if (sChildProcessType == GeckoProcessType_Default) {
--- a/tools/profiler/GeckoProfiler.h
+++ b/tools/profiler/GeckoProfiler.h
@@ -143,9 +143,19 @@ static inline void profiler_unregister_t
 static inline void profiler_js_operation_callback() {}
 
 #else
 
 #include "GeckoProfilerImpl.h"
 
 #endif
 
+class GeckoProfilerInitRAII {
+public:
+  GeckoProfilerInitRAII() {
+    profiler_init();
+  }
+  ~GeckoProfilerInitRAII() {
+    profiler_shutdown();
+  }
+};
+
 #endif // ifndef SAMPLER_H
--- a/tools/profiler/platform-linux.cc
+++ b/tools/profiler/platform-linux.cc
@@ -355,16 +355,19 @@ void Sampler::Stop() {
     sigaction(SIGNAL_SAVE_PROFILE, &old_sigsave_signal_handler_, 0);
     sigaction(SIGPROF, &old_sigprof_signal_handler_, 0);
     signal_handler_installed_ = false;
   }
 }
 
 bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
 {
+  if (!Sampler::sRegisteredThreadsMutex)
+    return false;
+
   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   ThreadInfo* info = new ThreadInfo(aName, gettid(),
     aIsMainThread, aPseudoStack);
 
   bool profileThread = sActiveSampler &&
     (aIsMainThread || sActiveSampler->ProfileThreads());
 
@@ -382,16 +385,19 @@ bool Sampler::RegisterCurrentThread(cons
   }
 
   sRegisteredThreads->push_back(info);
   return true;
 }
 
 void Sampler::UnregisterCurrentThread()
 {
+  if (!Sampler::sRegisteredThreadsMutex)
+    return;
+
   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   int id = gettid();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id) {
       delete info;
--- a/tools/profiler/platform-macos.cc
+++ b/tools/profiler/platform-macos.cc
@@ -350,16 +350,19 @@ Sampler::GetProfiledThread(PlatformData*
 #include <sys/syscall.h>
 pid_t gettid()
 {
   return (pid_t) syscall(SYS_thread_selfid);
 }
 
 bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
 {
+  if (!Sampler::sRegisteredThreadsMutex)
+    return false;
+
   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   ThreadInfo* info = new ThreadInfo(aName, gettid(),
     aIsMainThread, aPseudoStack);
 
   bool profileThread = sActiveSampler &&
     (aIsMainThread || sActiveSampler->ProfileThreads());
 
@@ -377,16 +380,19 @@ bool Sampler::RegisterCurrentThread(cons
   }
 
   sRegisteredThreads->push_back(info);
   return true;
 }
 
 void Sampler::UnregisterCurrentThread()
 {
+  if (!Sampler::sRegisteredThreadsMutex)
+    return;
+
   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   int id = gettid();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id) {
       delete info;
--- a/tools/profiler/platform-win32.cc
+++ b/tools/profiler/platform-win32.cc
@@ -258,16 +258,19 @@ void Thread::Join() {
 }
 
 void OS::Sleep(int milliseconds) {
   ::Sleep(milliseconds);
 }
 
 bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
 {
+  if (!Sampler::sRegisteredThreadsMutex)
+    return false;
+
   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   ThreadInfo* info = new ThreadInfo(aName, GetCurrentThreadId(),
     aIsMainThread, aPseudoStack);
 
   bool profileThread = sActiveSampler &&
     (aIsMainThread || sActiveSampler->ProfileThreads());
 
@@ -285,16 +288,19 @@ bool Sampler::RegisterCurrentThread(cons
   }
 
   sRegisteredThreads->push_back(info);
   return true;
 }
 
 void Sampler::UnregisterCurrentThread()
 {
+  if (!Sampler::sRegisteredThreadsMutex)
+    return;
+
   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
 
   int id = GetCurrentThreadId();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id) {
       delete info;
--- a/tools/profiler/platform.cpp
+++ b/tools/profiler/platform.cpp
@@ -26,16 +26,17 @@ mozilla::ThreadLocal<TableTicker *> tlsT
 // 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;
 
 TimeStamp   sLastTracerEvent; // is raced on
 int         sFrameNumber = 0;
 int         sLastFrameNumber = 0;
+int         sInitCount = 0; // Each init must have a matched shutdown.
 static bool sIsProfiling = false; // is raced on
 
 /* 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 trackplaing everytime we go around
  * the event loop */
@@ -58,16 +59,21 @@ void Sampler::Startup() {
 void Sampler::Shutdown() {
   while (sRegisteredThreads->size() > 0) {
     delete sRegisteredThreads->back();
     sRegisteredThreads->pop_back();
   }
 
   delete sRegisteredThreadsMutex;
   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() {
   free(mName);
 
   if (mProfile)
     delete mProfile;
 
@@ -238,16 +244,18 @@ void read_profiler_env_vars()
   return;
 }
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN externally visible functions
 
 void mozilla_sampler_init()
 {
+  sInitCount++;
+
   if (stack_key_initialized)
     return;
 
   LOG("BEGIN mozilla_sampler_init");
   if (!tlsPseudoStack.init() || !tlsTicker.init()) {
     LOG("Failed to init.");
     return;
   }
@@ -301,16 +309,21 @@ void mozilla_sampler_init()
                          };
   profiler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
                          features, sizeof(features)/sizeof(const char*));
   LOG("END   mozilla_sampler_init");
 }
 
 void mozilla_sampler_shutdown()
 {
+  sInitCount--;
+
+  if (sInitCount > 0)
+    return;
+
   // Save the profile on shutdown if requested.
   TableTicker *t = tlsTicker.get();
   if (t) {
     const char *val = PR_GetEnv("MOZ_PROFILER_SHUTDOWN");
     if (val) {
       std::ofstream stream;
       stream.open(val);
       if (stream.is_open()) {
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -726,14 +726,16 @@ ShutdownXPCOM(nsIServiceManager* servMgr
     Omnijar::CleanUp();
 
     HangMonitor::Shutdown();
 
 #ifdef MOZ_VISUAL_EVENT_TRACER
     eventtracer::Shutdown();
 #endif
 
+    profiler_shutdown();
+
     NS_LogTerm();
 
     return NS_OK;
 }
 
 } // namespace mozilla