Bug 1183433 - Implement centralized crash report annotations, so that all async-shutdown hangs use only one key, and keeping track of all states each plugin has been through. r=cpearce, a=lmandel
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -30,16 +30,17 @@
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsHashKeys.h"
#include "nsIFile.h"
#include "nsISimpleEnumerator.h"
#if defined(MOZ_CRASHREPORTER)
#include "nsExceptionHandler.h"
+#include "nsPrintfCString.h"
#endif
#include <limits>
namespace mozilla {
#ifdef LOG
#undef LOG
#endif
@@ -396,16 +397,87 @@ GeckoMediaPluginServiceParent::AsyncShut
// The main thread may be waiting for async shutdown of plugins,
// one of which has completed. Wake up the main thread by sending a task.
nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
this, &GeckoMediaPluginServiceParent::NotifyAsyncShutdownComplete));
NS_DispatchToMainThread(task);
}
}
+#ifdef MOZ_CRASHREPORTER
+void
+GeckoMediaPluginServiceParent::SetAsyncShutdownPluginState(GMPParent* aGMPParent,
+ char aId,
+ const nsCString& aState)
+{
+ MutexAutoLock lock(mMutex);
+ mAsyncShutdownPluginStates.Update(aGMPParent->GetDisplayName(),
+ nsPrintfCString("%p", aGMPParent),
+ aId,
+ aState);
+}
+
+void
+GeckoMediaPluginServiceParent::AsyncShutdownPluginStates::Update(const nsCString& aPlugin,
+ const nsCString& aInstance,
+ char aId,
+ const nsCString& aState)
+{
+ nsCString note;
+ StatesByInstance* instances = mStates.LookupOrAdd(aPlugin);
+ if (!instances) { return; }
+ State* state = instances->LookupOrAdd(aInstance);
+ if (!state) { return; }
+ state->mStateSequence += aId;
+ state->mLastStateDescription = aState;
+ note += '{';
+ mStates.EnumerateRead(EnumReadPlugins, ¬e);
+ note += '}';
+ LOGD(("%s::%s states[%s][%s]='%c'/'%s' -> %s", __CLASS__, __FUNCTION__,
+ aPlugin.get(), aInstance.get(), aId, aState.get(), note.get()));
+ CrashReporter::AnnotateCrashReport(
+ NS_LITERAL_CSTRING("AsyncPluginShutdownStates"),
+ note);
+}
+
+// static
+PLDHashOperator
+GeckoMediaPluginServiceParent::AsyncShutdownPluginStates::EnumReadPlugins(
+ StateInstancesByPlugin::KeyType aKey,
+ StateInstancesByPlugin::UserDataType aData,
+ void* aUserArg)
+{
+ nsCString& note = *static_cast<nsCString*>(aUserArg);
+ if (note.Last() != '{') { note += ','; }
+ note += aKey;
+ note += ":{";
+ aData->EnumerateRead(EnumReadInstances, ¬e);
+ note += '}';
+ return PL_DHASH_NEXT;
+}
+
+// static
+PLDHashOperator
+GeckoMediaPluginServiceParent::AsyncShutdownPluginStates::EnumReadInstances(
+ StatesByInstance::KeyType aKey,
+ StatesByInstance::UserDataType aData,
+ void* aUserArg)
+{
+ nsCString& note = *static_cast<nsCString*>(aUserArg);
+ if (note.Last() != '{') { note += ','; }
+ note += aKey;
+ note += ":\"";
+ note += aData->mStateSequence;
+ note += '=';
+ note += aData->mLastStateDescription;
+ note += '"';
+ return PL_DHASH_NEXT;
+}
+#endif // MOZ_CRASHREPORTER
+
void
GeckoMediaPluginServiceParent::NotifyAsyncShutdownComplete()
{
MOZ_ASSERT(NS_IsMainThread());
// Nothing to do, this task is just used to wake up the event loop in Observe().
}
void
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -46,16 +46,19 @@ public:
NS_DECL_MOZIGECKOMEDIAPLUGINCHROMESERVICE
NS_DECL_NSIOBSERVER
void AsyncShutdownNeeded(GMPParent* aParent);
void AsyncShutdownComplete(GMPParent* aParent);
int32_t AsyncShutdownTimeoutMs();
+#ifdef MOZ_CRASHREPORTER
+ void SetAsyncShutdownPluginState(GMPParent* aGMPParent, char aId, const nsCString& aState);
+#endif // MOZ_CRASHREPORTER
private:
friend class GMPServiceParent;
virtual ~GeckoMediaPluginServiceParent();
void ClearStorage();
@@ -134,16 +137,35 @@ private:
EOperation mOperation;
bool mDefer;
};
// Protected by mMutex from the base class.
nsTArray<nsRefPtr<GMPParent>> mPlugins;
bool mShuttingDown;
nsTArray<nsRefPtr<GMPParent>> mAsyncShutdownPlugins;
+#ifdef MOZ_CRASHREPORTER
+ class AsyncShutdownPluginStates
+ {
+ public:
+ void Update(const nsCString& aPlugin, const nsCString& aInstance,
+ char aId, const nsCString& aState);
+ private:
+ struct State { nsAutoCString mStateSequence; nsCString mLastStateDescription; };
+ typedef nsClassHashtable<nsCStringHashKey, State> StatesByInstance;
+ typedef nsClassHashtable<nsCStringHashKey, StatesByInstance> StateInstancesByPlugin;
+ static PLDHashOperator EnumReadPlugins(StateInstancesByPlugin::KeyType aKey,
+ StateInstancesByPlugin::UserDataType aData,
+ void* aUserArg);
+ static PLDHashOperator EnumReadInstances(StatesByInstance::KeyType aKey,
+ StatesByInstance::UserDataType aData,
+ void* aUserArg);
+ StateInstancesByPlugin mStates;
+ } mAsyncShutdownPluginStates;
+#endif // MOZ_CRASHREPORTER
// True if we've inspected MOZ_GMP_PATH on the GMP thread and loaded any
// plugins found there into mPlugins.
Atomic<bool> mScannedPluginOnDisk;
template<typename T>
class MainThreadOnly {
public: