mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-17 16:43:53 +00:00
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:
parent
9bc0a99681
commit
475ac81cce
@ -47,6 +47,7 @@
|
|||||||
'simulate_crash.h',
|
'simulate_crash.h',
|
||||||
'simulate_crash_mac.cc',
|
'simulate_crash_mac.cc',
|
||||||
'simulate_crash_mac.h',
|
'simulate_crash_mac.h',
|
||||||
|
'simulate_crash_win.h',
|
||||||
],
|
],
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['OS=="win"', {
|
['OS=="win"', {
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
#include "base/mac/scoped_mach_port.h"
|
#include "base/mac/scoped_mach_port.h"
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
@ -87,6 +89,13 @@ class CrashpadClient {
|
|||||||
//!
|
//!
|
||||||
//! \return `true` on success and `false` on failure.
|
//! \return `true` on success and `false` on failure.
|
||||||
bool SetHandler(const std::string& ipc_port);
|
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
|
#endif
|
||||||
|
|
||||||
//! \brief Configures the process to direct its crashes to a Crashpad handler.
|
//! \brief Configures the process to direct its crashes to a Crashpad handler.
|
||||||
|
@ -21,17 +21,32 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/string16.h"
|
#include "base/strings/string16.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
|
#include "base/synchronization/lock.h"
|
||||||
#include "util/file/file_io.h"
|
#include "util/file/file_io.h"
|
||||||
#include "util/win/registration_protocol_win.h"
|
#include "util/win/registration_protocol_win.h"
|
||||||
#include "util/win/scoped_handle.h"
|
#include "util/win/scoped_handle.h"
|
||||||
|
|
||||||
namespace {
|
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;
|
HANDLE g_signal_exception = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
// Where we store the exception information that the crash handler reads.
|
// 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) {
|
LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
||||||
// Tracks whether a thread has already entered UnhandledExceptionHandler.
|
// 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
|
// Otherwise, we're the first thread, so record the exception pointer and
|
||||||
// signal the crash handler.
|
// signal the crash handler.
|
||||||
g_exception_information.thread_id = GetCurrentThreadId();
|
g_crash_exception_information.thread_id = GetCurrentThreadId();
|
||||||
g_exception_information.exception_pointers =
|
g_crash_exception_information.exception_pointers =
|
||||||
reinterpret_cast<crashpad::WinVMAddress>(exception_pointers);
|
reinterpret_cast<crashpad::WinVMAddress>(exception_pointers);
|
||||||
|
|
||||||
// Now signal the crash server, which will take a dump and then terminate us
|
// 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
|
// 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
|
// ourselves in case the crash server is gone, so that we don't leave zombies
|
||||||
// around. This would ideally never happen.
|
// around. This would ideally never happen.
|
||||||
// TODO(scottmg): Re-add the "reply" event here, for implementing
|
|
||||||
// DumpWithoutCrashing.
|
|
||||||
Sleep(kMillisecondsUntilTerminate);
|
Sleep(kMillisecondsUntilTerminate);
|
||||||
|
|
||||||
LOG(ERROR) << "crash server did not respond, self-terminating";
|
LOG(ERROR) << "crash server did not respond, self-terminating";
|
||||||
@ -102,13 +115,19 @@ bool CrashpadClient::StartHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CrashpadClient::SetHandler(const std::string& ipc_port) {
|
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;
|
ClientToServerMessage message;
|
||||||
memset(&message, 0, sizeof(message));
|
memset(&message, 0, sizeof(message));
|
||||||
message.type = ClientToServerMessage::kRegister;
|
message.type = ClientToServerMessage::kRegister;
|
||||||
message.registration.version = RegistrationRequest::kMessageVersion;
|
message.registration.version = RegistrationRequest::kMessageVersion;
|
||||||
message.registration.client_process_id = GetCurrentProcessId();
|
message.registration.client_process_id = GetCurrentProcessId();
|
||||||
message.registration.exception_information =
|
message.registration.crash_exception_information =
|
||||||
reinterpret_cast<WinVMAddress>(&g_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};
|
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.
|
// The server returns these already duplicated to be valid in this process.
|
||||||
g_signal_exception = reinterpret_cast<HANDLE>(
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CrashpadClient::UseHandler() {
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// In theory we could store the previous handler but it is not clear what
|
// In theory we could store the previous handler but it is not clear what
|
||||||
// use we have for it.
|
// use we have for it.
|
||||||
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
||||||
return true;
|
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
|
} // namespace crashpad
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
#include "client/simulate_crash_mac.h"
|
#include "client/simulate_crash_mac.h"
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
#include "client/simulate_crash_win.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_H_
|
#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_H_
|
||||||
|
30
client/simulate_crash_win.h
Normal file
30
client/simulate_crash_win.h
Normal 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_
|
@ -97,6 +97,7 @@
|
|||||||
['OS=="win"', {
|
['OS=="win"', {
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'crashpad_snapshot_test_crashing_child',
|
'crashpad_snapshot_test_crashing_child',
|
||||||
|
'crashpad_snapshot_test_dump_without_crashing',
|
||||||
'crashpad_snapshot_test_image_reader',
|
'crashpad_snapshot_test_image_reader',
|
||||||
'crashpad_snapshot_test_image_reader_module',
|
'crashpad_snapshot_test_image_reader_module',
|
||||||
],
|
],
|
||||||
@ -152,6 +153,19 @@
|
|||||||
'win/crashpad_snapshot_test_crashing_child.cc',
|
'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',
|
'target_name': 'crashpad_snapshot_test_image_reader',
|
||||||
'type': 'executable',
|
'type': 'executable',
|
||||||
|
42
snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc
Normal file
42
snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc
Normal 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, ¤t_address, sizeof(current_address));
|
||||||
|
|
||||||
|
CRASHPAD_SIMULATE_CRASH();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -36,65 +36,6 @@ namespace crashpad {
|
|||||||
namespace test {
|
namespace test {
|
||||||
namespace {
|
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.
|
// Runs the ExceptionHandlerServer on a background thread.
|
||||||
class RunServerThread : public Thread {
|
class RunServerThread : public Thread {
|
||||||
public:
|
public:
|
||||||
@ -133,13 +74,58 @@ class ScopedStopServerAndJoinThread {
|
|||||||
DISALLOW_COPY_AND_ASSIGN(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) {
|
void TestCrashingChild(const base::string16& directory_modification) {
|
||||||
// Set up the registration server on a background thread.
|
// Set up the registration server on a background thread.
|
||||||
std::string pipe_name = "\\\\.\\pipe\\handler_test_pipe_" +
|
std::string pipe_name = "\\\\.\\pipe\\handler_test_pipe_" +
|
||||||
base::StringPrintf("%08x", GetCurrentProcessId());
|
base::StringPrintf("%08x", GetCurrentProcessId());
|
||||||
ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr));
|
ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr));
|
||||||
ScopedKernelHANDLE completed(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;
|
ExceptionHandlerServer exception_handler_server;
|
||||||
RunServerThread server_thread(
|
RunServerThread server_thread(
|
||||||
@ -186,6 +172,104 @@ TEST(ExceptionSnapshotWinTest, ChildCrashWOW64) {
|
|||||||
}
|
}
|
||||||
#endif // ARCH_CPU_64_BITS
|
#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
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -103,19 +103,31 @@ class ClientData {
|
|||||||
ClientData(HANDLE port,
|
ClientData(HANDLE port,
|
||||||
ExceptionHandlerServer::Delegate* delegate,
|
ExceptionHandlerServer::Delegate* delegate,
|
||||||
ScopedKernelHANDLE process,
|
ScopedKernelHANDLE process,
|
||||||
WinVMAddress exception_information_address,
|
WinVMAddress crash_exception_information_address,
|
||||||
WAITORTIMERCALLBACK dump_request_callback,
|
WinVMAddress non_crash_exception_information_address,
|
||||||
|
WAITORTIMERCALLBACK crash_dump_request_callback,
|
||||||
|
WAITORTIMERCALLBACK non_crash_dump_request_callback,
|
||||||
WAITORTIMERCALLBACK process_end_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),
|
process_end_thread_pool_wait_(INVALID_HANDLE_VALUE),
|
||||||
lock_(),
|
lock_(),
|
||||||
port_(port),
|
port_(port),
|
||||||
delegate_(delegate),
|
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)),
|
CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
|
||||||
process_(process.Pass()),
|
process_(process.Pass()),
|
||||||
exception_information_address_(exception_information_address) {
|
crash_exception_information_address_(
|
||||||
RegisterThreadPoolWaits(dump_request_callback, process_end_callback);
|
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() {
|
~ClientData() {
|
||||||
@ -128,22 +140,44 @@ class ClientData {
|
|||||||
base::Lock* lock() { return &lock_; }
|
base::Lock* lock() { return &lock_; }
|
||||||
HANDLE port() const { return port_; }
|
HANDLE port() const { return port_; }
|
||||||
ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
|
ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
|
||||||
HANDLE dump_requested_event() const { return dump_requested_event_.get(); }
|
HANDLE crash_dump_requested_event() const {
|
||||||
WinVMAddress exception_information_address() const {
|
return crash_dump_requested_event_.get();
|
||||||
return exception_information_address_;
|
}
|
||||||
|
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(); }
|
HANDLE process() const { return process_.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RegisterThreadPoolWaits(WAITORTIMERCALLBACK dump_request_callback,
|
void RegisterThreadPoolWaits(
|
||||||
WAITORTIMERCALLBACK process_end_callback) {
|
WAITORTIMERCALLBACK crash_dump_request_callback,
|
||||||
if (!RegisterWaitForSingleObject(&dump_request_thread_pool_wait_,
|
WAITORTIMERCALLBACK non_crash_dump_request_callback,
|
||||||
dump_requested_event_.get(),
|
WAITORTIMERCALLBACK process_end_callback) {
|
||||||
dump_request_callback,
|
if (!RegisterWaitForSingleObject(&crash_dump_request_thread_pool_wait_,
|
||||||
|
crash_dump_requested_event_.get(),
|
||||||
|
crash_dump_request_callback,
|
||||||
this,
|
this,
|
||||||
INFINITE,
|
INFINITE,
|
||||||
WT_EXECUTEDEFAULT)) {
|
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_,
|
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
|
// delete this object. Because of this, it must be executed on the main
|
||||||
// thread, not a threadpool thread.
|
// thread, not a threadpool thread.
|
||||||
void UnregisterThreadPoolWaits() {
|
void UnregisterThreadPoolWaits() {
|
||||||
UnregisterWaitEx(dump_request_thread_pool_wait_, INVALID_HANDLE_VALUE);
|
UnregisterWaitEx(crash_dump_request_thread_pool_wait_,
|
||||||
dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
|
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);
|
UnregisterWaitEx(process_end_thread_pool_wait_, INVALID_HANDLE_VALUE);
|
||||||
process_end_thread_pool_wait_ = INVALID_HANDLE_VALUE;
|
process_end_thread_pool_wait_ = INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are only accessed on the main thread.
|
// 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_;
|
HANDLE process_end_thread_pool_wait_;
|
||||||
|
|
||||||
base::Lock lock_;
|
base::Lock lock_;
|
||||||
// Access to these fields must be guarded by lock_.
|
// Access to these fields must be guarded by lock_.
|
||||||
HANDLE port_; // weak
|
HANDLE port_; // weak
|
||||||
ExceptionHandlerServer::Delegate* delegate_; // 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_;
|
ScopedKernelHANDLE process_;
|
||||||
WinVMAddress exception_information_address_;
|
WinVMAddress crash_exception_information_address_;
|
||||||
|
WinVMAddress non_crash_exception_information_address_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ClientData);
|
DISALLOW_COPY_AND_ASSIGN(ClientData);
|
||||||
};
|
};
|
||||||
@ -364,21 +406,29 @@ bool ExceptionHandlerServer::ServiceClientConnection(
|
|||||||
internal::ClientData* client;
|
internal::ClientData* client;
|
||||||
{
|
{
|
||||||
base::AutoLock lock(*service_context.clients_lock());
|
base::AutoLock lock(*service_context.clients_lock());
|
||||||
client =
|
client = new internal::ClientData(
|
||||||
new internal::ClientData(service_context.port(),
|
service_context.port(),
|
||||||
service_context.delegate(),
|
service_context.delegate(),
|
||||||
ScopedKernelHANDLE(client_process),
|
ScopedKernelHANDLE(client_process),
|
||||||
message.registration.exception_information,
|
message.registration.crash_exception_information,
|
||||||
&OnDumpEvent,
|
message.registration.non_crash_exception_information,
|
||||||
&OnProcessEnd);
|
&OnCrashDumpEvent,
|
||||||
|
&OnNonCrashDumpEvent,
|
||||||
|
&OnProcessEnd);
|
||||||
service_context.clients()->insert(client);
|
service_context.clients()->insert(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duplicate the events back to the client so they can request a dump.
|
// Duplicate the events back to the client so they can request a dump.
|
||||||
ServerToClientMessage response;
|
ServerToClientMessage response;
|
||||||
response.registration.request_report_event =
|
response.registration.request_crash_dump_event =
|
||||||
base::checked_cast<uint32_t>(reinterpret_cast<uintptr_t>(
|
base::checked_cast<uint32_t>(reinterpret_cast<uintptr_t>(DuplicateEvent(
|
||||||
DuplicateEvent(client->process(), client->dump_requested_event())));
|
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)))
|
if (!LoggingWriteFile(service_context.pipe(), &response, sizeof(response)))
|
||||||
return false;
|
return false;
|
||||||
@ -408,18 +458,32 @@ DWORD __stdcall ExceptionHandlerServer::PipeServiceProc(void* ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void __stdcall ExceptionHandlerServer::OnDumpEvent(void* ctx, BOOLEAN) {
|
void __stdcall ExceptionHandlerServer::OnCrashDumpEvent(void* ctx, BOOLEAN) {
|
||||||
// This function is executed on the thread pool.
|
// This function is executed on the thread pool.
|
||||||
internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
|
internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
|
||||||
base::AutoLock lock(*client->lock());
|
base::AutoLock lock(*client->lock());
|
||||||
|
|
||||||
// Capture the exception.
|
// Capture the exception.
|
||||||
unsigned int exit_code = client->delegate()->ExceptionHandlerServerException(
|
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);
|
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
|
// static
|
||||||
void __stdcall ExceptionHandlerServer::OnProcessEnd(void* ctx, BOOLEAN) {
|
void __stdcall ExceptionHandlerServer::OnProcessEnd(void* ctx, BOOLEAN) {
|
||||||
// This function is executed on the thread pool.
|
// This function is executed on the thread pool.
|
||||||
|
@ -76,7 +76,8 @@ class ExceptionHandlerServer {
|
|||||||
static bool ServiceClientConnection(
|
static bool ServiceClientConnection(
|
||||||
const internal::PipeServiceContext& service_context);
|
const internal::PipeServiceContext& service_context);
|
||||||
static DWORD __stdcall PipeServiceProc(void* ctx);
|
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);
|
static void __stdcall OnProcessEnd(void* ctx, BOOLEAN);
|
||||||
|
|
||||||
ScopedKernelHANDLE port_;
|
ScopedKernelHANDLE port_;
|
||||||
|
@ -50,8 +50,14 @@ struct RegistrationRequest {
|
|||||||
DWORD client_process_id;
|
DWORD client_process_id;
|
||||||
|
|
||||||
//! \brief The address, in the client process address space, of an
|
//! \brief The address, in the client process address space, of an
|
||||||
//! ExceptionInformation structure.
|
//! ExceptionInformation structure, used when handling a crash dump
|
||||||
WinVMAddress exception_information;
|
//! 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.
|
//! \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
|
//! \brief An event `HANDLE`, valid in the client process, that should be
|
||||||
//! signaled to request a crash report. 64-bit clients should convert the
|
//! signaled to request a crash report. 64-bit clients should convert the
|
||||||
//! value to a `HANDLE` using sign-extension.
|
//! 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().
|
//! \brief The response sent back to the client via SendToCrashHandlerServer().
|
||||||
|
Loading…
x
Reference in New Issue
Block a user