Bug 1463048 - Remove asynchronous minidump generation r=ted

This reverts the changes in bug 1360308, bug 1390143 and bug 1469603. Minidump
generation will now only happen on the main process' main thread which might
lead to hangs but is known to be fairly robust. Asynchronous generation proved
too brittle and enormously increased the complexity of this already
hard-to-read code.

Differential Revision: https://phabricator.services.mozilla.com/D5147

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Gabriele Svelto 2018-09-17 20:51:45 +00:00
Родитель e361f7ef17
Коммит a1f6255102
12 изменённых файлов: 162 добавлений и 656 удалений

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

@ -3381,34 +3381,17 @@ ContentParent::KillHard(const char* aReason)
mCrashReporter->AddAnnotation(CrashReporter::Annotation::ipc_channel_error,
reason);
Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
RefPtr<ContentParent> self = this;
std::function<void(bool)> callback = [self](bool aResult) {
self->OnGenerateMinidumpComplete(aResult);
};
// Generate the report and insert into the queue for submittal.
mCrashReporter->GenerateMinidumpAndPair(Process(),
nullptr,
NS_LITERAL_CSTRING("browser"),
std::move(callback),
true);
return;
if (mCrashReporter->GenerateMinidumpAndPair(this,
nullptr,
NS_LITERAL_CSTRING("browser")))
{
mCreatedPairedMinidumps = mCrashReporter->FinalizeCrashReport();
}
Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
}
OnGenerateMinidumpComplete(false);
}
void
ContentParent::OnGenerateMinidumpComplete(bool aDumpResult)
{
if (mCrashReporter && aDumpResult) {
// CrashReporterHost::GenerateMinidumpAndPair() is successful.
mCreatedPairedMinidumps = mCrashReporter->FinalizeCrashReport();
}
Unused << aDumpResult; // Don't care about result if no minidump was requested.
ProcessHandle otherProcessHandle;
if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle)) {
NS_ERROR("Failed to open child process when attempting kill.");

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

@ -862,8 +862,6 @@ private:
// Start the force-kill timer on shutdown.
void StartForceKillTimer();
void OnGenerateMinidumpComplete(bool aDumpResult);
// Ensure that the permissions for the giben Permission key are set in the
// content process.
//

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

@ -264,8 +264,6 @@ private:
void SendHangNotification(const HangData& aHangData,
const nsString& aBrowserDumpId,
bool aTakeMinidump);
void OnTakeFullMinidumpComplete(const HangData& aHangData,
const nsString& aDumpId);
void ClearHangNotification();
@ -738,48 +736,25 @@ HangMonitorParent::SendHangNotification(const HangData& aHangData,
// chrome process, main thread
MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsString dumpId;
if ((aHangData.type() == HangData::TPluginHangData) && aTakeMinidump) {
// We've been handed a partial minidump; complete it with plugin and
// content process dumps.
const PluginHangData& phd = aHangData.get_PluginHangData();
WeakPtr<HangMonitorParent> self = this;
std::function<void(nsString)> callback =
[self, aHangData](nsString aResult) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
if (!self) {
// Don't report hang since the process has already shut down.
return;
}
self->UpdateMinidump(aHangData.get_PluginHangData().pluginId(),
aResult);
self->OnTakeFullMinidumpComplete(aHangData, aResult);
};
plugins::TakeFullMinidump(phd.pluginId(),
phd.contentProcessId(),
aBrowserDumpId,
std::move(callback),
true);
plugins::TakeFullMinidump(phd.pluginId(), phd.contentProcessId(),
aBrowserDumpId, dumpId);
UpdateMinidump(phd.pluginId(), dumpId);
} else {
// We already have a full minidump; go ahead and use it.
OnTakeFullMinidumpComplete(aHangData, aBrowserDumpId);
dumpId = aBrowserDumpId;
}
}
void
HangMonitorParent::OnTakeFullMinidumpComplete(const HangData& aHangData,
const nsString& aDumpId)
{
mProcess->SetHangData(aHangData, aDumpId);
mProcess->SetHangData(aHangData, dumpId);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->NotifyObservers(mProcess,
"process-hang-report",
nullptr);
observerService->NotifyObservers(mProcess, "process-hang-report", nullptr);
}
void
@ -1117,20 +1092,12 @@ HangMonitoredProcess::TerminatePlugin()
// Use the multi-process crash report generated earlier.
uint32_t id = mHangData.get_PluginHangData().pluginId();
base::ProcessId contentPid = mHangData.get_PluginHangData().contentProcessId();
plugins::TerminatePlugin(id, contentPid, NS_LITERAL_CSTRING("HangMonitor"),
mDumpId);
RefPtr<HangMonitoredProcess> self{this};
std::function<void(bool)> callback =
[self, id](bool aResult) {
if (self->mActor) {
self->mActor->CleanupPluginHang(id, false);
}
};
plugins::TerminatePlugin(id,
contentPid,
NS_LITERAL_CSTRING("HangMonitor"),
mDumpId,
std::move(callback));
if (mActor) {
mActor->CleanupPluginHang(id, false);
}
return NS_OK;
}

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

