Chromium Code Reviews| Index: client/crashpad_client_win.cc |
| diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc |
| index e19aa7469d81a8015f5bd9bc70a52f97761ab999..e65206b8d1e0f6a7142cfeaf329e81b6da3a6067 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(). |
| +base::Lock g_non_crash_dump_lock; |
|
Mark Mentovai
2015/09/24 20:37:29
This carries a static constructor. This should be
scottmg
2015/09/24 21:16:59
Done.
|
| + |
| +// 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 |
| @@ -107,8 +122,10 @@ bool CrashpadClient::SetHandler(const std::string& ipc_port) { |
| 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 +136,63 @@ 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)); |
| + |
| 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 |
| +bool 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 false; |
| + } |
| + |
| + // 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 (with a null EXCEPTION_RECORD), so that |
| + // the handler has a CONTEXT to start from. We don't include an |
| + // EXCEPTION_RECORD as there hasn't actually been an exception. |
| + EXCEPTION_POINTERS exception_pointers = {0}; |
| + // This is logically const, but EXCEPTION_POINTERS does not declare it as |
|
Mark Mentovai
2015/09/24 20:37:29
Blank line before this, so that EXCEPTION_POINTERS
scottmg
2015/09/24 21:16:59
Done.
|
| + // const, so we have to cast that away from the argument. |
| + exception_pointers.ContextRecord = const_cast<CONTEXT*>(&context); |
| + |
| + 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"; |
| + |
| + return true; |
| +} |
| + |
| } // namespace crashpad |