Bug 818307: Part 2 - Plugin Hang UI ChromeHang annotations; r=gfritzsche
☠☠ backed out by 15a271e87dd3 ☠ ☠
authorAaron Klotz <aklotz@mozilla.com>
Fri, 10 Oct 2014 12:19:58 -0600
changeset 233093 fb6af789bc67305f9fb503c372d571d1c68a4e5d
parent 233092 313123f12e959a9113a2e922d37727593f364d7d
child 233094 fb27bd0c00841ba23ccfcde67f7ca6f7e7f0edeb
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgfritzsche
bugs818307
milestone35.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 818307: Part 2 - Plugin Hang UI ChromeHang annotations; r=gfritzsche
dom/plugins/ipc/PluginHangUIParent.cpp
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginModuleParent.h
--- a/dom/plugins/ipc/PluginHangUIParent.cpp
+++ b/dom/plugins/ipc/PluginHangUIParent.cpp
@@ -351,16 +351,17 @@ PluginHangUIParent::RecvUserResponse(con
   mIsShowing = false;
   // responseCode: 1 = Stop, 2 = Continue, 3 = Cancel
   int responseCode;
   if (aResponse & HANGUI_USER_RESPONSE_STOP) {
     // User clicked Stop
     mModule->TerminateChildProcess(mMainThreadMessageLoop);
     responseCode = 1;
   } else if(aResponse & HANGUI_USER_RESPONSE_CONTINUE) {
+    mModule->OnHangUIContinue();
     // User clicked Continue
     responseCode = 2;
   } else {
     // Dialog was cancelled
     responseCode = 3;
   }
   int dontAskCode = (aResponse & HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN) ? 1 : 0;
   nsCOMPtr<nsIRunnable> workItem = new nsPluginHangUITelemetry(responseCode,
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -129,16 +129,17 @@ PluginModuleParent::LoadModule(const cha
 PluginModuleParent::PluginModuleParent(const char* aFilePath)
     : mSubprocess(new PluginProcessParent(aFilePath))
     , mShutdown(false)
     , mClearSiteDataSupported(false)
     , mGetSitesWithDataSupported(false)
     , mNPNIface(nullptr)
     , mPlugin(nullptr)
     , mTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST())
+    , mHangAnnotationFlags(0)
 #ifdef XP_WIN
     , mPluginCpuUsageOnHang()
     , mHangUIParent(nullptr)
     , mHangUIEnabled(true)
     , mIsTimerReset(true)
 #ifdef MOZ_CRASHREPORTER
     , mCrashReporterMutex("PluginModuleParent::mCrashReporterMutex")
     , mCrashReporter(nullptr)
@@ -156,16 +157,18 @@ PluginModuleParent::PluginModuleParent(c
 #ifdef XP_WIN
     Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
     Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
 #endif
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     InitPluginProfiling();
 #endif
+
+    mozilla::HangMonitor::RegisterAnnotator(*this);
 }
 
 PluginModuleParent::~PluginModuleParent()
 {
     if (!OkToCleanup()) {
         NS_RUNTIMEABORT("unsafe destruction");
     }
 
@@ -199,16 +202,18 @@ PluginModuleParent::~PluginModuleParent(
     Preferences::UnregisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
     Preferences::UnregisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
 
     if (mHangUIParent) {
         delete mHangUIParent;
         mHangUIParent = nullptr;
     }
 #endif
+
+    mozilla::HangMonitor::UnregisterAnnotator(*this);
 }
 
 #ifdef MOZ_CRASHREPORTER
 void
 PluginModuleParent::WriteExtraDataForMinidump(AnnotationTable& notes)
 {
 #ifdef XP_WIN
     // mCrashReporterMutex is already held by the caller
@@ -220,30 +225,18 @@ PluginModuleParent::WriteExtraDataForMin
     const std::string& pluginFile = mSubprocess->GetPluginFilePath();
     size_t filePos = pluginFile.rfind(FILE_PATH_SEPARATOR);
     if (filePos == std::string::npos)
         filePos = 0;
     else
         filePos++;
     notes.Put(NS_LITERAL_CSTRING("PluginFilename"), CS(pluginFile.substr(filePos).c_str()));
 
-    nsCString pluginName;
-    nsCString pluginVersion;
-
-    nsRefPtr<nsPluginHost> ph = nsPluginHost::GetInst();
-    if (ph) {
-        nsPluginTag* tag = ph->TagForPlugin(mPlugin);
-        if (tag) {
-            pluginName = tag->mName;
-            pluginVersion = tag->mVersion;
-        }
-    }
-
-    notes.Put(NS_LITERAL_CSTRING("PluginName"), pluginName);
-    notes.Put(NS_LITERAL_CSTRING("PluginVersion"), pluginVersion);
+    notes.Put(NS_LITERAL_CSTRING("PluginName"), mPluginName);
+    notes.Put(NS_LITERAL_CSTRING("PluginVersion"), mPluginVersion);
 
     CrashReporterParent* crashReporter = CrashReporter();
     if (crashReporter) {
 #ifdef XP_WIN
         if (mPluginCpuUsageOnHang.Length() > 0) {
             notes.Put(NS_LITERAL_CSTRING("NumberOfProcessors"),
                       nsPrintfCString("%d", PR_GetNumberOfProcessors()));
 
@@ -385,23 +378,62 @@ GetProcessCpuUsage(const InfallibleTArra
     cpuUsage.AppendElement(usage);
   }
 
   return true;
 }
 
 } // anonymous namespace
 
+#endif // #ifdef XP_WIN
+
+void
+PluginModuleParent::EnteredCxxStack()
+{
+    mHangAnnotationFlags |= kInPluginCall;
+}
+
 void
 PluginModuleParent::ExitedCxxStack()
 {
+    mHangAnnotationFlags = 0;
+#ifdef XP_WIN
     FinishHangUI();
+#endif
 }
 
-#endif // #ifdef XP_WIN
+/**
+ * This function is always called by the HangMonitor thread.
+ */
+void
+PluginModuleParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations)
+{
+    uint32_t flags = mHangAnnotationFlags;
+    if (flags) {
+        /* We don't actually annotate anything specifically for kInPluginCall;
+           we use it to determine whether to annotate other things. It will
+           be pretty obvious from the ChromeHang stack that we're in a plugin
+           call when the hang occurred. */
+        if (flags & kHangUIShown) {
+            aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIShown"),
+                                       true);
+        }
+        if (flags & kHangUIContinued) {
+            aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIContinued"),
+                                       true);
+        }
+        if (flags & kHangUIDontShow) {
+            aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIDontShow"),
+                                       true);
+        }
+        aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginName"), mPluginName);
+        aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginVersion"),
+                                   mPluginVersion);
+    }
+}
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 static bool
 CreateFlashMinidump(DWORD processId, ThreadId childThread,
                     nsIFile* parentMinidump, const nsACString& name)
 {
   if (processId == 0) {
     return false;
@@ -527,16 +559,33 @@ PluginModuleParent::TerminateChildProces
         FROM_HERE,
         mTaskFactory.NewRunnableMethod(
             &PluginModuleParent::CleanupFromTimeout, isFromHangUI));
 
     if (!KillProcess(OtherProcess(), 1, false))
         NS_WARNING("failed to kill subprocess!");
 }
 
