| Index: client/crashpad_client_win.cc
|
| diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc
|
| index e19aa7469d81a8015f5bd9bc70a52f97761ab999..195916004198e2a517ff7b8fe30aa4f244552da9 100644
|
| --- a/client/crashpad_client_win.cc
|
| +++ b/client/crashpad_client_win.cc
|
| @@ -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
|
|
|