зеркало из https://github.com/mozilla/gecko-dev.git
130 строки
4.5 KiB
C++
130 строки
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
|