Bug 1360308 - Part 2: Use callback in CrashReporterHost::GenerateMinidumpAndPair() and up the caller chain. r=gsvelto

This changes CrashReporterHost::GenerateMinidumpAndPair() and up the caller chain to use callbacks so we
may call it synchronously or asynchronously.

MozReview-Commit-ID: 4PQH6cVdOk0
This commit is contained in:
Cervantes Yu
2017-06-22 18:52:58 +08:00
parent 7c616228a2
commit b2491ab85e
9 changed files with 412 additions and 86 deletions

View File

@@ -375,13 +375,19 @@ void
mozilla::plugins::TakeFullMinidump(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsAString& aBrowserDumpId,
nsString& aDumpId)
std::function<void(nsString)>&& aCallback,
bool aAsync)
{
PluginModuleChromeParent* chromeParent =
PluginModuleChromeParentForId(aPluginId);
if (chromeParent) {
chromeParent->TakeFullMinidump(aContentProcessId, aBrowserDumpId, aDumpId);
chromeParent->TakeFullMinidump(aContentProcessId,
aBrowserDumpId,
Move(aCallback),
aAsync);
} else {
aCallback(EmptyString());
}
}
@@ -389,7 +395,8 @@ void
mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsCString& aMonitorDescription,
const nsAString& aDumpId)
const nsAString& aDumpId,
std::function<void(bool)>&& aCallback)
{
PluginModuleChromeParent* chromeParent =
PluginModuleChromeParentForId(aPluginId);
@@ -398,7 +405,11 @@ mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
chromeParent->TerminateChildProcess(MessageLoop::current(),
aContentProcessId,
aMonitorDescription,
aDumpId);
aDumpId,
Move(aCallback),
true); // Always runs asynchronously.
} else {
aCallback(true);
}
}
@@ -1156,10 +1167,15 @@ PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
// original plugin hang behaviour and kill the plugin container.
FinishHangUI();
#endif // XP_WIN
// Terminate the child process synchronously because this function can be
// called in sync IPC.
TerminateChildProcess(MessageLoop::current(),
mozilla::ipc::kInvalidProcessId,
NS_LITERAL_CSTRING("ModalHangUI"),
EmptyString());
EmptyString(),
DummyCallback<bool>(),
/* aAsync = */ false);
GetIPCChannel()->CloseWithTimeout();
return false;
}
@@ -1184,56 +1200,144 @@ PluginModuleContentParent::OnExitedSyncSend()
void
PluginModuleChromeParent::TakeFullMinidump(base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
nsString& aDumpId)
std::function<void(nsString)>&& aCallback,
bool aAsync)
{
#ifdef MOZ_CRASHREPORTER
mozilla::MutexAutoLock lock(mCrashReporterMutex);
if (!mCrashReporter) {
if (!mCrashReporter || !mTakeFullMinidumpCallback.IsEmpty()) {
aCallback(EmptyString());
return;
}
mTakeFullMinidumpCallback.Init(Move(aCallback), aAsync);
bool reportsReady = false;
nsString browserDumpId{aBrowserDumpId};
// Check to see if we already have a browser dump id - with e10s plugin
// hangs we take this earlier (see ProcessHangMonitor) from a background
// thread. We do this before we message the main thread about the hang
// since the posted message will trash our browser stack state.
nsCOMPtr<nsIFile> browserDumpFile;
if (CrashReporter::GetMinidumpForID(aBrowserDumpId,
getter_AddRefs(browserDumpFile))) {
getter_AddRefs(mBrowserDumpFile))) {
// Hold a ref to mPlugin to keep *this* alive until the callback runs.
RetainPluginRef();
std::function<void(bool)> callback =
[this, aContentPid, browserDumpId, aAsync](bool aResult) {
if (aAsync) {
this->mCrashReporterMutex.Lock();
}
this->TakeBrowserAndPluginMinidumps(aResult,
aContentPid,
browserDumpId,
aAsync);
if (aAsync) {
this->mCrashReporterMutex.Unlock();
}
this->ReleasePluginRef();
};
// We have a single browser report, generate a new plugin process parent
// report and pair it up with the browser report handed in.
reportsReady = mCrashReporter->GenerateMinidumpAndPair(
Process(),
browserDumpFile,
NS_LITERAL_CSTRING("browser"));
if (!reportsReady) {
browserDumpFile = nullptr;
CrashReporter::DeleteMinidumpFilesForID(aBrowserDumpId);
}
mCrashReporter->GenerateMinidumpAndPair(Process(), mBrowserDumpFile,
NS_LITERAL_CSTRING("browser"),
Move(callback), aAsync);
} else {
TakeBrowserAndPluginMinidumps(false, aContentPid, browserDumpId, aAsync);
}
#else // MOZ_CRASHREPORTER
aCallback(NS_LITERAL_STRING(""));
#endif
}
#ifdef MOZ_CRASHREPORTER
void
PluginModuleChromeParent::RetainPluginRef()
{
if (!mPlugin) {
return;
}
if (NS_IsMainThread()) {
mPlugin->AddRef();
} else {
// XXX We can't sync-dispatch to the main thread because doing that
// deadlocks when we are called from
// PluginHangUIParent::RecvUserResponse().
Unused << NS_DispatchToMainThread(
NewNonOwningRunnableMethod(mPlugin, &nsNPAPIPlugin::AddRef));
}
}
void
PluginModuleChromeParent::ReleasePluginRef()
{
if (!mPlugin) {
return;
}
if (NS_IsMainThread()) {
mPlugin->Release();
} else {
// Async release the reference to mPlugin.
Unused << NS_DispatchToMainThread(
NewNonOwningRunnableMethod(mPlugin, &nsNPAPIPlugin::Release));
}
}
void
PluginModuleChromeParent::TakeBrowserAndPluginMinidumps(bool aReportsReady,
base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
bool aAsync)
{
mCrashReporterMutex.AssertCurrentThreadOwns();
// Generate crash report including plugin and browser process minidumps.
// The plugin process is the parent report with additional dumps including
// the browser process, content process when running under e10s, and
// various flash subprocesses if we're the flash module.
if (!reportsReady) {
reportsReady = mCrashReporter->GenerateMinidumpAndPair(
Process(),
nullptr, // Pair with a dump of this process and thread.
NS_LITERAL_CSTRING("browser"));
}
if (!aReportsReady) {
mBrowserDumpFile = nullptr;
CrashReporter::DeleteMinidumpFilesForID(aBrowserDumpId);
if (reportsReady) {
aDumpId = mCrashReporter->MinidumpID();
nsString browserDumpId{aBrowserDumpId};
RetainPluginRef();
std::function<void(bool)> callback =
[this, aContentPid, browserDumpId](bool aResult) {
this->OnTakeFullMinidumpComplete(aResult,
aContentPid,
browserDumpId);
this->ReleasePluginRef();
};
mCrashReporter->GenerateMinidumpAndPair(Process(),
nullptr, // Pair with a dump of this process and thread.
NS_LITERAL_CSTRING("browser"),
Move(callback),
aAsync);
} else {
OnTakeFullMinidumpComplete(aReportsReady, aContentPid, aBrowserDumpId);
}
}
void
PluginModuleChromeParent::OnTakeFullMinidumpComplete(bool aReportsReady,
base::ProcessId aContentPid,
const nsAString& aBrowserDumpId)
{
mCrashReporterMutex.AssertCurrentThreadOwns();
if (aReportsReady) {
nsString dumpId = mCrashReporter->MinidumpID();
PLUGIN_LOG_DEBUG(
("generated paired browser/plugin minidumps: %s)",
NS_ConvertUTF16toUTF8(aDumpId).get()));
("generated paired browser/plugin minidumps: %s)",
NS_ConvertUTF16toUTF8(dumpId).get()));
nsAutoCString additionalDumps("browser");
nsCOMPtr<nsIFile> pluginDumpFile;
if (GetMinidumpForID(aDumpId, getter_AddRefs(pluginDumpFile))) {
if (GetMinidumpForID(dumpId, getter_AddRefs(pluginDumpFile))) {
#ifdef MOZ_CRASHREPORTER_INJECTOR
// If we have handles to the flash sandbox processes on Windows,
// include those minidumps as well.
@@ -1255,34 +1359,74 @@ PluginModuleChromeParent::TakeFullMinidump(base::ProcessId aContentPid,
}
}
}
mCrashReporter->AddNote(
NS_LITERAL_CSTRING("additional_minidumps"),
additionalDumps);
mCrashReporter->AddNote(NS_LITERAL_CSTRING("additional_minidumps"),
additionalDumps);
mTakeFullMinidumpCallback.Invoke(mCrashReporter->MinidumpID());
} else {
mTakeFullMinidumpCallback.Invoke(EmptyString());
NS_WARNING("failed to capture paired minidumps from hang");
}
#endif // MOZ_CRASHREPORTER
}
#endif // MOZ_CRASHREPORTER
void
PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid,
const nsCString& aMonitorDescription,
const nsAString& aDumpId)
const nsAString& aDumpId,
std::function<void(bool)>&& aCallback,
bool aAsync)
{
if (!mTerminateChildProcessCallback.IsEmpty()) {
aCallback(false);
return;
}
mTerminateChildProcessCallback.Init(Move(aCallback), aAsync);
#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 recursive locking.
nsAutoString dumpId;
if (aDumpId.IsEmpty()) {
TakeFullMinidump(aContentPid, EmptyString(), dumpId);
RetainPluginRef();
std::function<void(nsString)> callback =
[this, aMsgLoop, aMonitorDescription, aAsync](nsString aResult) {
if (aAsync) {
this->mCrashReporterMutex.Lock();
}
this->TerminateChildProcessOnDumpComplete(aMsgLoop,
aMonitorDescription);
if (aAsync) {
this->mCrashReporterMutex.Unlock();
}
this->ReleasePluginRef();
};
TakeFullMinidump(aContentPid, EmptyString(), Move(callback), aAsync);
} else {
TerminateChildProcessOnDumpComplete(aMsgLoop, aMonitorDescription);
}
mozilla::MutexAutoLock lock(mCrashReporterMutex);
#else
TerminateChildProcessOnDumpComplete(aMsgLoop, aMonitorDescription);
#endif
}
void
PluginModuleChromeParent::TerminateChildProcessOnDumpComplete(MessageLoop* aMsgLoop,
const nsCString& aMonitorDescription)
{
#ifdef MOZ_CRASHREPORTER
mCrashReporterMutex.AssertCurrentThreadOwns();
if (!mCrashReporter) {
// If mCrashReporter is null then the hang has ended, the plugin module
// is shutting down. There's nothing to do here.
mTerminateChildProcessCallback.Invoke(true);
return;
}
mCrashReporter->AddNote(NS_LITERAL_CSTRING("PluginHang"),
@@ -1331,7 +1475,7 @@ PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
if (!GetProcessCpuUsage(processHandles, mPluginCpuUsageOnHang)) {
mPluginCpuUsageOnHang.Clear();
}
#endif
#endif // MOZ_CRASHREPORTER
// this must run before the error notification from the channel,
// or not at all
@@ -1343,6 +1487,8 @@ PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
if (!childOpened || !KillProcess(geckoChildProcess, 1, false)) {
NS_WARNING("failed to kill subprocess!");
}
mTerminateChildProcessCallback.Invoke(true);
}
bool