+bool
+PluginModuleParent::GetPluginDetails(nsACString& aPluginName,
+                                     nsACString& aPluginVersion)
+{
+    nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+    if (!host) {
+        return false;
+    }
+    nsPluginTag* pluginTag = host->TagForPlugin(mPlugin);
+    if (!pluginTag) {
+        return false;
+    }
+    aPluginName = pluginTag->mName;
+    aPluginVersion = pluginTag->mVersion;
+    return true;
+}
+
 #ifdef XP_WIN
 void
 PluginModuleParent::EvaluateHangUIState(const bool aReset)
 {
     int32_t minDispSecs = Preferences::GetInt(kHangUIMinDisplayPref, 10);
     int32_t autoStopSecs = Preferences::GetInt(kChildTimeoutPref, 0);
     int32_t timeoutSecs = 0;
     if (autoStopSecs > 0 && autoStopSecs < minDispSecs) {
@@ -563,56 +612,43 @@ PluginModuleParent::EvaluateHangUIState(
             autoStopSecs *= 2;
         }
     }
     mIsTimerReset = false;
     SetChildTimeout(autoStopSecs);
 }
 
 bool
-PluginModuleParent::GetPluginName(nsAString& aPluginName)
-{
-    nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
-    if (!host) {
-        return false;
-    }
-    nsPluginTag* pluginTag = host->TagForPlugin(mPlugin);
-    if (!pluginTag) {
-        return false;
-    }
-    CopyUTF8toUTF16(pluginTag->mName, aPluginName);
-    return true;
-}
-
-bool
 PluginModuleParent::LaunchHangUI()
 {
     if (!mHangUIEnabled) {
         return false;
     }
     if (mHangUIParent) {
         if (mHangUIParent->IsShowing()) {
             // We've already shown the UI but the timeout has expired again.
             return false;
         }
         if (mHangUIParent->DontShowAgain()) {
-            return !mHangUIParent->WasLastHangStopped();
+            mHangAnnotationFlags |= kHangUIDontShow;
+            bool wasLastHangStopped = mHangUIParent->WasLastHangStopped();
+            if (!wasLastHangStopped) {
+                mHangAnnotationFlags |= kHangUIContinued;
+            }
+            return !wasLastHangStopped;
         }
         delete mHangUIParent;
         mHangUIParent = nullptr;
     }
     mHangUIParent = new PluginHangUIParent(this, 
             Preferences::GetInt(kHangUITimeoutPref, 0),
             Preferences::GetInt(kChildTimeoutPref, 0));
-    nsAutoString pluginName;
-    if (!GetPluginName(pluginName)) {
-        return false;
-    }
-    bool retval = mHangUIParent->Init(pluginName);
+    bool retval = mHangUIParent->Init(NS_ConvertUTF8toUTF16(mPluginName));
     if (retval) {
+        mHangAnnotationFlags |= kHangUIShown;
         /* Once the UI is shown we switch the timeout over to use 
            kChildTimeoutPref, allowing us to terminate a hung plugin 
            after kChildTimeoutPref seconds if the user doesn't respond to 
            the hang UI. */
         EvaluateHangUIState(false);
     }
     return retval;
 }