@ -7,8 +7,6 @@
#ifndef mozilla_plugins_PluginBridge_h
#define mozilla_plugins_PluginBridge_h
#include <functional>
#include "base/process.h"
namespace mozilla {
@ -41,15 +39,14 @@ void
TakeFullMinidump(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsAString& aBrowserDumpId,
std::function<void(nsString)>&& aCallback,
bool aAsync);
nsString& aDumpId);
void
TerminatePlugin(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsCString& aMonitorDescription,
const nsAString& aDumpId,
std::function<void(bool)>&& aCallback);
const nsAString& aDumpId);
} // namespace plugins
} // namespace mozilla

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

@ -355,13 +355,10 @@ PluginHangUIParent::RecvUserResponse(const unsigned int& aResponse)
int responseCode;
if (aResponse & HANGUI_USER_RESPONSE_STOP) {
// User clicked Stop
std::function<void(bool)> callback = [](bool aResult) { };
mModule->TerminateChildProcess(mMainThreadMessageLoop,
mozilla::ipc::kInvalidProcessId,
NS_LITERAL_CSTRING("ModalHangUI"),
EmptyString(),
mModule->DummyCallback<bool>(),
/* aAsync = */ false);
EmptyString());
responseCode = 1;
} else if(aResponse & HANGUI_USER_RESPONSE_CONTINUE) {
mModule->OnHangUIContinue();

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

@ -357,19 +357,13 @@ void
mozilla::plugins::TakeFullMinidump(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsAString& aBrowserDumpId,
std::function<void(nsString)>&& aCallback,
bool aAsync)
nsString& aDumpId)
{
PluginModuleChromeParent* chromeParent =
PluginModuleChromeParentForId(aPluginId);
if (chromeParent) {
chromeParent->TakeFullMinidump(aContentProcessId,
aBrowserDumpId,
std::move(aCallback),
aAsync);
} else {
aCallback(EmptyString());
chromeParent->TakeFullMinidump(aContentProcessId, aBrowserDumpId, aDumpId);
}
}
@ -377,8 +371,7 @@ void
mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsCString& aMonitorDescription,
const nsAString& aDumpId,
std::function<void(bool)>&& aCallback)
const nsAString& aDumpId)
{
PluginModuleChromeParent* chromeParent =
PluginModuleChromeParentForId(aPluginId);
@ -387,11 +380,7 @@ mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
chromeParent->TerminateChildProcess(MessageLoop::current(),
aContentProcessId,
aMonitorDescription,
aDumpId,
std::move(aCallback),
true); // Always runs asynchronously.
} else {
aCallback(true);
aDumpId);
}
}
@ -587,7 +576,7 @@ PluginModuleChromeParent::InitCrashReporter()
}
{
mozilla::RecursiveMutexAutoLock lock(mCrashReporterMutex);
mozilla::MutexAutoLock lock(mCrashReporterMutex);
mCrashReporter = MakeUnique<ipc::CrashReporterHost>(
GeckoProcessType_Plugin,
shmem,
@ -727,7 +716,7 @@ void
PluginModuleChromeParent::WriteExtraDataForMinidump()
{
// mCrashReporterMutex is already held by the caller
mCrashReporterMutex.AssertCurrentThreadIn();
mCrashReporterMutex.AssertCurrentThreadOwns();
typedef nsDependentCString cstring;
@ -1066,14 +1055,10 @@ PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
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(),
DummyCallback<bool>(),
/* aAsync = */ false);
EmptyString());
GetIPCChannel()->CloseWithTimeout();
return false;
}
@ -1098,141 +1083,55 @@ PluginModuleContentParent::OnExitedSyncSend()
void
PluginModuleChromeParent::TakeFullMinidump(base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
std::function<void(nsString)>&& aCallback,
bool aAsync)
nsString& aDumpId)
{
mozilla::RecursiveMutexAutoLock lock(mCrashReporterMutex);
mozilla::MutexAutoLock lock(mCrashReporterMutex);
if (!mCrashReporter || !mTakeFullMinidumpCallback.IsEmpty()) {
aCallback(EmptyString());
if (!mCrashReporter) {
return;
}
mTakeFullMinidumpCallback.Init(std::move(aCallback), aAsync);
nsString browserDumpId{aBrowserDumpId};
bool reportsReady = false;
// 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(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();
};
getter_AddRefs(browserDumpFile))) {
// We have a single browser report, generate a new plugin process parent
// report and pair it up with the browser report handed in.
mCrashReporter->GenerateMinidumpAndPair(Process(), mBrowserDumpFile,
NS_LITERAL_CSTRING("browser"),
std::move(callback), aAsync);
} else {
TakeBrowserAndPluginMinidumps(false, aContentPid, browserDumpId, aAsync);
}
}
reportsReady = mCrashReporter->GenerateMinidumpAndPair(
this,
browserDumpFile,
NS_LITERAL_CSTRING("browser"));
void
PluginModuleChromeParent::RetainPluginRef()
{
if (!mPlugin) {
return;
if (!reportsReady) {
browserDumpFile = nullptr;
CrashReporter::DeleteMinidumpFilesForID(aBrowserDumpId);
}
}
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("nsNPAPIPlugin::AddRef",
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("nsNPAPIPlugin::Release",
mPlugin, &nsNPAPIPlugin::Release));
}
}
void
PluginModuleChromeParent::TakeBrowserAndPluginMinidumps(bool aReportsReady,
base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
bool aAsync)
{
mCrashReporterMutex.AssertCurrentThreadIn();
// 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 (!aReportsReady) {
mBrowserDumpFile = nullptr;
CrashReporter::DeleteMinidumpFilesForID(aBrowserDumpId);
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"),
std::move(callback),
aAsync);
} else {
OnTakeFullMinidumpComplete(aReportsReady, aContentPid, aBrowserDumpId);
if (!reportsReady) {
reportsReady = mCrashReporter->GenerateMinidumpAndPair(
this,
nullptr, // Pair with a dump of this process and thread.
NS_LITERAL_CSTRING("browser"));
}
}
void
PluginModuleChromeParent::OnTakeFullMinidumpComplete(bool aReportsReady,
base::ProcessId aContentPid,
const nsAString& aBrowserDumpId)
{
mCrashReporterMutex.AssertCurrentThreadIn();
if (aReportsReady) {
nsString dumpId = mCrashReporter->MinidumpID();
if (reportsReady) {
aDumpId = mCrashReporter->MinidumpID();
PLUGIN_LOG_DEBUG(
("generated paired browser/plugin minidumps: %s)",
NS_ConvertUTF16toUTF8(dumpId).get()));
("generated paired browser/plugin minidumps: %s)",
NS_ConvertUTF16toUTF8(aDumpId).get()));
nsAutoCString additionalDumps("browser");
nsCOMPtr<nsIFile> pluginDumpFile;
if (GetMinidumpForID(dumpId, getter_AddRefs(pluginDumpFile))) {
if (GetMinidumpForID(aDumpId, getter_AddRefs(pluginDumpFile))) {
#ifdef MOZ_CRASHREPORTER_INJECTOR
// If we have handles to the flash sandbox processes on Windows,
// include those minidumps as well.
@ -1256,10 +1155,7 @@ PluginModuleChromeParent::OnTakeFullMinidumpComplete(bool aReportsReady,
}
mCrashReporter->AddAnnotation(Annotation::additional_minidumps,
additionalDumps);
mTakeFullMinidumpCallback.Invoke(mCrashReporter->MinidumpID());
} else {
mTakeFullMinidumpCallback.Invoke(EmptyString());
NS_WARNING("failed to capture paired minidumps from hang");
}
}
@ -1268,52 +1164,20 @@ void
PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid,
const nsCString& aMonitorDescription,
const nsAString& aDumpId,
std::function<void(bool)>&& aCallback,
bool aAsync)
const nsAString& aDumpId)
{
if (!mTerminateChildProcessCallback.IsEmpty()) {
aCallback(false);
return;
}
mTerminateChildProcessCallback.Init(std::move(aCallback), aAsync);
// 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()) {
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(), std::move(callback), aAsync);
} else {
TerminateChildProcessOnDumpComplete(aMsgLoop, aMonitorDescription);
TakeFullMinidump(aContentPid, EmptyString(), dumpId);
}
}
void
PluginModuleChromeParent::TerminateChildProcessOnDumpComplete(MessageLoop* aMsgLoop,
const nsCString& aMonitorDescription)
{
mCrashReporterMutex.AssertCurrentThreadIn();
mozilla::MutexAutoLock lock(mCrashReporterMutex);
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->AddAnnotation(Annotation::PluginHang, true);
@ -1370,8 +1234,6 @@ PluginModuleChromeParent::TerminateChildProcessOnDumpComplete(MessageLoop* aMsgL
if (!childOpened || !KillProcess(geckoChildProcess, 1, false)) {
NS_WARNING("failed to kill subprocess!");
}
mTerminateChildProcessCallback.Invoke(true);
}
bool
@ -1522,7 +1384,7 @@ RemoveMinidump(nsIFile* minidump)
void
PluginModuleChromeParent::ProcessFirstMinidump()
{
mozilla::RecursiveMutexAutoLock lock(mCrashReporterMutex);
mozilla::MutexAutoLock lock(mCrashReporterMutex);
if (!mCrashReporter)
return;

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

@ -11,13 +11,11 @@
#include "mozilla/FileUtils.h"
#include "mozilla/HangAnnotations.h"
#include "mozilla/PluginLibrary.h"
#include "mozilla/ipc/CrashReporterHost.h"
#include "mozilla/plugins/PluginProcessParent.h"
#include "mozilla/plugins/PPluginModuleParent.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "mozilla/plugins/PluginTypes.h"
#include "mozilla/ipc/TaskFactory.h"
#include "mozilla/RecursiveMutex.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
#include "npapi.h"
@ -34,6 +32,9 @@ class nsPluginTag;
namespace mozilla {
namespace ipc {
class CrashReporterHost;
} // namespace ipc
namespace layers {
class TextureClientRecycleAllocator;
} // namespace layers
@ -318,11 +319,9 @@ protected:
* This mutex protects the crash reporter when the Plugin Hang UI event
* handler is executing off main thread. It is intended to protect both
* the mCrashReporter variable in addition to the CrashReporterHost object
* that mCrashReporter refers to. Sometimes asynchronous crash reporter
* callbacks are dispatched synchronously while the caller is still holding
* the mutex. This requires recursive locking support in the mutex.
* that mCrashReporter refers to.
*/
mozilla::RecursiveMutex mCrashReporterMutex;
mozilla::Mutex mCrashReporterMutex;
UniquePtr<ipc::CrashReporterHost> mCrashReporter;
};
@ -359,10 +358,6 @@ class PluginModuleChromeParent
, public mozilla::BackgroundHangAnnotator
{
friend class mozilla::ipc::CrashReporterHost;
using TerminateChildProcessCallback =
mozilla::ipc::CrashReporterHost::CallbackWrapper<bool>;
using TakeFullMinidumpCallback =
mozilla::ipc::CrashReporterHost::CallbackWrapper<nsString>;
public:
/**
* LoadModule
@ -387,16 +382,12 @@ class PluginModuleChromeParent
* 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 aCallback a callback invoked when the operation completes. The ID
* of the newly generated crash dump is provided in the callback argument.
* An empty string will be provided upon failure.
* @param aAsync whether to perform the dump asynchronously.
* @param aDumpId Returns the ID of the newly generated crash dump. Left
* untouched upon failure.
*/
void
TakeFullMinidump(base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
std::function<void(nsString)>&& aCallback,
bool aAsync);
void TakeFullMinidump(base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
nsString& aDumpId);
/*
* Terminates the plugin process associated with this plugin module. Also
@ -414,46 +405,11 @@ class PluginModuleChromeParent
* TerminateChildProcess will use this dump file instead of generating a
* multi-process crash report. If not provided a multi-process dump will
* be taken at the time of this call.
* @param aCallback a callback invoked when the operation completes. The
* argument denotes whether the operation succeeded.
* @param aAsync whether to perform the operation asynchronously.
*/
void
TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid,
const nsCString& aMonitorDescription,
const nsAString& aDumpId,
std::function<void(bool)>&& aCallback,
bool aAsync);
/**
* Helper for passing a dummy callback in calling the above function if it
* is called synchronously and the caller doesn't care about the callback
* result.
*/
template<typename T>
static std::function<void(T)> DummyCallback()
{
return std::function<void(T)>([](T aResult) { });
}
private:
// The following methods are callbacks invoked after calling
// TakeFullMinidump(). The methods are invoked in the following order:
void TakeBrowserAndPluginMinidumps(bool aReportsReady,
base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
bool aAsync);
void OnTakeFullMinidumpComplete(bool aReportsReady,
base::ProcessId aContentPid,
const nsAString& aBrowserDumpId);
// The following method is the callback invoked after calling
// TerminateChidlProcess().
void TerminateChildProcessOnDumpComplete(MessageLoop* aMsgLoop,
const nsCString& aMonitorDescription);
public:
void TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid,
const nsCString& aMonitorDescription,
const nsAString& aDumpId);
#ifdef XP_WIN
/**
@ -486,8 +442,6 @@ private:
void ProcessFirstMinidump();
void WriteExtraDataForMinidump();
void RetainPluginRef();
void ReleasePluginRef();
PluginProcessParent* Process() const { return mSubprocess; }
base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
@ -603,12 +557,6 @@ private:
nsCOMPtr<nsIObserver> mPluginOfflineObserver;
bool mIsBlocklisted;
nsCOMPtr<nsIFile> mBrowserDumpFile;
TakeFullMinidumpCallback mTakeFullMinidumpCallback;
TerminateChildProcessCallback mTerminateChildProcessCallback;
bool mIsCleaningFromTimeout;
};

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

@ -7,12 +7,10 @@
#include "CrashReporterHost.h"
#include "CrashReporterMetadataShmem.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/recordreplay/ParentIPC.h"
#include "mozilla/Sprintf.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/Telemetry.h"
#include "nsExceptionHandler.h"
#include "nsIAsyncShutdown.h"
#include "nsICrashService.h"
@ -21,7 +19,7 @@ namespace ipc {
CrashReporterHost::CrashReporterHost(GeckoProcessType aProcessType,
const Shmem& aShmem,
ThreadId aThreadId)
CrashReporter::ThreadId aThreadId)
: mProcessType(aProcessType),
mShmem(aShmem),
mThreadId(aThreadId),
@ -121,120 +119,6 @@ CrashReporterHost::FinalizeCrashReport()
return true;
}
namespace {
class GenerateMinidumpShutdownBlocker : public nsIAsyncShutdownBlocker {
public:
GenerateMinidumpShutdownBlocker() = default;
NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aBarrierClient) override
{
return NS_OK;
}
NS_IMETHOD GetName(nsAString& aName) override
{
aName = NS_LITERAL_STRING("Crash Reporter: blocking on minidump"
"generation.");
return NS_OK;
}
NS_IMETHOD GetState(nsIPropertyBag**) override
{
return NS_OK;
}
NS_DECL_THREADSAFE_ISUPPORTS
private:
virtual ~GenerateMinidumpShutdownBlocker() = default;
};
NS_IMPL_ISUPPORTS(GenerateMinidumpShutdownBlocker, nsIAsyncShutdownBlocker)
}
static nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
nsCOMPtr<nsIAsyncShutdownClient> barrier;
nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return barrier.forget();
}
void
CrashReporterHost::GenerateMinidumpAndPair(GeckoChildProcessHost* aChildProcess,
nsIFile* aMinidumpToPair,
const nsACString& aPairName,
std::function<void(bool)>&& aCallback,
bool aAsync)
{
base::ProcessHandle childHandle;
#ifdef XP_MACOSX
childHandle = aChildProcess->GetChildTask();
#else
childHandle = aChildProcess->GetChildProcessHandle();
#endif
if (!mCreateMinidumpCallback.IsEmpty()) {
aCallback(false);
return;
}
mCreateMinidumpCallback.Init(std::move(aCallback), aAsync);
if (!childHandle) {
NS_WARNING("Failed to get child process handle.");
mCreateMinidumpCallback.Invoke(false);
return;
}
nsCOMPtr<nsIAsyncShutdownBlocker> shutdownBlocker;
if (aAsync && NS_IsMainThread()) {
nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
if (!barrier) {
mCreateMinidumpCallback.Invoke(false);
return;
}
shutdownBlocker = new GenerateMinidumpShutdownBlocker();
nsresult rv = barrier->AddBlocker(shutdownBlocker,
NS_LITERAL_STRING(__FILE__), __LINE__,
NS_LITERAL_STRING("Minidump generation"));
Unused << NS_WARN_IF(NS_FAILED(rv));
}
std::function<void(bool)> callback =
[this, shutdownBlocker](bool aResult) {
if (aResult &&
CrashReporter::GetIDFromMinidump(this->mTargetDump, this->mDumpID)) {
this->mCreateMinidumpCallback.Invoke(true);
} else {
this->mCreateMinidumpCallback.Invoke(false);
}
if (shutdownBlocker) {
nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
if (barrier) {
barrier->RemoveBlocker(shutdownBlocker);
}
}
};
CrashReporter::CreateMinidumpsAndPair(childHandle,
mThreadId,
aPairName,
aMinidumpToPair,
getter_AddRefs(mTargetDump),
std::move(callback),
aAsync);
}
/* static */ void
CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
int32_t aCrashType,

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

@ -18,8 +18,6 @@
namespace mozilla {
namespace ipc {
class GeckoChildProcessHost;
// This is the newer replacement for CrashReporterParent. It is created in
// response to a InitCrashReporter message on a top-level actor, and simply
// holds the metadata shmem alive until the process ends. When the process
@ -29,68 +27,12 @@ class CrashReporterHost
{
typedef mozilla::ipc::Shmem Shmem;
typedef CrashReporter::AnnotationTable AnnotationTable;
typedef CrashReporter::ThreadId ThreadId;
public:
template <typename T>
class CallbackWrapper {
public:
void Init(std::function<void(T)>&& aCallback, bool aAsync)
{
mCallback = std::move(aCallback);
mAsync = aAsync;
if (IsAsync()) {
// Don't call do_GetCurrentThread() if this is called synchronously
// because 1. it's unnecessary, and 2. more importantly, it might create
// one if called from a native thread, and the thread will be leaked.
mTargetThread = do_GetCurrentThread();
}
}
bool IsEmpty()
{
return !mCallback;
}
bool IsAsync()
{
return mAsync;
}
void Invoke(T aResult)
{
if (IsAsync()) {
decltype(mCallback) callback = std::move(mCallback);
mTargetThread->
Dispatch(NS_NewRunnableFunction("ipc::CrashReporterHost::CallbackWrapper::Invoke",
[callback, aResult](){
callback(aResult);
}), NS_DISPATCH_NORMAL);
} else {
MOZ_ASSERT(!mTargetThread);
mCallback(aResult);
}
Clear();
}
private:
void Clear()
{
mCallback = nullptr;
mTargetThread = nullptr;
mAsync = false;
}
bool mAsync;
std::function<void(T)> mCallback;
nsCOMPtr<nsIThread> mTargetThread;
};
CrashReporterHost(GeckoProcessType aProcessType,
const Shmem& aShmem,
ThreadId aThreadId);
CrashReporter::ThreadId aThreadId);
// Helper function for generating a crash report for a process that probably
// crashed (i.e., had an AbnormalShutdown in ActorDestroy). Returns true if
@ -112,15 +54,37 @@ public:
// Generate a paired minidump. This does not take the crash report, as
// GenerateCrashReport does. After this, FinalizeCrashReport may be called.
// Minidump(s) can be generated synchronously or asynchronously, specified in
// argument aAsync. When the operation completes, aCallback is invoked, where
// the callback argument denotes whether the operation succeeded.
void
GenerateMinidumpAndPair(GeckoChildProcessHost* aChildProcess,
nsIFile* aMinidumpToPair,
const nsACString& aPairName,
std::function<void(bool)>&& aCallback,
bool aAsync);
//
// This calls TakeCrashedChildMinidump and FinalizeCrashReport.
template <typename Toplevel>
bool GenerateMinidumpAndPair(Toplevel* aToplevelProtocol,
nsIFile* aMinidumpToPair,
const nsACString& aPairName)
{
ScopedProcessHandle childHandle;
#ifdef XP_MACOSX
childHandle = aToplevelProtocol->Process()->GetChildTask();
#else
if (!base::OpenPrivilegedProcessHandle(aToplevelProtocol->OtherPid(),
&childHandle.rwget()))
{
NS_WARNING("Failed to open child process handle.");
return false;
}
#endif
nsCOMPtr<nsIFile> targetDump;
if (!CrashReporter::CreateMinidumpsAndPair(childHandle,
mThreadId,
aPairName,
aMinidumpToPair,
getter_AddRefs(targetDump)))
{
return false;
}
return CrashReporter::GetIDFromMinidump(targetDump, mDumpID);
}
void AddAnnotation(CrashReporter::Annotation aKey, bool aValue);
void AddAnnotation(CrashReporter::Annotation aKey, int aValue);
@ -152,15 +116,13 @@ private:
const nsString& aChildDumpID);
private:
CallbackWrapper<bool> mCreateMinidumpCallback;
GeckoProcessType mProcessType;
Shmem mShmem;
ThreadId mThreadId;
CrashReporter::ThreadId mThreadId;
time_t mStartTime;
AnnotationTable mExtraAnnotations;
nsString mDumpID;
bool mFinalized;
nsCOMPtr<nsIFile> mTargetDump;
};
} // namespace ipc

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

