Bug 1481009 Part 5 - Generate a minidump when reporting a fatal record/replay error, r=froydnj.

--HG--
extra : rebase_source : e9f64d2fe625dbb6eb01c9868e8aab6ebc11b8f4
This commit is contained in:
Brian Hackett 2018-08-13 20:47:35 +00:00
parent a4c6ff4a58
commit 71986ca9f7
12 changed files with 66 additions and 36 deletions

View File

@ -127,9 +127,7 @@ PassThroughThreadEventsAllowCallbacks(const std::function<void()>& aFn)
while (true) {
ThreadEvent ev = (ThreadEvent) thread->Events().ReadScalar();
if (ev != ThreadEvent::ExecuteCallback) {
if (ev != ThreadEvent::CallbacksFinished) {
child::ReportFatalError("Unexpected event while replaying callback events");
}
MOZ_RELEASE_ASSERT(ev == ThreadEvent::CallbacksFinished);
break;
}
size_t id = thread->Events().ReadScalar();

View File

@ -6,7 +6,7 @@
#include "DirtyMemoryHandler.h"
#include "ipc/ChildIPC.h"
#include "ipc/ChildInternal.h"
#include "mozilla/Sprintf.h"
#include "MemorySnapshot.h"
#include "Thread.h"
@ -65,11 +65,15 @@ DirtyMemoryExceptionHandlerThread(void*)
if (HandleDirtyMemoryFault(faultingAddress)) {
replyCode = KERN_SUCCESS;
} else {
child::ReportFatalError("HandleDirtyMemoryFault failed %p %s", faultingAddress,
gMozCrashReason ? gMozCrashReason : "");
child::MinidumpInfo info(request.body.exception,
request.body.code[0], request.body.code[1],
request.body.thread.name);
child::ReportFatalError(Some(info), "HandleDirtyMemoryFault failed %p %s",
faultingAddress, gMozCrashReason ? gMozCrashReason : "");
}
} else {
child::ReportFatalError("DirtyMemoryExceptionHandlerThread mach_msg returned unexpected data");
child::ReportFatalError(Nothing(),
"DirtyMemoryExceptionHandlerThread mach_msg returned unexpected data");
}
__Reply__exception_raise_t reply;

View File

@ -6,7 +6,7 @@
#include "File.h"
#include "ipc/ChildIPC.h"
#include "ipc/ChildInternal.h"
#include "mozilla/Compression.h"
#include "mozilla/Sprintf.h"
#include "ProcessRewind.h"
@ -161,7 +161,8 @@ Stream::CheckInput(size_t aValue)
size_t oldValue = aValue;
RecordOrReplayScalar(&oldValue);
if (oldValue != aValue) {
child::ReportFatalError("Input Mismatch: Recorded: %zu Replayed %zu\n", oldValue, aValue);
child::ReportFatalError(Nothing(), "Input Mismatch: Recorded: %zu Replayed %zu\n",
oldValue, aValue);
Unreachable();
}
}

View File

@ -410,7 +410,7 @@ CountdownThreadMain(void*)
if (gMemoryInfo->mCountdown && --gMemoryInfo->mCountdown == 0) {
// When debugging hangs in the child process, we can break here in lldb
// to inspect what the process is doing.
child::ReportFatalError("CountdownThread activated");
child::ReportFatalError(Nothing(), "CountdownThread activated");
}
ThreadYield();
}
@ -665,11 +665,13 @@ HandleDirtyMemoryFault(uint8_t* aAddress)
void
UnrecoverableSnapshotFailure()
{
AutoSpinLock lock(gMemoryInfo->mTrackedRegionsLock);
DirectUnprotectMemory(PageBase(&errno), PageSize, false);
for (auto region : gMemoryInfo->mTrackedRegionsByAllocationOrder) {
DirectUnprotectMemory(region.mBase, region.mSize, region.mExecutable,
/* aIgnoreFailures = */ true);
if (gMemoryInfo) {
AutoSpinLock lock(gMemoryInfo->mTrackedRegionsLock);
DirectUnprotectMemory(PageBase(&errno), PageSize, false);
for (auto region : gMemoryInfo->mTrackedRegionsByAllocationOrder) {
DirectUnprotectMemory(region.mBase, region.mSize, region.mExecutable,
/* aIgnoreFailures = */ true);
}
}
}
@ -1064,14 +1066,14 @@ CheckFixedMemory(void* aAddress, size_t aSize)
gMemoryInfo->mTrackedRegions.lookupClosestLessOrEqual(page);
if (!region.isSome() ||
!MemoryContains(region.ref().mBase, region.ref().mSize, page, PageSize)) {
child::ReportFatalError("Fixed memory is not tracked!");
MOZ_CRASH("Fixed memory is not tracked!");
}
}
}
// The memory should not be free.
if (gFreeRegions.Intersects(aAddress, aSize)) {
child::ReportFatalError("Fixed memory is currently free!");
MOZ_CRASH("Fixed memory is currently free!");
}
}

View File

