From 1a770c8237733e2f68bd506a99d05d82fe25fc6b Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 14 Aug 2015 15:22:09 -0700 Subject: [PATCH] Implement more of CrashpadClient on Windows SetHandler connects to RegistrationServer as a handler, and catching an exception writes EXCEPTION_POINTERS to CrashpadInfo. R=cpu@chromium.org, mark@chromium.org BUG=crashpad:1 Review URL: https://codereview.chromium.org/1287073002 . --- client/crashpad_client_mac.cc | 1 - client/crashpad_client_win.cc | 96 ++++++++++++++++++++++++++++++++--- client/crashpad_info.cc | 1 - client/crashpad_info.h | 16 ++++++ 4 files changed, 106 insertions(+), 8 deletions(-) diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc index e97e58b1..c81dd543 100644 --- a/client/crashpad_client_mac.cc +++ b/client/crashpad_client_mac.cc @@ -21,7 +21,6 @@ #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/strings/stringprintf.h" -#include "client/crashpad_client.h" #include "util/mach/child_port_handshake.h" #include "util/mach/exception_ports.h" #include "util/mach/mach_extensions.h" diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index 734bedfd..3e109005 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -16,14 +16,21 @@ #include +#include "base/atomicops.h" #include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "client/crashpad_info.h" +#include "client/registration_protocol_win.h" +#include "util/file/file_io.h" +#include "util/win/scoped_handle.h" namespace { // Time to wait for the handler to create a dump. This is tricky to figure out. const DWORD kMillisecondsUntilTerminate = 5000; // This is the exit code that the process will return to the system once the -// crash has been handled by Crashpad. We don't want to clash with the +// crash has been handled by Crashpad. We don't want to clash with the // application-defined exit codes but we don't know them so we use one that is // unlikely to be used. const UINT kCrashExitCode = 0xffff7001; @@ -32,8 +39,30 @@ const UINT kCrashExitCode = 0xffff7001; HANDLE g_signal_exception = nullptr; HANDLE g_wait_termination = nullptr; +// Tracks whether a thread has already entered UnhandledExceptionHandler. +base::subtle::AtomicWord g_have_crashed; + LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { - // TODO (cpu): Here write |exception_pointers| to g_crashpad_info. + // This is a per-process handler. While this handler is being invoked, other + // threads are still executing as usual, so multiple threads could enter at + // the same time. Because we're in a crashing state, we shouldn't be doing + // anything that might cause allocations, call into kernel mode, etc. So, we + // don't want to take a critical section here to avoid simultaneous access to + // the global exception pointers in CrashpadInfo. Because the crash handler + // will record all threads, it's fine to simply have the second and subsequent + // entrants block here. They will soon be suspended by the crash handler, and + // then the entire process will be terminated below. This means that we won't + // save the exception pointers from the second and further crashes, but + // contention here is very unlikely, and we'll still have a stack that's + // blocked at this location. + if (base::subtle::Barrier_AtomicIncrement(&g_have_crashed, 1) > 1) { + SleepEx(false, INFINITE); + } + + // Otherwise, we're the first thread, so record the exception pointer and + // signal the crash handler. + crashpad::CrashpadInfo::GetCrashpadInfo()->set_exception_pointers( + exception_pointers); DWORD rv = SignalObjectAndWait(g_signal_exception, g_wait_termination, kMillisecondsUntilTerminate, @@ -51,6 +80,45 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { return 0L; } +// Returns a pipe handle connected to the RegistrationServer. +crashpad::ScopedFileHANDLE Connect(const base::string16& pipe_name) { + crashpad::ScopedFileHANDLE pipe; + const int kMaxTries = 5; + for (int tries = 0; tries < kMaxTries; ++tries) { + pipe.reset(CreateFile(pipe_name.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, + nullptr)); + if (pipe.is_valid()) + break; + + // If busy, wait 60s before retrying. + if (GetLastError() != ERROR_PIPE_BUSY) { + PLOG(ERROR) << "CreateFile pipe connection"; + return crashpad::ScopedFileHANDLE(); + } else if (!WaitNamedPipe(pipe_name.c_str(), 60000)) { + PLOG(ERROR) << "WaitNamedPipe"; + } + } + + if (!pipe.is_valid()) + return crashpad::ScopedFileHANDLE(); + + DWORD mode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState(pipe.get(), + &mode, + nullptr, // Don't set maximum bytes. + nullptr)) { // Don't set maximum time. + PLOG(ERROR) << "SetNamedPipeHandleState"; + return crashpad::ScopedFileHANDLE(); + } + + return pipe.Pass(); +} + } // namespace namespace crashpad { @@ -67,14 +135,30 @@ bool CrashpadClient::StartHandler( const std::string& url, const std::map& annotations, const std::vector& arguments) { - // TODO(cpu): Provide a reference implementation. return false; } bool CrashpadClient::SetHandler(const std::string& ipc_port) { - // TODO (cpu): Contact the handler and obtain g_signal_exception and - // g_wait_termination. - return false; + RegistrationRequest request = {0}; + request.client_process_id = GetCurrentProcessId(); + request.crashpad_info_address = + reinterpret_cast(CrashpadInfo::GetCrashpadInfo()); + + RegistrationResponse response = {0}; + + ScopedFileHANDLE pipe = Connect(base::UTF8ToUTF16(ipc_port)); + if (!pipe.is_valid()) + return false; + bool result = LoggingWriteFile(pipe.get(), &request, sizeof(request)) && + LoggingReadFile(pipe.get(), &response, sizeof(response)); + if (!result) + return result; + + // The server returns these already duplicated to be valid in this process. + g_signal_exception = reinterpret_cast(response.request_report_event); + g_wait_termination = + reinterpret_cast(response.report_complete_event); + return true; } bool CrashpadClient::UseHandler() { diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index 2fe3b1f7..b1b1f216 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -14,7 +14,6 @@ #include "client/crashpad_info.h" -#include "build/build_config.h" #include "util/stdlib/cxx.h" #if defined(OS_MACOSX) diff --git a/client/crashpad_info.h b/client/crashpad_info.h index 5ce9fb8e..57ea4eb3 100644 --- a/client/crashpad_info.h +++ b/client/crashpad_info.h @@ -19,9 +19,14 @@ #include +#include "build/build_config.h" #include "client/simple_string_dictionary.h" #include "util/misc/tri_state.h" +#if defined(OS_WIN) +#include +#endif // OS_WIN + namespace crashpad { //! \brief A structure that can be used by a Crashpad-enabled program to @@ -93,6 +98,13 @@ struct CrashpadInfo { system_crash_reporter_forwarding_ = system_crash_reporter_forwarding; } +#if defined(OS_WIN) + //! \brief Save an EXCEPTION_POINTERS record for the crash handler. + void set_exception_pointers(EXCEPTION_POINTERS* exception_pointers) { + exception_pointers_ = exception_pointers; + } +#endif // OS_WIN + enum : uint32_t { kSignature = 'CPad', }; @@ -116,6 +128,10 @@ struct CrashpadInfo { uint16_t padding_0_; SimpleStringDictionary* simple_annotations_; // weak +#if defined(OS_WIN) + EXCEPTION_POINTERS* exception_pointers_; +#endif // OS_WIN + #if defined(__clang__) #pragma clang diagnostic pop #endif