@@ -632,16 +668,22 @@ PluginModuleParent::FinishHangUI()
             !mIsTimerReset && mHangUIParent->WasShown()) {
             /* We changed the timeout to kChildTimeoutPref when the plugin hang
                UI was displayed. Now that we're finishing the UI, we need to 
                switch it back to kHangUITimeoutPref. */
             EvaluateHangUIState(true);
         }
     }
 }
+
+void
+PluginModuleParent::OnHangUIContinue()
+{
+    mHangAnnotationFlags |= kHangUIContinued;
+}
 #endif // XP_WIN
 
 #ifdef MOZ_CRASHREPORTER
 CrashReporterParent*
 PluginModuleParent::CrashReporter()
 {
     return static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
 }
@@ -1335,16 +1377,20 @@ PluginModuleParent::NPP_New(NPMIMEType p
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
+    if (mPluginName.IsEmpty()) {
+        GetPluginDetails(mPluginName, mPluginVersion);
+    }
+
     // create the instance on the other side
     InfallibleTArray<nsCString> names;
     InfallibleTArray<nsCString> values;
 
     for (int i = 0; i < argc; ++i) {
         names.AppendElement(NullableString(argn[i]));
         values.AppendElement(NullableString(argv[i]));
     }
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -4,16 +4,17 @@
  * 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 mozilla_plugins_PluginModuleParent_h
 #define mozilla_plugins_PluginModuleParent_h
 
 #include "base/process.h"
 #include "mozilla/FileUtils.h"
+#include "mozilla/HangMonitor.h"
 #include "mozilla/PluginLibrary.h"
 #include "mozilla/plugins/ScopedMethodFactory.h"
 #include "mozilla/plugins/PluginProcessParent.h"
 #include "mozilla/plugins/PPluginModuleParent.h"
 #include "mozilla/plugins/PluginMessageUtils.h"
 #include "npapi.h"
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
@@ -54,16 +55,17 @@ class PluginHangUIParent;
  * This class is responsible for "actually" making those function calls.
  */
 class PluginModuleParent
     : public PPluginModuleParent
     , public PluginLibrary
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     , public CrashReporter::InjectorCrashCallback
 #endif
+    , public mozilla::HangMonitor::Annotator
 {
 private:
     typedef mozilla::PluginLibrary PluginLibrary;
     typedef mozilla::dom::PCrashReporterParent PCrashReporterParent;
     typedef mozilla::dom::CrashReporterParent CrashReporterParent;
 
 protected:
 
@@ -124,20 +126,33 @@ public:
      */
     PluginIdentifierParent*
     GetIdentifierForNPIdentifier(NPP npp, NPIdentifier aIdentifier);
 
     void ProcessRemoteNativeEventsInInterruptCall();
 
     void TerminateChildProcess(MessageLoop* aMsgLoop);
 
+    virtual void
+    EnteredCxxStack() MOZ_OVERRIDE;
+
+    virtual void
+    ExitedCxxStack() MOZ_OVERRIDE;
+
+    virtual void
+    AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) MOZ_OVERRIDE;
+
 #ifdef XP_WIN
+    /**
+     * Called by Plugin Hang UI to notify that the user has clicked continue.
+     * Used for chrome hang annotations.
+     */
     void
-    ExitedCxxStack() MOZ_OVERRIDE;
-#endif // XP_WIN
+    OnHangUIContinue();
+#endif
 
 protected:
     virtual mozilla::ipc::RacyInterruptPolicy
     MediateInterruptRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
@@ -300,16 +315,26 @@ private:
     const NPNetscapeFuncs* mNPNIface;
     nsDataHashtable<nsPtrHashKey<void>, PluginIdentifierParent*> mIdentifiers;
     nsNPAPIPlugin* mPlugin;
     ScopedMethodFactory<PluginModuleParent> mTaskFactory;
     nsString mPluginDumpID;
     nsString mBrowserDumpID;
     nsString mHangID;
     nsRefPtr<nsIObserver> mProfilerObserver;
+    enum HangAnnotationFlags
+    {
+        kInPluginCall = (1u << 0),
+        kHangUIShown = (1u << 1),
+        kHangUIContinued = (1u << 2),
+        kHangUIDontShow = (1u << 3)
+    };
+    Atomic<uint32_t> mHangAnnotationFlags;
+    nsCString mPluginName;
+    nsCString mPluginVersion;
 #ifdef XP_WIN
     InfallibleTArray<float> mPluginCpuUsageOnHang;
     PluginHangUIParent *mHangUIParent;
     bool mHangUIEnabled;
     bool mIsTimerReset;
 #ifdef MOZ_CRASHREPORTER
     /**
      * This mutex protects the crash reporter when the Plugin Hang UI event
@@ -320,19 +345,16 @@ private:
     mozilla::Mutex mCrashReporterMutex;
     CrashReporterParent* mCrashReporter;
 #endif // MOZ_CRASHREPORTER
 
 
     void
     EvaluateHangUIState(const bool aReset);
 
-    bool
-    GetPluginName(nsAString& aPluginName);
-
     /**
      * Launches the Plugin Hang UI.
      *
      * @return true if plugin-hang-ui.exe has been successfully launched.
      *         false if the Plugin Hang UI is disabled, already showing,
      *               or the launch failed.
      */
     bool
@@ -340,16 +362,19 @@ private:
 
     /**
      * Finishes the Plugin Hang UI and cancels if it is being shown to the user.
      */
     void
     FinishHangUI();
 #endif
 
+    bool
+    GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion);
+
 #ifdef MOZ_X11
     // Dup of plugin's X socket, used to scope its resources to this
     // object instead of the plugin process's lifetime
     ScopedClose mPluginXSocketFdDup;
 #endif
 
     friend class mozilla::dom::CrashReporterParent;