@ -205,9 +205,9 @@ MOZ_EXPORT void
RecordReplayInterface_InternalInvalidateRecording(const char* aWhy)
{
if (IsRecording()) {
child::ReportFatalError("Recording invalidated: %s", aWhy);
child::ReportFatalError(Nothing(), "Recording invalidated: %s", aWhy);
} else {
child::ReportFatalError("Recording invalidated while replaying: %s", aWhy);
child::ReportFatalError(Nothing(), "Recording invalidated while replaying: %s", aWhy);
}
Unreachable();
}
@ -471,7 +471,8 @@ RecordReplayInterface_InternalRecordReplayAssert(const char* aFormat, va_list aA
SetCurrentStackString(text, text + strlen(text), sizeof(text) - strlen(text));
}
child::ReportFatalError("Assertion Mismatch: Thread %d\n"
child::ReportFatalError(Nothing(),
"Assertion Mismatch: Thread %d\n"
"Recorded: %s [%d,%d]\n"
"Replayed: %s [%d,%d]\n",
(int) thread->Id(), buffer, (int) streamPos, (int) progress, text,
@ -547,7 +548,8 @@ RecordReplayInterface_InternalRecordReplayAssertBytes(const void* aData, size_t
}
}
child::ReportFatalError("Byte Comparison Check Failed: Position %d %d Length %d %d\n",
child::ReportFatalError(Nothing(),
"Byte Comparison Check Failed: Position %d %d Length %d %d\n",
(int) streamPos, (int) thread->Events().StreamPosition(),
(int) oldSize, (int) aSize);
Unreachable();

View File

@ -256,7 +256,7 @@ Thread::StartThread(Callback aStart, void* aArgument, bool aNeedsJoin)
}
}
if (id >= MaxRecordedThreadId) {
child::ReportFatalError("Too many threads");
child::ReportFatalError(Nothing(), "Too many threads");
}
MOZ_RELEASE_ASSERT(id <= MaxRecordedThreadId);
}

View File

@ -188,7 +188,7 @@ RecordReplayInterface_ExecuteTriggers()
ThreadEvent ev = (ThreadEvent) thread->Events().ReadScalar();
if (ev != ThreadEvent::ExecuteTrigger) {
if (ev != ThreadEvent::ExecuteTriggersFinished) {
child::ReportFatalError("ExecuteTrigger Mismatch");
child::ReportFatalError(Nothing(), "ExecuteTrigger Mismatch");
Unreachable();
}
break;

View File

@ -14,6 +14,7 @@
#include "chrome/common/child_thread.h"
#include "chrome/common/mach_ipc_mac.h"
#include "ipc/Channel.h"
#include "mac/handler/exception_handler.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/Sprintf.h"
@ -282,7 +283,7 @@ InitRecordingOrReplayingProcess(int* aArgc, char*** aArgv)
// If we failed to initialize then report it to the user.
if (gInitializationFailureMessage) {
ReportFatalError("%s", gInitializationFailureMessage);
ReportFatalError(Nothing(), "%s", gInitializationFailureMessage);
Unreachable();
}
}
@ -300,8 +301,21 @@ ParentProcessId()
}
void
ReportFatalError(const char* aFormat, ...)
ReportFatalError(const Maybe<MinidumpInfo>& aMinidump, const char* aFormat, ...)
{
// Unprotect any memory which might be written while producing the minidump.
UnrecoverableSnapshotFailure();
AutoEnsurePassThroughThreadEvents pt;
#ifdef MOZ_CRASHREPORTER
MinidumpInfo info = aMinidump.isSome()
? aMinidump.ref()
: MinidumpInfo(EXC_CRASH, 1, 0, mach_thread_self());
google_breakpad::ExceptionHandler::WriteForwardedExceptionMinidump
(info.mExceptionType, info.mCode, info.mSubcode, info.mThread);
#endif
va_list ap;
va_start(ap, aFormat);
char buf[2048];
@ -323,8 +337,6 @@ ReportFatalError(const char* aFormat, ...)
DirectPrint(buf);
DirectPrint("\n");
UnrecoverableSnapshotFailure();
// Block until we get a terminate message and die.
Thread::WaitForeverNoIdle();
}

View File

@ -60,9 +60,6 @@ void NotifyFlushedRecording();
// Notify the middleman about an AlwaysMarkMajorCheckpoints directive.
void NotifyAlwaysMarkMajorCheckpoints();
// Report a fatal error to the middleman process.
void ReportFatalError(const char* aFormat, ...);
// Mark a time span when the main thread is idle.
void BeginIdleTime();
void EndIdleTime();

View File

@ -78,6 +78,24 @@ void HitCheckpoint(size_t aId, bool aRecordingEndpoint);
void HitBreakpoint(bool aRecordingEndpoint, const uint32_t* aBreakpoints, size_t aNumBreakpoints);
// Optional information about a crash that occurred. If not provided to
// ReportFatalError, the current thread will be treated as crashed.
struct MinidumpInfo
{
int mExceptionType;
int mCode;
int mSubcode;
mach_port_t mThread;
MinidumpInfo(int aExceptionType, int aCode, int aSubcode, mach_port_t aThread)
: mExceptionType(aExceptionType), mCode(aCode), mSubcode(aSubcode), mThread(aThread)
{}
};
// Generate a minidump and report a fatal error to the middleman process.
void ReportFatalError(const Maybe<MinidumpInfo>& aMinidumpInfo,
const char* aFormat, ...);
// Monitor used for various synchronization tasks.
extern Monitor* gMonitor;

View File

@ -92,12 +92,6 @@ NotifyAlwaysMarkMajorCheckpoints()
MOZ_CRASH();
}
void
ReportFatalError(const char* aFormat, ...)
{
MOZ_CRASH();
}
void
BeginIdleTime()
{

View File

@ -49,6 +49,8 @@ LOCAL_INCLUDES += [
'!/ipc/ipdl/_ipdlheaders',
'/ipc/chromium/src',
'/js/xpconnect/src',
'/toolkit/crashreporter/breakpad-client',
'/toolkit/crashreporter/google-breakpad/src',
]
FINAL_LIBRARY = 'xul'