win: Implement CRASHPAD_SIMULATE_CRASH()

Windows requires the connection to the handler to do anything, so it
can't really be implemented or tested without CrashpadClient and the
connection machinery.

R=mark@chromium.org
BUG=crashpad:53

Review URL: https://codereview.chromium.org/1356383002 .
This commit is contained in:
Scott Graham 2015-09-25 13:45:32 -07:00
parent 9bc0a99681
commit 475ac81cce
11 changed files with 451 additions and 106 deletions

View File

@ -47,6 +47,7 @@
'simulate_crash.h',
'simulate_crash_mac.cc',
'simulate_crash_mac.h',
'simulate_crash_win.h',
],
'conditions': [
['OS=="win"', {

View File

@ -25,6 +25,8 @@
#if defined(OS_MACOSX)
#include "base/mac/scoped_mach_port.h"
#elif defined(OS_WIN)
#include <windows.h>
#endif
namespace crashpad {
@ -87,6 +89,13 @@ class CrashpadClient {
//!
//! \return `true` on success and `false` on failure.
bool SetHandler(const std::string& ipc_port);
//! \brief Requests that the handler capture a dump even though there hasn't
//! been a crash.
//!
//! \param[in] context A CONTEXT, generally captured by `RtlCaptureContext()`
//! or similar.
static void DumpWithoutCrash(const CONTEXT& context);
#endif
//! \brief Configures the process to direct its crashes to a Crashpad handler.

View File

@ -21,17 +21,32 @@
#include "base/logging.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "util/file/file_io.h"
#include "util/win/registration_protocol_win.h"
#include "util/win/scoped_handle.h"
namespace {
// This handle is never closed.
// This handle is never closed. This is used to signal to the server that a dump
// should be taken in the event of a crash.
HANDLE g_signal_exception = INVALID_HANDLE_VALUE;
// Where we store the exception information that the crash handler reads.
crashpad::ExceptionInformation g_exception_information;
crashpad::ExceptionInformation g_crash_exception_information;
// These handles are never closed. g_signal_non_crash_dump is used to signal to
// the server to take a dump (not due to an exception), and the server will
// signal g_non_crash_dump_done when the dump is completed.
HANDLE g_signal_non_crash_dump = INVALID_HANDLE_VALUE;
HANDLE g_non_crash_dump_done = INVALID_HANDLE_VALUE;
// Guards multiple simultaneous calls to DumpWithoutCrash(). This is leaked.
base::Lock* g_non_crash_dump_lock;
// Where we store a pointer to the context information when taking a non-crash
// dump.
crashpad::ExceptionInformation g_non_crash_exception_information;
LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
// Tracks whether a thread has already entered UnhandledExceptionHandler.
@ -55,8 +70,8 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
// Otherwise, we're the first thread, so record the exception pointer and
// signal the crash handler.
g_exception_information.thread_id = GetCurrentThreadId();
g_exception_information.exception_pointers =
g_crash_exception_information.thread_id = GetCurrentThreadId();
g_crash_exception_information.exception_pointers =
reinterpret_cast<crashpad::WinVMAddress>(exception_pointers);
// Now signal the crash server, which will take a dump and then terminate us
@ -69,8 +84,6 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
// Sleep for a while to allow it to process us. Eventually, we terminate
// ourselves in case the crash server is gone, so that we don't leave zombies
// around. This would ideally never happen.
// TODO(scottmg): Re-add the "reply" event here, for implementing
// DumpWithoutCrashing.
Sleep(kMillisecondsUntilTerminate);
LOG(ERROR) << "crash server did not respond, self-terminating";
@ -102,13 +115,19 @@ bool CrashpadClient::StartHandler(
}
bool CrashpadClient::SetHandler(const std::string& ipc_port) {
DCHECK_EQ(g_signal_exception, INVALID_HANDLE_VALUE);
DCHECK_EQ(g_signal_non_crash_dump, INVALID_HANDLE_VALUE);
DCHECK_EQ(g_non_crash_dump_done, INVALID_HANDLE_VALUE);
ClientToServerMessage message;
memset(&message, 0, sizeof(message));
message.type = ClientToServerMessage::kRegister;
message.registration.version = RegistrationRequest::kMessageVersion;
message.registration.client_process_id = GetCurrentProcessId();
message.registration.exception_information =
reinterpret_cast<WinVMAddress>(&g_exception_information);
message.registration.crash_exception_information =
reinterpret_cast<WinVMAddress>(&g_crash_exception_information);
message.registration.non_crash_exception_information =
reinterpret_cast<WinVMAddress>(&g_non_crash_exception_information);
ServerToClientMessage response = {0};
@ -119,17 +138,80 @@ bool CrashpadClient::SetHandler(const std::string& ipc_port) {
// The server returns these already duplicated to be valid in this process.
g_signal_exception = reinterpret_cast<HANDLE>(
static_cast<uintptr_t>(response.registration.request_report_event));
static_cast<uintptr_t>(response.registration.request_crash_dump_event));
g_signal_non_crash_dump = reinterpret_cast<HANDLE>(static_cast<uintptr_t>(
response.registration.request_non_crash_dump_event));
g_non_crash_dump_done = reinterpret_cast<HANDLE>(static_cast<uintptr_t>(
response.registration.non_crash_dump_completed_event));
g_non_crash_dump_lock = new base::Lock();
return true;
}
bool CrashpadClient::UseHandler() {
if (g_signal_exception == INVALID_HANDLE_VALUE)
if (g_signal_exception == INVALID_HANDLE_VALUE ||
g_signal_non_crash_dump == INVALID_HANDLE_VALUE ||
g_non_crash_dump_done == INVALID_HANDLE_VALUE) {
return false;
}
// In theory we could store the previous handler but it is not clear what
// use we have for it.
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
return true;
}
// static
void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) {
if (g_signal_non_crash_dump == INVALID_HANDLE_VALUE ||
g_non_crash_dump_done == INVALID_HANDLE_VALUE) {
LOG(ERROR) << "haven't called SetHandler()";
return;
}
// In the non-crashing case, we aren't concerned about avoiding calls into
// Win32 APIs, so just use regular locking here in case of multiple threads
// calling this function. If a crash occurs while we're in here, the worst
// that can happen is that the server captures a partial dump for this path
// because on the other thread gathering a crash dump, it TerminateProcess()d,
// causing this one to abort.
base::AutoLock lock(*g_non_crash_dump_lock);
// Create a fake EXCEPTION_POINTERS to give the handler something to work
// with.
EXCEPTION_POINTERS exception_pointers = {0};
// This is logically const, but EXCEPTION_POINTERS does not declare it as
// const, so we have to cast that away from the argument.
exception_pointers.ContextRecord = const_cast<CONTEXT*>(&context);
// We include a fake exception and use a code of '0x517a7ed' (something like
// "simulated") so that it's relatively obvious in windbg that it's not
// actually an exception. Most values in
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082.aspx have
// some of the top nibble set, so we make sure to pick a value that doesn't,
// so as to be unlikely to conflict.
const uint32_t kSimulatedExceptionCode = 0x517a7ed;
EXCEPTION_RECORD record = {0};
record.ExceptionCode = kSimulatedExceptionCode;
#if defined(ARCH_CPU_64_BITS)
record.ExceptionAddress = reinterpret_cast<void*>(context.Rip);
#else
record.ExceptionAddress = reinterpret_cast<void*>(context.Eip);
#endif // ARCH_CPU_64_BITS
exception_pointers.ExceptionRecord = &record;
g_non_crash_exception_information.thread_id = GetCurrentThreadId();
g_non_crash_exception_information.exception_pointers =
reinterpret_cast<crashpad::WinVMAddress>(&exception_pointers);
bool set_event_result = SetEvent(g_signal_non_crash_dump);
PLOG_IF(ERROR, !set_event_result) << "SetEvent";
DWORD wfso_result = WaitForSingleObject(g_non_crash_dump_done, INFINITE);
PLOG_IF(ERROR, wfso_result != WAIT_OBJECT_0) << "WaitForSingleObject";
}
} // namespace crashpad

View File

@ -19,6 +19,8 @@
#if defined(OS_MACOSX)
#include "client/simulate_crash_mac.h"
#elif defined(OS_WIN)
#include "client/simulate_crash_win.h"
#endif
#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_H_

View File

@ -0,0 +1,30 @@
// Copyright 2015 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_
#define CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_
#include <windows.h>
#include "client/crashpad_client.h"
//! \brief Captures the CPU context and captures a dump without an exception.
#define CRASHPAD_SIMULATE_CRASH() \
do { \
CONTEXT context; \
RtlCaptureContext(&context); \
crashpad::CrashpadClient::DumpWithoutCrash(context); \
} while (false)
#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_

View File

@ -97,6 +97,7 @@
['OS=="win"', {
'dependencies': [
'crashpad_snapshot_test_crashing_child',
'crashpad_snapshot_test_dump_without_crashing',
'crashpad_snapshot_test_image_reader',
'crashpad_snapshot_test_image_reader_module',
],
@ -152,6 +153,19 @@
'win/crashpad_snapshot_test_crashing_child.cc',
],
},
{
'target_name': 'crashpad_snapshot_test_dump_without_crashing',
'type': 'executable',
'dependencies': [
'../client/client.gyp:crashpad_client',
'../compat/compat.gyp:crashpad_compat',
'../third_party/mini_chromium/mini_chromium.gyp:base',
'../util/util.gyp:crashpad_util',
],
'sources': [
'win/crashpad_snapshot_test_dump_without_crashing.cc',
],
},
{
'target_name': 'crashpad_snapshot_test_image_reader',
'type': 'executable',

View File

@ -0,0 +1,42 @@
// Copyright 2015 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <windows.h>
#include "base/logging.h"
#include "client/crashpad_client.h"
#include "client/simulate_crash.h"
#include "util/file/file_io.h"
#include "util/win/address_types.h"
__declspec(noinline) crashpad::WinVMAddress CurrentAddress() {
return reinterpret_cast<crashpad::WinVMAddress>(_ReturnAddress());
}
int main(int argc, char* argv[]) {
CHECK_EQ(argc, 2);
crashpad::CrashpadClient client;
CHECK(client.SetHandler(argv[1]));
CHECK(client.UseHandler());
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle";
crashpad::WinVMAddress current_address = CurrentAddress();
crashpad::CheckedWriteFile(out, &current_address, sizeof(current_address));
CRASHPAD_SIMULATE_CRASH();
return 0;
}

View File

@ -36,65 +36,6 @@ namespace crashpad {
namespace test {
namespace {
HANDLE DuplicateEvent(HANDLE process, HANDLE event) {
HANDLE handle;
if (DuplicateHandle(GetCurrentProcess(),
event,
process,
&handle,
SYNCHRONIZE | EVENT_MODIFY_STATE,
false,
0)) {
return handle;
}
return nullptr;
}
class Delegate : public ExceptionHandlerServer::Delegate {
public:
Delegate(HANDLE server_ready, HANDLE completed_test_event)
: server_ready_(server_ready),
completed_test_event_(completed_test_event),
break_near_(0) {}
~Delegate() override {}
void set_break_near(WinVMAddress break_near) { break_near_ = break_near; }
void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); }
unsigned int ExceptionHandlerServerException(
HANDLE process,
WinVMAddress exception_information_address) override {
ScopedProcessSuspend suspend(process);
ProcessSnapshotWin snapshot;
snapshot.Initialize(process, ProcessSuspensionState::kSuspended);
snapshot.InitializeException(exception_information_address);
// Confirm the exception record was read correctly.
EXPECT_NE(snapshot.Exception()->ThreadID(), 0u);
EXPECT_EQ(snapshot.Exception()->Exception(), EXCEPTION_BREAKPOINT);
// Verify the exception happened at the expected location with a bit of
// slop space to allow for reading the current PC before the exception
// happens. See CrashingChildProcess::Run().
const uint64_t kAllowedOffset = 64;
EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_);
EXPECT_LT(snapshot.Exception()->ExceptionAddress(),
break_near_ + kAllowedOffset);
SetEvent(completed_test_event_);
return snapshot.Exception()->Exception();
}
private:
HANDLE server_ready_; // weak
HANDLE completed_test_event_; // weak
WinVMAddress break_near_;
DISALLOW_COPY_AND_ASSIGN(Delegate);
};
// Runs the ExceptionHandlerServer on a background thread.
class RunServerThread : public Thread {
public:
@ -133,13 +74,58 @@ class ScopedStopServerAndJoinThread {
DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread);
};
class CrashingDelegate : public ExceptionHandlerServer::Delegate {
public:
CrashingDelegate(HANDLE server_ready, HANDLE completed_test_event)
: server_ready_(server_ready),
completed_test_event_(completed_test_event),
break_near_(0) {}
~CrashingDelegate() override {}
void set_break_near(WinVMAddress break_near) { break_near_ = break_near; }
void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); }
unsigned int ExceptionHandlerServerException(
HANDLE process,
WinVMAddress exception_information_address) override {
ScopedProcessSuspend suspend(process);
ProcessSnapshotWin snapshot;
snapshot.Initialize(process, ProcessSuspensionState::kSuspended);
snapshot.InitializeException(exception_information_address);
// Confirm the exception record was read correctly.
EXPECT_NE(snapshot.Exception()->ThreadID(), 0u);
EXPECT_EQ(snapshot.Exception()->Exception(), EXCEPTION_BREAKPOINT);
// Verify the exception happened at the expected location with a bit of
// slop space to allow for reading the current PC before the exception
// happens. See TestCrashingChild().
const uint64_t kAllowedOffset = 64;
EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_);
EXPECT_LT(snapshot.Exception()->ExceptionAddress(),
break_near_ + kAllowedOffset);
SetEvent(completed_test_event_);
return snapshot.Exception()->Exception();
}
private:
HANDLE server_ready_; // weak
HANDLE completed_test_event_; // weak
WinVMAddress break_near_;
DISALLOW_COPY_AND_ASSIGN(CrashingDelegate);
};
void TestCrashingChild(const base::string16& directory_modification) {
// Set up the registration server on a background thread.
std::string pipe_name = "\\\\.\\pipe\\handler_test_pipe_" +
base::StringPrintf("%08x", GetCurrentProcessId());
ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr));
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));
Delegate delegate(server_ready.get(), completed.get());
CrashingDelegate delegate(server_ready.get(), completed.get());
ExceptionHandlerServer exception_handler_server;
RunServerThread server_thread(
@ -186,6 +172,104 @@ TEST(ExceptionSnapshotWinTest, ChildCrashWOW64) {
}
#endif // ARCH_CPU_64_BITS
class SimulateDelegate : public ExceptionHandlerServer::Delegate {
public:
SimulateDelegate(HANDLE server_ready, HANDLE completed_test_event)
: server_ready_(server_ready),
completed_test_event_(completed_test_event),
dump_near_(0) {}
~SimulateDelegate() override {}
void set_dump_near(WinVMAddress dump_near) { dump_near_ = dump_near; }
void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); }
unsigned int ExceptionHandlerServerException(
HANDLE process,
WinVMAddress exception_information_address) override {
ScopedProcessSuspend suspend(process);
ProcessSnapshotWin snapshot;
snapshot.Initialize(process, ProcessSuspensionState::kSuspended);
snapshot.InitializeException(exception_information_address);
EXPECT_TRUE(snapshot.Exception());
EXPECT_EQ(0, snapshot.Exception()->Exception());
EXPECT_EQ(0, snapshot.Exception()->ExceptionAddress());
// Verify the dump was captured at the expected location with some slop
// space.
const uint64_t kAllowedOffset = 64;
EXPECT_GT(snapshot.Exception()->Context()->InstructionPointer(),
dump_near_);
EXPECT_LT(snapshot.Exception()->Context()->InstructionPointer(),
dump_near_ + kAllowedOffset);
SetEvent(completed_test_event_);
return 0;
}
private:
HANDLE server_ready_; // weak
HANDLE completed_test_event_; // weak
WinVMAddress dump_near_;
DISALLOW_COPY_AND_ASSIGN(SimulateDelegate);
};
void TestDumpWithoutCrashingChild(
const base::string16& directory_modification) {
// Set up the registration server on a background thread.
std::string pipe_name = "\\\\.\\pipe\\handler_test_pipe_" +
base::StringPrintf("%08x", GetCurrentProcessId());
ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr));
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));
SimulateDelegate delegate(server_ready.get(), completed.get());
ExceptionHandlerServer exception_handler_server;
RunServerThread server_thread(
&exception_handler_server, &delegate, pipe_name);
server_thread.Start();
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
&exception_handler_server, &server_thread);
WaitForSingleObject(server_ready.get(), INFINITE);
// Spawn a child process, passing it the pipe name to connect to.
base::FilePath test_executable = Paths::Executable();
std::wstring child_test_executable =
test_executable.DirName()
.Append(directory_modification)
.Append(test_executable.BaseName().RemoveFinalExtension().value() +
L"_dump_without_crashing.exe")
.value();
ChildLauncher child(child_test_executable, base::UTF8ToUTF16(pipe_name));
child.Start();
// The child tells us (approximately) where it will capture a dump.
WinVMAddress dump_near_address;
LoggingReadFile(child.stdout_read_handle(),
&dump_near_address,
sizeof(dump_near_address));
delegate.set_dump_near(dump_near_address);
// Wait for the child to crash and the exception information to be validated.
WaitForSingleObject(completed.get(), INFINITE);
}
TEST(SimulateCrash, ChildDumpWithoutCrashing) {
TestDumpWithoutCrashingChild(FILE_PATH_LITERAL("."));
}
#if defined(ARCH_CPU_64_BITS)
TEST(SimulateCrash, ChildDumpWithoutCrashingWOW64) {
#ifndef NDEBUG
TestDumpWithoutCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Debug"));
#else
TestDumpWithoutCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Release"));
#endif
}
#endif // ARCH_CPU_64_BITS
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -103,19 +103,31 @@ class ClientData {
ClientData(HANDLE port,
ExceptionHandlerServer::Delegate* delegate,
ScopedKernelHANDLE process,
WinVMAddress exception_information_address,
WAITORTIMERCALLBACK dump_request_callback,
WinVMAddress crash_exception_information_address,
WinVMAddress non_crash_exception_information_address,
WAITORTIMERCALLBACK crash_dump_request_callback,
WAITORTIMERCALLBACK non_crash_dump_request_callback,
WAITORTIMERCALLBACK process_end_callback)
: dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),
: crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),
non_crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),
process_end_thread_pool_wait_(INVALID_HANDLE_VALUE),
lock_(),
port_(port),
delegate_(delegate),
dump_requested_event_(
crash_dump_requested_event_(
CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
non_crash_dump_requested_event_(
CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
non_crash_dump_completed_event_(
CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
process_(process.Pass()),
exception_information_address_(exception_information_address) {
RegisterThreadPoolWaits(dump_request_callback, process_end_callback);
crash_exception_information_address_(
crash_exception_information_address),
non_crash_exception_information_address_(
non_crash_exception_information_address) {
RegisterThreadPoolWaits(crash_dump_request_callback,
non_crash_dump_request_callback,
process_end_callback);
}
~ClientData() {
@ -128,22 +140,44 @@ class ClientData {
base::Lock* lock() { return &lock_; }
HANDLE port() const { return port_; }
ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
HANDLE dump_requested_event() const { return dump_requested_event_.get(); }
WinVMAddress exception_information_address() const {
return exception_information_address_;
HANDLE crash_dump_requested_event() const {
return crash_dump_requested_event_.get();
}
HANDLE non_crash_dump_requested_event() const {
return non_crash_dump_requested_event_.get();
}
HANDLE non_crash_dump_completed_event() const {
return non_crash_dump_completed_event_.get();
}
WinVMAddress crash_exception_information_address() const {
return crash_exception_information_address_;
}
WinVMAddress non_crash_exception_information_address() const {
return non_crash_exception_information_address_;
}
HANDLE process() const { return process_.get(); }
private:
void RegisterThreadPoolWaits(WAITORTIMERCALLBACK dump_request_callback,
WAITORTIMERCALLBACK process_end_callback) {
if (!RegisterWaitForSingleObject(&dump_request_thread_pool_wait_,
dump_requested_event_.get(),
dump_request_callback,
void RegisterThreadPoolWaits(
WAITORTIMERCALLBACK crash_dump_request_callback,
WAITORTIMERCALLBACK non_crash_dump_request_callback,
WAITORTIMERCALLBACK process_end_callback) {
if (!RegisterWaitForSingleObject(&crash_dump_request_thread_pool_wait_,
crash_dump_requested_event_.get(),
crash_dump_request_callback,
this,
INFINITE,
WT_EXECUTEDEFAULT)) {
LOG(ERROR) << "RegisterWaitForSingleObject dump requested";
LOG(ERROR) << "RegisterWaitForSingleObject crash dump requested";
}
if (!RegisterWaitForSingleObject(&non_crash_dump_request_thread_pool_wait_,
non_crash_dump_requested_event_.get(),
non_crash_dump_request_callback,
this,
INFINITE,
WT_EXECUTEDEFAULT)) {
LOG(ERROR) << "RegisterWaitForSingleObject non-crash dump requested";
}
if (!RegisterWaitForSingleObject(&process_end_thread_pool_wait_,
@ -160,23 +194,31 @@ class ClientData {
// delete this object. Because of this, it must be executed on the main
// thread, not a threadpool thread.
void UnregisterThreadPoolWaits() {
UnregisterWaitEx(dump_request_thread_pool_wait_, INVALID_HANDLE_VALUE);
dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
UnregisterWaitEx(crash_dump_request_thread_pool_wait_,
INVALID_HANDLE_VALUE);
crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
UnregisterWaitEx(non_crash_dump_request_thread_pool_wait_,
INVALID_HANDLE_VALUE);
non_crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
UnregisterWaitEx(process_end_thread_pool_wait_, INVALID_HANDLE_VALUE);
process_end_thread_pool_wait_ = INVALID_HANDLE_VALUE;
}
// These are only accessed on the main thread.
HANDLE dump_request_thread_pool_wait_;
HANDLE crash_dump_request_thread_pool_wait_;
HANDLE non_crash_dump_request_thread_pool_wait_;
HANDLE process_end_thread_pool_wait_;
base::Lock lock_;
// Access to these fields must be guarded by lock_.
HANDLE port_; // weak
ExceptionHandlerServer::Delegate* delegate_; // weak
ScopedKernelHANDLE dump_requested_event_;
ScopedKernelHANDLE crash_dump_requested_event_;
ScopedKernelHANDLE non_crash_dump_requested_event_;
ScopedKernelHANDLE non_crash_dump_completed_event_;
ScopedKernelHANDLE process_;
WinVMAddress exception_information_address_;
WinVMAddress crash_exception_information_address_;
WinVMAddress non_crash_exception_information_address_;
DISALLOW_COPY_AND_ASSIGN(ClientData);
};
@ -364,21 +406,29 @@ bool ExceptionHandlerServer::ServiceClientConnection(
internal::ClientData* client;
{
base::AutoLock lock(*service_context.clients_lock());
client =
new internal::ClientData(service_context.port(),
service_context.delegate(),
ScopedKernelHANDLE(client_process),
message.registration.exception_information,
&OnDumpEvent,
&OnProcessEnd);
client = new internal::ClientData(
service_context.port(),
service_context.delegate(),
ScopedKernelHANDLE(client_process),
message.registration.crash_exception_information,
message.registration.non_crash_exception_information,
&OnCrashDumpEvent,
&OnNonCrashDumpEvent,
&OnProcessEnd);
service_context.clients()->insert(client);
}
// Duplicate the events back to the client so they can request a dump.
ServerToClientMessage response;
response.registration.request_report_event =
base::checked_cast<uint32_t>(reinterpret_cast<uintptr_t>(
DuplicateEvent(client->process(), client->dump_requested_event())));
response.registration.request_crash_dump_event =
base::checked_cast<uint32_t>(reinterpret_cast<uintptr_t>(DuplicateEvent(
client->process(), client->crash_dump_requested_event())));
response.registration.request_non_crash_dump_event =
base::checked_cast<uint32_t>(reinterpret_cast<uintptr_t>(DuplicateEvent(
client->process(), client->non_crash_dump_requested_event())));
response.registration.non_crash_dump_completed_event =
base::checked_cast<uint32_t>(reinterpret_cast<uintptr_t>(DuplicateEvent(
client->process(), client->non_crash_dump_completed_event())));
if (!LoggingWriteFile(service_context.pipe(), &response, sizeof(response)))
return false;
@ -408,18 +458,32 @@ DWORD __stdcall ExceptionHandlerServer::PipeServiceProc(void* ctx) {
}
// static
void __stdcall ExceptionHandlerServer::OnDumpEvent(void* ctx, BOOLEAN) {
void __stdcall ExceptionHandlerServer::OnCrashDumpEvent(void* ctx, BOOLEAN) {
// This function is executed on the thread pool.
internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
base::AutoLock lock(*client->lock());
// Capture the exception.
unsigned int exit_code = client->delegate()->ExceptionHandlerServerException(
client->process(), client->exception_information_address());
client->process(), client->crash_exception_information_address());
TerminateProcess(client->process(), exit_code);
}
// static
void __stdcall ExceptionHandlerServer::OnNonCrashDumpEvent(void* ctx, BOOLEAN) {
// This function is executed on the thread pool.
internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
base::AutoLock lock(*client->lock());
// Capture the exception.
client->delegate()->ExceptionHandlerServerException(
client->process(), client->non_crash_exception_information_address());
bool result = SetEvent(client->non_crash_dump_completed_event());
PLOG_IF(ERROR, !result) << "SetEvent";
}
// static
void __stdcall ExceptionHandlerServer::OnProcessEnd(void* ctx, BOOLEAN) {
// This function is executed on the thread pool.

View File

@ -76,7 +76,8 @@ class ExceptionHandlerServer {
static bool ServiceClientConnection(
const internal::PipeServiceContext& service_context);
static DWORD __stdcall PipeServiceProc(void* ctx);
static void __stdcall OnDumpEvent(void* ctx, BOOLEAN);
static void __stdcall OnCrashDumpEvent(void* ctx, BOOLEAN);
static void __stdcall OnNonCrashDumpEvent(void* ctx, BOOLEAN);
static void __stdcall OnProcessEnd(void* ctx, BOOLEAN);
ScopedKernelHANDLE port_;

View File

@ -50,8 +50,14 @@ struct RegistrationRequest {
DWORD client_process_id;
//! \brief The address, in the client process address space, of an
//! ExceptionInformation structure.
WinVMAddress exception_information;
//! ExceptionInformation structure, used when handling a crash dump
//! request.
WinVMAddress crash_exception_information;
//! \brief The address, in the client process address space, of an
//! ExceptionInformation structure, used when handling a non-crashing dump
//! request.
WinVMAddress non_crash_exception_information;
};
//! \brief A message only sent to the server by itself to trigger shutdown.
@ -88,7 +94,17 @@ struct RegistrationResponse {
//! \brief An event `HANDLE`, valid in the client process, that should be
//! signaled to request a crash report. 64-bit clients should convert the
//! value to a `HANDLE` using sign-extension.
uint32_t request_report_event;
uint32_t request_crash_dump_event;
//! \brief An event `HANDLE`, valid in the client process, that should be
//! signaled to request a non-crashing dump be taken. 64-bit clients
//! should convert the value to `HANDLEEE` using sign-extension.
uint32_t request_non_crash_dump_event;
//! \brief An event `HANDLE`, valid in the client process, that will be
//! signaled by the server when the non-crashing dump is complete. 64-bit
//! clients should convert the value to `HANDLEEE` using sign-extension.
uint32_t non_crash_dump_completed_event;
};
//! \brief The response sent back to the client via SendToCrashHandlerServer().