gecko-dev/toolkit/recordreplay/DirtyMemoryHandler.cpp
Brian Hackett 71986ca9f7 Bug 1481009 Part 5 - Generate a minidump when reporting a fatal record/replay error, r=froydnj.
--HG--
extra : rebase_source : e9f64d2fe625dbb6eb01c9868e8aab6ebc11b8f4
2018-08-13 20:47:35 +00:00

130 lines
4.5 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "DirtyMemoryHandler.h"
#include "ipc/ChildInternal.h"
#include "mozilla/Sprintf.h"
#include "MemorySnapshot.h"
#include "Thread.h"
#include <mach/exc.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <sys/time.h>
namespace mozilla {
namespace recordreplay {
static mach_port_t gDirtyMemoryExceptionPort;
// See AsmJSSignalHandlers.cpp.
static const mach_msg_id_t sExceptionId = 2405;
// This definition was generated by mig (the Mach Interface Generator) for the
// routine 'exception_raise' (exc.defs). See js/src/wasm/WasmSignalHandlers.cpp.
#pragma pack(4)
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
} Request__mach_exception_raise_t;
#pragma pack()
typedef struct {
Request__mach_exception_raise_t body;
mach_msg_trailer_t trailer;
} ExceptionRequest;
static void
DirtyMemoryExceptionHandlerThread(void*)
{
kern_return_t kret;
while (true) {
ExceptionRequest request;
kret = mach_msg(&request.body.Head, MACH_RCV_MSG, 0, sizeof(request),
gDirtyMemoryExceptionPort, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
kern_return_t replyCode = KERN_FAILURE;
if (kret == KERN_SUCCESS &&
request.body.Head.msgh_id == sExceptionId &&
request.body.exception == EXC_BAD_ACCESS &&
request.body.codeCnt == 2)
{
uint8_t* faultingAddress = (uint8_t*) request.body.code[1];
if (HandleDirtyMemoryFault(faultingAddress)) {
replyCode = KERN_SUCCESS;
} else {
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(Nothing(),
"DirtyMemoryExceptionHandlerThread mach_msg returned unexpected data");
}
__Reply__exception_raise_t reply;
reply.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request.body.Head.msgh_bits), 0);
reply.Head.msgh_size = sizeof(reply);
reply.Head.msgh_remote_port = request.body.Head.msgh_remote_port;
reply.Head.msgh_local_port = MACH_PORT_NULL;
reply.Head.msgh_id = request.body.Head.msgh_id + 100;
reply.NDR = NDR_record;
reply.RetCode = replyCode;
mach_msg(&reply.Head, MACH_SEND_MSG, sizeof(reply), 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
}
void
SetupDirtyMemoryHandler()
{
// Allow repeated calls.
static bool hasDirtyMemoryHandler = false;
if (hasDirtyMemoryHandler) {
return;
}
hasDirtyMemoryHandler = true;
MOZ_RELEASE_ASSERT(AreThreadEventsPassedThrough());
kern_return_t kret;
// Get a port which can send and receive data.
kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &gDirtyMemoryExceptionPort);
MOZ_RELEASE_ASSERT(kret == KERN_SUCCESS);
kret = mach_port_insert_right(mach_task_self(),
gDirtyMemoryExceptionPort, gDirtyMemoryExceptionPort,
MACH_MSG_TYPE_MAKE_SEND);
MOZ_RELEASE_ASSERT(kret == KERN_SUCCESS);
// Create a thread to block on reading the port.
Thread::SpawnNonRecordedThread(DirtyMemoryExceptionHandlerThread, nullptr);
// Set exception ports on the entire task. Unfortunately, this clobbers any
// other exception ports for the task, and forwarding to those other ports
// is not easy to get right.
kret = task_set_exception_ports(mach_task_self(),
EXC_MASK_BAD_ACCESS,
gDirtyMemoryExceptionPort,
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
THREAD_STATE_NONE);
MOZ_RELEASE_ASSERT(kret == KERN_SUCCESS);
}
} // namespace recordreplay
} // namespace mozilla