Bug 1262852 - Create a minidump of the plugin process as soon as possible during hang r=jimm

This commit is contained in:
Gabriele Svelto 2016-06-24 14:25:08 +02:00
Родитель 8d93701b0a
Коммит 35b1fc1eb3
4 изменённых файлов: 147 добавлений и 54 удалений

Просмотреть файл

@ -162,14 +162,25 @@ public:
mActor = nullptr; mActor = nullptr;
} }
/** Sets the information associated with this hang: this includes the ID of
* the plugin which caused the hang as well as the content PID.
*
* @param aHangData The hang information
*/
void SetHangData(const HangData& aHangData) { mHangData = aHangData; } void SetHangData(const HangData& aHangData) { mHangData = aHangData; }
void SetBrowserDumpId(nsAutoString& aId) {
mBrowserDumpId = aId; /** Sets the ID of the crash dump associated with this hang. When the ID has
* been set then the corresponding crash dump will be used for reporting
* instead of generating a new one.
*
* @param aId The ID of the crash dump taken when the hang was detected. */
void SetDumpId(nsString& aId) {
mDumpId = aId;
} }
void ClearHang() { void ClearHang() {
mHangData = HangData(); mHangData = HangData();
mBrowserDumpId.Truncate(); mDumpId.Truncate();
} }
private: private:
@ -179,7 +190,7 @@ private:
HangMonitorParent* mActor; HangMonitorParent* mActor;
ContentParent* mContentParent; ContentParent* mContentParent;
HangData mHangData; HangData mHangData;
nsAutoString mBrowserDumpId; nsAutoString mDumpId;
}; };
class HangMonitorParent class HangMonitorParent
@ -551,8 +562,16 @@ public:
{ {
// chrome process, main thread // chrome process, main thread
MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsString dumpId;
if (mHangData.type() == HangData::TPluginHangData) {
const PluginHangData& phd = mHangData.get_PluginHangData();
plugins::TakeFullMinidump(phd.pluginId(), phd.contentProcessId(),
mBrowserDumpId, dumpId);
}
mProcess->SetHangData(mHangData); mProcess->SetHangData(mHangData);
mProcess->SetBrowserDumpId(mBrowserDumpId); mProcess->SetDumpId(dumpId);
nsCOMPtr<nsIObserverService> observerService = nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService(); mozilla::services::GetObserverService();
@ -861,12 +880,11 @@ HangMonitoredProcess::TerminatePlugin()
return NS_ERROR_UNEXPECTED; return NS_ERROR_UNEXPECTED;
} }
// generates a crash report that includes a browser report taken here // Use the multi-process crash report generated earlier.
// earlier, the content process, and any plugin process(es).
uint32_t id = mHangData.get_PluginHangData().pluginId(); uint32_t id = mHangData.get_PluginHangData().pluginId();
base::ProcessId contentPid = mHangData.get_PluginHangData().contentProcessId(); base::ProcessId contentPid = mHangData.get_PluginHangData().contentProcessId();
plugins::TerminatePlugin(id, contentPid, NS_LITERAL_CSTRING("HangMonitor"), plugins::TerminatePlugin(id, contentPid, NS_LITERAL_CSTRING("HangMonitor"),
mBrowserDumpId); mDumpId);
if (mActor) { if (mActor) {
mActor->CleanupPluginHang(id, false); mActor->CleanupPluginHang(id, false);

Просмотреть файл

@ -26,11 +26,17 @@ FindPluginsForContent(uint32_t aPluginEpoch,
nsTArray<PluginTag>* aPlugins, nsTArray<PluginTag>* aPlugins,
uint32_t* aNewPluginEpoch); uint32_t* aNewPluginEpoch);
void
TakeFullMinidump(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsAString& aBrowserDumpId,
nsString& aDumpId);
void void
TerminatePlugin(uint32_t aPluginId, TerminatePlugin(uint32_t aPluginId,
base::ProcessId aContentProcessId, base::ProcessId aContentProcessId,
const nsCString& aMonitorDescription, const nsCString& aMonitorDescription,
const nsAString& aBrowserDumpId); const nsAString& aDumpId);
} // namespace plugins } // namespace plugins
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -353,26 +353,50 @@ bool PluginModuleMapping::sIsLoadModuleOnStack = false;
} // namespace } // namespace
void static PluginModuleChromeParent*
mozilla::plugins::TerminatePlugin(uint32_t aPluginId, PluginModuleChromeParentForId(const uint32_t aPluginId)
base::ProcessId aContentProcessId,
const nsCString& aMonitorDescription,
const nsAString& aBrowserDumpId)
{ {
MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(XRE_IsParentProcess());
RefPtr<nsPluginHost> host = nsPluginHost::GetInst(); RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
nsPluginTag* pluginTag = host->PluginWithId(aPluginId); nsPluginTag* pluginTag = host->PluginWithId(aPluginId);
if (!pluginTag || !pluginTag->mPlugin) { if (!pluginTag || !pluginTag->mPlugin) {
return; return nullptr;
} }
RefPtr<nsNPAPIPlugin> plugin = pluginTag->mPlugin; RefPtr<nsNPAPIPlugin> plugin = pluginTag->mPlugin;
return static_cast<PluginModuleChromeParent*>(plugin->GetLibrary());
}
void
mozilla::plugins::TakeFullMinidump(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsAString& aBrowserDumpId,
nsString& aDumpId)
{
PluginModuleChromeParent* chromeParent = PluginModuleChromeParent* chromeParent =
static_cast<PluginModuleChromeParent*>(plugin->GetLibrary()); PluginModuleChromeParentForId(aPluginId);
if (chromeParent) {
chromeParent->TakeFullMinidump(aContentProcessId, aBrowserDumpId, aDumpId);
}
}
void
mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsCString& aMonitorDescription,
const nsAString& aDumpId)
{
PluginModuleChromeParent* chromeParent =
PluginModuleChromeParentForId(aPluginId);
if (chromeParent) {
chromeParent->TerminateChildProcess(MessageLoop::current(), chromeParent->TerminateChildProcess(MessageLoop::current(),
aContentProcessId, aContentProcessId,
aMonitorDescription, aMonitorDescription,
aBrowserDumpId); aDumpId);
}
} }
/* static */ PluginLibrary* /* static */ PluginLibrary*
@ -1198,38 +1222,19 @@ PluginModuleContentParent::OnExitedSyncSend()
} }
void void
PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop, PluginModuleChromeParent::TakeFullMinidump(base::ProcessId aContentPid,
base::ProcessId aContentPid, const nsAString& aBrowserDumpId,
const nsCString& aMonitorDescription, nsString& aDumpId)
const nsAString& aBrowserDumpId)
{ {
#ifdef MOZ_CRASHREPORTER #ifdef MOZ_CRASHREPORTER
#ifdef XP_WIN #ifdef XP_WIN
mozilla::MutexAutoLock lock(mCrashReporterMutex); mozilla::MutexAutoLock lock(mCrashReporterMutex);
CrashReporterParent* crashReporter = mCrashReporter; #endif // XP_WIN
CrashReporterParent* crashReporter = CrashReporter();
if (!crashReporter) { if (!crashReporter) {
// If mCrashReporter is null then the hang has ended, the plugin module
// is shutting down. There's nothing to do here.
return; return;
} }
#else
CrashReporterParent* crashReporter = CrashReporter();
#endif
crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("PluginHang"),
NS_LITERAL_CSTRING("1"));
crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("HangMonitorDescription"),
aMonitorDescription);
#ifdef XP_WIN
if (mHangUIParent) {
unsigned int hangUIDuration = mHangUIParent->LastShowDurationMs();
if (hangUIDuration) {
nsPrintfCString strHangUIDuration("%u", hangUIDuration);
crashReporter->AnnotateCrashReport(
NS_LITERAL_CSTRING("PluginHangUIDuration"),
strHangUIDuration);
}
}
#endif // XP_WIN
bool reportsReady = false; bool reportsReady = false;
@ -1266,6 +1271,7 @@ PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
// Important to set this here, it tells the ActorDestroy handler // Important to set this here, it tells the ActorDestroy handler
// that we have an existing crash report that needs to be finalized. // that we have an existing crash report that needs to be finalized.
mPluginDumpID = crashReporter->ChildDumpID(); mPluginDumpID = crashReporter->ChildDumpID();
aDumpId = mPluginDumpID;
PLUGIN_LOG_DEBUG( PLUGIN_LOG_DEBUG(
("generated paired browser/plugin minidumps: %s)", ("generated paired browser/plugin minidumps: %s)",
NS_ConvertUTF16toUTF8(mPluginDumpID).get())); NS_ConvertUTF16toUTF8(mPluginDumpID).get()));
@ -1284,7 +1290,7 @@ PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
NS_LITERAL_CSTRING("flash2"))) { NS_LITERAL_CSTRING("flash2"))) {
additionalDumps.AppendLiteral(",flash2"); additionalDumps.AppendLiteral(",flash2");
} }
#endif #endif // MOZ_CRASHREPORTER_INJECTOR
if (aContentPid != mozilla::ipc::kInvalidProcessId) { if (aContentPid != mozilla::ipc::kInvalidProcessId) {
// Include the content process minidump // Include the content process minidump
if (CreatePluginMinidump(aContentPid, 0, if (CreatePluginMinidump(aContentPid, 0,
@ -1300,7 +1306,51 @@ PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
} else { } else {
NS_WARNING("failed to capture paired minidumps from hang"); NS_WARNING("failed to capture paired minidumps from hang");
} }
#endif #endif // MOZ_CRASHREPORTER
}
void
PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid,
const nsCString& aMonitorDescription,
const nsAString& aDumpId)
{
#ifdef MOZ_CRASHREPORTER
// Start by taking a full minidump if necessary, this is done early
// because it also needs to lock the mCrashReporterMutex and Mutex doesn't
// support recrusive locking.
nsAutoString dumpId;
if (aDumpId.IsEmpty()) {
TakeFullMinidump(aContentPid, EmptyString(), dumpId);
}
#ifdef XP_WIN
mozilla::MutexAutoLock lock(mCrashReporterMutex);
CrashReporterParent* crashReporter = mCrashReporter;
if (!crashReporter) {
// If mCrashReporter is null then the hang has ended, the plugin module
// is shutting down. There's nothing to do here.
return;
}
#else
CrashReporterParent* crashReporter = CrashReporter();
#endif // XP_WIN
crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("PluginHang"),
NS_LITERAL_CSTRING("1"));
crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("HangMonitorDescription"),
aMonitorDescription);
#ifdef XP_WIN
if (mHangUIParent) {
unsigned int hangUIDuration = mHangUIParent->LastShowDurationMs();
if (hangUIDuration) {
nsPrintfCString strHangUIDuration("%u", hangUIDuration);
crashReporter->AnnotateCrashReport(
NS_LITERAL_CSTRING("PluginHangUIDuration"),
strHangUIDuration);
}
}
#endif // XP_WIN
#endif // MOZ_CRASHREPORTER
mozilla::ipc::ScopedProcessHandle geckoChildProcess; mozilla::ipc::ScopedProcessHandle geckoChildProcess;
bool childOpened = base::OpenProcessHandle(OtherPid(), bool childOpened = base::OpenProcessHandle(OtherPid(),

Просмотреть файл

@ -418,10 +418,29 @@ class PluginModuleChromeParent
virtual ~PluginModuleChromeParent(); virtual ~PluginModuleChromeParent();
/*
* Takes a full multi-process dump including the plugin process and the
* content process. If aBrowserDumpId is not empty then the browser dump
* associated with it will be paired to the resulting minidump.
* Takes ownership of the file associated with aBrowserDumpId.
*
* @param aContentPid PID of the e10s content process from which a hang was
* reported. May be kInvalidProcessId if not applicable.
* @param aBrowserDumpId (optional) previously taken browser dump id. If
* provided TakeFullMinidump will use this dump file instead of
* generating a new one. If not provided a browser dump will be taken at
* the time of this call.
* @param aDumpId Returns the ID of the newly generated crash dump. Left
* untouched upon failure.
*/
void TakeFullMinidump(base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
nsString& aDumpId);
/* /*
* Terminates the plugin process associated with this plugin module. Also * Terminates the plugin process associated with this plugin module. Also
* generates appropriate crash reports. Takes ownership of the file * generates appropriate crash reports unless an existing one is provided.
* associated with aBrowserDumpId on success. * Takes ownership of the file associated with aDumpId on success.
* *
* @param aMsgLoop the main message pump associated with the module * @param aMsgLoop the main message pump associated with the module
* protocol. * protocol.
@ -430,15 +449,15 @@ class PluginModuleChromeParent
* @param aMonitorDescription a string describing the hang monitor that * @param aMonitorDescription a string describing the hang monitor that
* is making this call. This string is added to the crash reporter * is making this call. This string is added to the crash reporter
* annotations for the plugin process. * annotations for the plugin process.
* @param aBrowserDumpId (optional) previously taken browser dump id. If * @param aDumpId (optional) previously taken dump id. If provided
* provided TerminateChildProcess will use this browser dump file in * TerminateChildProcess will use this dump file instead of generating a
* generating a multi-process crash report. If not provided a browser * multi-process crash report. If not provided a multi-process dump will
* dump will be taken at the time of this call. * be taken at the time of this call.
*/ */
void TerminateChildProcess(MessageLoop* aMsgLoop, void TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid, base::ProcessId aContentPid,
const nsCString& aMonitorDescription, const nsCString& aMonitorDescription,
const nsAString& aBrowserDumpId); const nsAString& aDumpId);
#ifdef XP_WIN #ifdef XP_WIN
/** /**