@ -4,6 +4,8 @@
* 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/. */
#include <functional>
#include "nsExceptionHandler.h"
#include "nsExceptionHandlerUtils.h"
@ -393,15 +395,14 @@ TakeMinidump(nsIFile** aResult, bool aMoveToPending)
return false;
}
void
bool
CreateMinidumpsAndPair(ProcessHandle aTargetPid,
ThreadId aTargetBlamedThread,
const nsACString& aIncomingPairName,
nsIFile* aIncomingDumpToPair,
nsIFile** aMainDumpOut,
std::function<void(bool)>&& aCallback,
bool aAsync)
nsIFile** aTargetDumpOut)
{
return false;
}
bool

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

@ -306,8 +306,6 @@ static ChildMinidumpMap* pidToMinidump;
static uint32_t crashSequence;
static bool OOPInitialized();
static nsIThread* sMinidumpWriterThread;
#ifdef MOZ_CRASHREPORTER_INJECTOR
static nsIThread* sInjectorThread;
@ -3408,11 +3406,6 @@ OOPDeinit()
}
#endif
if (sMinidumpWriterThread) {
sMinidumpWriterThread->Shutdown();
NS_RELEASE(sMinidumpWriterThread);
}
delete crashServer;
crashServer = nullptr;
@ -3851,37 +3844,17 @@ bool TakeMinidump(nsIFile** aResult, bool aMoveToPending)
return true;
}
static inline void
NotifyDumpResult(bool aResult,
bool aAsync,
std::function<void(bool)>&& aCallback,
RefPtr<nsIThread>&& aCallbackThread)
bool
CreateMinidumpsAndPair(ProcessHandle aTargetPid,
ThreadId aTargetBlamedThread,
const nsACString& aIncomingPairName,
nsIFile* aIncomingDumpToPair,
nsIFile** aMainDumpOut)
{
std::function<void()> runnable = [&](){
aCallback(aResult);
};
if (aAsync) {
MOZ_ASSERT(!!aCallbackThread);
Unused << aCallbackThread->Dispatch(NS_NewRunnableFunction("CrashReporter::InvokeCallback",
std::move(runnable)),
NS_DISPATCH_SYNC);
} else {
runnable();
if (!GetEnabled()) {
return false;
}
}
static void
CreatePairedChildMinidumpAsync(ProcessHandle aTargetPid,
ThreadId aTargetBlamedThread,
nsCString aIncomingPairName,
nsCOMPtr<nsIFile> aIncomingDumpToPair,
nsIFile** aMainDumpOut,
xpstring aDumpPath,
std::function<void(bool)>&& aCallback,
RefPtr<nsIThread>&& aCallbackThread,
bool aAsync)
{
AutoIOInterposerDisable disableIOInterposition;
#ifdef XP_MACOSX
@ -3890,64 +3863,6 @@ CreatePairedChildMinidumpAsync(ProcessHandle aTargetPid,
ThreadId targetThread = aTargetBlamedThread;
#endif
// dump the target
nsCOMPtr<nsIFile> targetMinidump;
if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
aTargetPid,
targetThread,
aDumpPath,
PairedDumpCallbackExtra,
static_cast<void*>(&targetMinidump)
#ifdef XP_WIN32
, GetMinidumpType()
#endif
)) {
NotifyDumpResult(false, aAsync, std::move(aCallback), std::move(aCallbackThread));
return;
}
nsCOMPtr<nsIFile> targetExtra;
GetExtraFileForMinidump(targetMinidump, getter_AddRefs(targetExtra));
if (!targetExtra) {
targetMinidump->Remove(false);
NotifyDumpResult(false, aAsync, std::move(aCallback), std::move(aCallbackThread));
return;
}
RenameAdditionalHangMinidump(aIncomingDumpToPair,
targetMinidump,
aIncomingPairName);
if (ShouldReport()) {
MoveToPending(targetMinidump, targetExtra, nullptr);
MoveToPending(aIncomingDumpToPair, nullptr, nullptr);
}
targetMinidump.forget(aMainDumpOut);
NotifyDumpResult(true, aAsync, std::move(aCallback), std::move(aCallbackThread));
}
void
CreateMinidumpsAndPair(ProcessHandle aTargetPid,
ThreadId aTargetBlamedThread,
const nsACString& aIncomingPairName,
nsIFile* aIncomingDumpToPair,
nsIFile** aMainDumpOut,
std::function<void(bool)>&& aCallback,
bool aAsync)
{
if (!GetEnabled()) {
aCallback(false);
return;
}
#if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
DllBlocklist_Shutdown();
#endif
AutoIOInterposerDisable disableIOInterposition;
xpstring dump_path;
#ifndef XP_LINUX
dump_path = gExceptionHandler->dump_path();
@ -3955,10 +3870,26 @@ CreateMinidumpsAndPair(ProcessHandle aTargetPid,
dump_path = gExceptionHandler->minidump_descriptor().directory();
#endif
// dump the target
nsCOMPtr<nsIFile> targetMinidump;
if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
aTargetPid,
targetThread,
dump_path,
PairedDumpCallbackExtra,
static_cast<void*>(&targetMinidump)
#ifdef XP_WIN32
, GetMinidumpType()
#endif
)) {
return false;
}
nsCOMPtr<nsIFile> targetExtra;
GetExtraFileForMinidump(targetMinidump, getter_AddRefs(targetExtra));
// If aIncomingDumpToPair isn't valid, create a dump of this process.
// This part needs to be synchronous, unfortunately, so that the parent dump
// contains the stack symmetrical with the child dump.
nsCOMPtr<nsIFile> incomingDumpToPair;
nsCOMPtr<nsIFile> incomingDump;
if (aIncomingDumpToPair == nullptr) {
if (!google_breakpad::ExceptionHandler::WriteMinidump(
dump_path,
@ -3966,52 +3897,32 @@ CreateMinidumpsAndPair(ProcessHandle aTargetPid,
true,
#endif
PairedDumpCallback,
static_cast<void*>(&incomingDumpToPair)
static_cast<void*>(&incomingDump)
#ifdef XP_WIN32
, GetMinidumpType()
#endif
)) {
aCallback(false);
return;
} // else incomingDump is assigned in PairedDumpCallback().
targetMinidump->Remove(false);
targetExtra->Remove(false);
return false;
}
} else {
incomingDumpToPair = aIncomingDumpToPair;
}
MOZ_ASSERT(!!incomingDumpToPair);
if (aAsync &&
!sMinidumpWriterThread &&
NS_FAILED(NS_NewNamedThread("Minidump Writer", &sMinidumpWriterThread))) {
aCallback(false);
return;
incomingDump = aIncomingDumpToPair;
}
nsCString incomingPairName(aIncomingPairName);
std::function<void(bool)> callback = std::move(aCallback);
// Don't call do_GetCurrentThread() if this is called synchronously because
// 1. it's unnecessary, and 2. more importantly, it might create one if called
// from a native thread, and the thread will be leaked.
RefPtr<nsIThread> callbackThread = aAsync ? do_GetCurrentThread() : nullptr;
RenameAdditionalHangMinidump(incomingDump, targetMinidump, aIncomingPairName);
std::function<void()> doDump = [=]() mutable {
CreatePairedChildMinidumpAsync(aTargetPid,
aTargetBlamedThread,
incomingPairName,
incomingDumpToPair,
aMainDumpOut,
dump_path,
std::move(callback),
std::move(callbackThread),
aAsync);
};
if (aAsync) {
sMinidumpWriterThread->Dispatch(NS_NewRunnableFunction("CrashReporter::CreateMinidumpsAndPair",
std::move(doDump)),
nsIEventTarget::DISPATCH_NORMAL);
} else {
doDump();
if (ShouldReport()) {
MoveToPending(targetMinidump, targetExtra, nullptr);
MoveToPending(incomingDump, nullptr, nullptr);
}
#if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST)
DllBlocklist_Shutdown();
#endif
targetMinidump.forget(aMainDumpOut);
return true;
}
bool

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

@ -17,7 +17,6 @@
#include "CrashAnnotations.h"
#include <functional>
#include <stddef.h>
#include <stdint.h>
#include "nsError.h"
@ -238,14 +237,11 @@ ThreadId CurrentThreadId();
* aIncomingDumpToPair.
* @return bool indicating success or failure
*/
void
CreateMinidumpsAndPair(ProcessHandle aTargetPid,
ThreadId aTargetBlamedThread,
const nsACString& aIncomingPairName,
nsIFile* aIncomingDumpToPair,
nsIFile** aTargetDumpOut,
std::function<void(bool)>&& aCallback,
bool aAsync);
bool CreateMinidumpsAndPair(ProcessHandle aTargetPid,
ThreadId aTargetBlamedThread,
const nsACString& aIncomingPairName,
nsIFile* aIncomingDumpToPair,
nsIFile** aTargetDumpOut);
// Create an additional minidump for a child of a process which already has
// a minidump (|parentMinidump|).