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 |