Bug 1504372 - Always generate minidumps for replaying child crashes, r=mccr8.

--HG--
extra : rebase_source : f68f98127fc914339db47f57bf365a950ca2f2b4
This commit is contained in:
Brian Hackett 2018-11-02 14:30:39 -10:00
parent cb45670d92
commit 645f80c048
5 changed files with 42 additions and 7 deletions

View File

@ -151,6 +151,7 @@ void
Channel::SendMessage(const Message& aMsg) Channel::SendMessage(const Message& aMsg)
{ {
MOZ_RELEASE_ASSERT(NS_IsMainThread() || MOZ_RELEASE_ASSERT(NS_IsMainThread() ||
aMsg.mType == MessageType::BeginFatalError ||
aMsg.mType == MessageType::FatalError || aMsg.mType == MessageType::FatalError ||
aMsg.mType == MessageType::MiddlemanCallRequest); aMsg.mType == MessageType::MiddlemanCallRequest);

View File

@ -109,8 +109,13 @@ namespace recordreplay {
\ \
/* A critical error occurred and execution cannot continue. The child will */ \ /* A critical error occurred and execution cannot continue. The child will */ \
/* stop executing after sending this message and will wait to be terminated. */ \ /* stop executing after sending this message and will wait to be terminated. */ \
/* A minidump for the child has been generated. */ \
_Macro(FatalError) \ _Macro(FatalError) \
\ \
/* Sent when a fatal error has occurred, but before the minidump has been */ \
/* generated. */ \
_Macro(BeginFatalError) \
\
/* The child's graphics were repainted. */ \ /* The child's graphics were repainted. */ \
_Macro(Paint) \ _Macro(Paint) \
\ \
@ -371,6 +376,8 @@ struct FatalErrorMessage : public Message
const char* Error() const { return Data<FatalErrorMessage, const char>(); } const char* Error() const { return Data<FatalErrorMessage, const char>(); }
}; };
typedef EmptyMessage<MessageType::BeginFatalError> BeginFatalErrorMessage;
// The format for graphics data which will be sent to the middleman process. // The format for graphics data which will be sent to the middleman process.
// This needs to match the format expected for canvas image data, to avoid // This needs to match the format expected for canvas image data, to avoid
// transforming the data before rendering it in the middleman process. // transforming the data before rendering it in the middleman process.

View File

@ -108,8 +108,9 @@ ChannelMessageHandler(Message* aMsg)
PrintSpew("Terminate message received, exiting...\n"); PrintSpew("Terminate message received, exiting...\n");
_exit(0); _exit(0);
} else { } else {
MOZ_CRASH("Hanged replaying process"); ReportFatalError(Nothing(), "Hung replaying process");
} }
break;
} }
case MessageType::SetIsActive: { case MessageType::SetIsActive: {
const SetIsActiveMessage& nmsg = (const SetIsActiveMessage&) *aMsg; const SetIsActiveMessage& nmsg = (const SetIsActiveMessage&) *aMsg;
@ -350,6 +351,10 @@ CreateCheckpoint()
void void
ReportFatalError(const Maybe<MinidumpInfo>& aMinidump, const char* aFormat, ...) ReportFatalError(const Maybe<MinidumpInfo>& aMinidump, const char* aFormat, ...)
{ {
// Notify the middleman that we are crashing and are going to try to write a
// minidump.
gChannel->SendMessage(BeginFatalErrorMessage());
// Unprotect any memory which might be written while producing the minidump. // Unprotect any memory which might be written while producing the minidump.
UnrecoverableSnapshotFailure(); UnrecoverableSnapshotFailure();

View File

@ -40,6 +40,8 @@ ChildProcessInfo::ChildProcessInfo(UniquePtr<ChildRole> aRole,
, mNumRecoveredMessages(0) , mNumRecoveredMessages(0)
, mRole(std::move(aRole)) , mRole(std::move(aRole))
, mPauseNeeded(false) , mPauseNeeded(false)
, mHasBegunFatalError(false)
, mHasFatalError(false)
{ {
MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(NS_IsMainThread());
@ -199,7 +201,11 @@ ChildProcessInfo::OnIncomingMessage(size_t aChannelId, const Message& aMsg)
} }
// Always handle fatal errors in the same way. // Always handle fatal errors in the same way.
if (aMsg.mType == MessageType::FatalError) { if (aMsg.mType == MessageType::BeginFatalError) {
mHasBegunFatalError = true;
return;
} else if (aMsg.mType == MessageType::FatalError) {
mHasFatalError = true;
const FatalErrorMessage& nmsg = static_cast<const FatalErrorMessage&>(aMsg); const FatalErrorMessage& nmsg = static_cast<const FatalErrorMessage&>(aMsg);
OnCrash(nmsg.Error()); OnCrash(nmsg.Error());
return; return;
@ -527,12 +533,23 @@ ChildProcessInfo::OnCrash(const char* aWhy)
{ {
MOZ_RELEASE_ASSERT(NS_IsMainThread()); MOZ_RELEASE_ASSERT(NS_IsMainThread());
// If a child process crashes or hangs then annotate the crash report and // If a child process crashes or hangs then annotate the crash report.
// shut down cleanly so that we don't mask the report with our own crash.
// We want the crash to happen quickly so the user doesn't get a hanged tab.
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::RecordReplayError, CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::RecordReplayError,
nsAutoCString(aWhy)); nsAutoCString(aWhy));
Shutdown();
// If we received a FatalError message then the child generated a minidump.
// Shut down cleanly so that we don't mask the report with our own crash.
if (mHasFatalError) {
Shutdown();
}
// Indicate when we crash if the child tried to send us a fatal error message
// but had a problem either unprotecting system memory or generating the
// minidump.
MOZ_RELEASE_ASSERT(!mHasBegunFatalError);
// The child crashed without producing a minidump, produce one ourselves.
MOZ_CRASH("Unexpected child crash");
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -613,7 +630,7 @@ ChildProcessInfo::WaitUntil(const std::function<bool()>& aCallback)
sentTerminateMessage = true; sentTerminateMessage = true;
} else { } else {
// The child is still non-responsive after sending the terminate // The child is still non-responsive after sending the terminate
// message, fail without producing a minidump. // message.
OnCrash("Child process non-responsive"); OnCrash("Child process non-responsive");
} }
} }

View File

@ -274,6 +274,11 @@ class ChildProcessInfo
// Whether we need this child to pause while the recording is updated. // Whether we need this child to pause while the recording is updated.
bool mPauseNeeded; bool mPauseNeeded;
// Flags for whether we have received messages from the child indicating it
// is crashing.
bool mHasBegunFatalError;
bool mHasFatalError;
void OnIncomingMessage(size_t aChannelId, const Message& aMsg); void OnIncomingMessage(size_t aChannelId, const Message& aMsg);
void OnIncomingRecoveryMessage(const Message& aMsg); void OnIncomingRecoveryMessage(const Message& aMsg);
void SendNextRecoveryMessage(); void SendNextRecoveryMessage();