Index: third_party/crashpad/crashpad/client/crashpad_client_win.cc |
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_win.cc b/third_party/crashpad/crashpad/client/crashpad_client_win.cc |
index b9e42c673e04c30dca730a500d8c6d72d3ffab1c..235ef1858e85b421170e4dd56827d24ffeb2196f 100644 |
--- a/third_party/crashpad/crashpad/client/crashpad_client_win.cc |
+++ b/third_party/crashpad/crashpad/client/crashpad_client_win.cc |
@@ -22,24 +22,29 @@ |
#include "base/atomicops.h" |
#include "base/logging.h" |
+#include "base/macros.h" |
#include "base/scoped_generic.h" |
#include "base/strings/string16.h" |
#include "base/strings/stringprintf.h" |
#include "base/strings/utf_string_conversions.h" |
#include "base/synchronization/lock.h" |
#include "util/file/file_io.h" |
+#include "util/misc/random_string.h" |
#include "util/win/address_types.h" |
#include "util/win/command_line.h" |
#include "util/win/critical_section_with_debug_info.h" |
#include "util/win/get_function.h" |
#include "util/win/handle.h" |
+#include "util/win/initial_client_data.h" |
#include "util/win/nt_internals.h" |
#include "util/win/ntstatus_logging.h" |
#include "util/win/process_info.h" |
#include "util/win/registration_protocol_win.h" |
-#include "util/win/scoped_handle.h" |
#include "util/win/scoped_process_suspend.h" |
#include "util/win/termination_codes.h" |
+#include "util/win/xp_compat.h" |
+ |
+namespace crashpad { |
namespace { |
@@ -48,7 +53,7 @@ namespace { |
HANDLE g_signal_exception = INVALID_HANDLE_VALUE; |
// Where we store the exception information that the crash handler reads. |
-crashpad::ExceptionInformation g_crash_exception_information; |
+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 |
@@ -61,7 +66,20 @@ 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; |
+ExceptionInformation g_non_crash_exception_information; |
+ |
+enum class StartupState : int { |
+ kNotReady = 0, // This must be value 0 because it is the initial value of a |
+ // global AtomicWord. |
+ kSucceeded = 1, // The CreateProcess() for the handler succeeded. |
+ kFailed = 2, // The handler failed to start. |
+}; |
+ |
+// This is a tri-state of type StartupState. It starts at 0 == kNotReady, and |
+// when the handler is known to have started successfully, or failed to start |
+// the value will be updated. The unhandled exception filter will not proceed |
+// until one of those two cases happens. |
+base::subtle::AtomicWord g_handler_startup_state; |
// A CRITICAL_SECTION initialized with |
// RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO to force it to be allocated with a |
@@ -70,7 +88,37 @@ crashpad::ExceptionInformation g_non_crash_exception_information; |
// list, so this allows the handler to capture all of them. |
CRITICAL_SECTION g_critical_section_with_debug_info; |
+void SetHandlerStartupState(StartupState state) { |
+ DCHECK(state == StartupState::kSucceeded || |
+ state == StartupState::kFailed); |
+ base::subtle::Acquire_Store(&g_handler_startup_state, |
+ static_cast<base::subtle::AtomicWord>(state)); |
+} |
+ |
+StartupState BlockUntilHandlerStartedOrFailed() { |
+ // Wait until we know the handler has either succeeded or failed to start. |
+ base::subtle::AtomicWord startup_state; |
+ while ( |
+ (startup_state = base::subtle::Release_Load(&g_handler_startup_state)) == |
+ static_cast<int>(StartupState::kNotReady)) { |
+ Sleep(1); |
+ } |
+ |
+ return static_cast<StartupState>(startup_state); |
+} |
+ |
LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { |
+ if (BlockUntilHandlerStartedOrFailed() == StartupState::kFailed) { |
+ // If we know for certain that the handler has failed to start, then abort |
+ // here, rather than trying to signal to a handler that will never arrive, |
+ // and then sleeping unnecessarily. |
+ LOG(ERROR) << "crash server failed to launch, self-terminating"; |
+ TerminateProcess(GetCurrentProcess(), kTerminationCodeCrashNoDump); |
+ return EXCEPTION_CONTINUE_SEARCH; |
+ } |
+ |
+ // Otherwise, we know the handler startup has succeeded, and we can continue. |
+ |
// Tracks whether a thread has already entered UnhandledExceptionHandler. |
static base::subtle::AtomicWord have_crashed; |
@@ -94,7 +142,7 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { |
// signal the crash handler. |
g_crash_exception_information.thread_id = GetCurrentThreadId(); |
g_crash_exception_information.exception_pointers = |
- reinterpret_cast<crashpad::WinVMAddress>(exception_pointers); |
+ reinterpret_cast<WinVMAddress>(exception_pointers); |
// Now signal the crash server, which will take a dump and then terminate us |
// when it's complete. |
@@ -110,7 +158,7 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { |
LOG(ERROR) << "crash server did not respond, self-terminating"; |
- TerminateProcess(GetCurrentProcess(), crashpad::kTerminationCodeCrashNoDump); |
+ TerminateProcess(GetCurrentProcess(), kTerminationCodeCrashNoDump); |
return EXCEPTION_CONTINUE_SEARCH; |
} |
@@ -121,9 +169,7 @@ std::wstring FormatArgumentString(const std::string& name, |
} |
struct ScopedProcThreadAttributeListTraits { |
- static PPROC_THREAD_ATTRIBUTE_LIST InvalidValue() { |
- return nullptr; |
- } |
+ static PPROC_THREAD_ATTRIBUTE_LIST InvalidValue() { return nullptr; } |
static void Free(PPROC_THREAD_ATTRIBUTE_LIST proc_thread_attribute_list) { |
// This is able to use GET_FUNCTION_REQUIRED() instead of GET_FUNCTION() |
@@ -186,74 +232,137 @@ void AddUint64(std::vector<unsigned char>* data_vector, uint64_t data) { |
static_cast<uint32_t>((data & 0xffffffff00000000ULL) >> 32)); |
} |
-} // namespace |
- |
-namespace crashpad { |
- |
-CrashpadClient::CrashpadClient() |
- : ipc_pipe_() { |
+//! \brief Creates a randomized pipe name to listen for client registrations |
+//! on and returns its name. |
+//! |
+//! \param[out] pipe_name The pipe name that will be listened on. |
+//! \param[out] pipe_handle The first pipe instance corresponding for the pipe. |
+void CreatePipe(std::wstring* pipe_name, ScopedFileHANDLE* pipe_instance) { |
+ int tries = 5; |
+ std::string pipe_name_base = |
+ base::StringPrintf("\\\\.\\pipe\\crashpad_%d_", GetCurrentProcessId()); |
+ do { |
+ *pipe_name = base::UTF8ToUTF16(pipe_name_base + RandomString()); |
+ |
+ pipe_instance->reset(CreateNamedPipeInstance(*pipe_name, true)); |
+ |
+ // CreateNamedPipe() is documented as setting the error to |
+ // ERROR_ACCESS_DENIED if FILE_FLAG_FIRST_PIPE_INSTANCE is specified and the |
+ // pipe name is already in use. However it may set the error to other codes |
+ // such as ERROR_PIPE_BUSY (if the pipe already exists and has reached its |
+ // maximum instance count) or ERROR_INVALID_PARAMETER (if the pipe already |
+ // exists and its attributes differ from those specified to |
+ // CreateNamedPipe()). Some of these errors may be ambiguous: for example, |
+ // ERROR_INVALID_PARAMETER may also occur if CreateNamedPipe() is called |
+ // incorrectly even in the absence of an existing pipe by the same name. |
+ // Rather than chasing down all of the possible errors that might indicate |
+ // that a pipe name is already in use, retry up to a few times on any error. |
+ } while (!pipe_instance->is_valid() && --tries); |
+ |
+ PCHECK(pipe_instance->is_valid()) << "CreateNamedPipe"; |
} |
-CrashpadClient::~CrashpadClient() { |
-} |
+struct BackgroundHandlerStartThreadData { |
+ BackgroundHandlerStartThreadData( |
+ const base::FilePath& handler, |
+ const base::FilePath& database, |
+ const base::FilePath& metrics_dir, |
+ const std::string& url, |
+ const std::map<std::string, std::string>& annotations, |
+ const std::vector<std::string>& arguments, |
+ const std::wstring& ipc_pipe, |
+ ScopedFileHANDLE ipc_pipe_handle) |
+ : handler(handler), |
+ database(database), |
+ metrics_dir(metrics_dir), |
+ url(url), |
+ annotations(annotations), |
+ arguments(arguments), |
+ ipc_pipe(ipc_pipe), |
+ ipc_pipe_handle(std::move(ipc_pipe_handle)) {} |
+ |
+ base::FilePath handler; |
+ base::FilePath database; |
+ base::FilePath metrics_dir; |
+ std::string url; |
+ std::map<std::string, std::string> annotations; |
+ std::vector<std::string> arguments; |
+ std::wstring ipc_pipe; |
+ ScopedFileHANDLE ipc_pipe_handle; |
+}; |
-bool CrashpadClient::StartHandler( |
- const base::FilePath& handler, |
- const base::FilePath& database, |
- const base::FilePath& metrics_dir, |
- const std::string& url, |
- const std::map<std::string, std::string>& annotations, |
- const std::vector<std::string>& arguments, |
- bool restartable) { |
- DCHECK(ipc_pipe_.empty()); |
+// Ensures that SetHandlerStartupState() is called on scope exit. Assumes |
+// failure, and on success, SetSuccessful() should be called. |
+class ScopedCallSetHandlerStartupState { |
+ public: |
+ ScopedCallSetHandlerStartupState() : successful_(false) {} |
- HANDLE pipe_read; |
- HANDLE pipe_write; |
- SECURITY_ATTRIBUTES security_attributes = {}; |
- security_attributes.nLength = sizeof(security_attributes); |
- security_attributes.bInheritHandle = TRUE; |
- if (!CreatePipe(&pipe_read, &pipe_write, &security_attributes, 0)) { |
- PLOG(ERROR) << "CreatePipe"; |
- return false; |
+ ~ScopedCallSetHandlerStartupState() { |
+ SetHandlerStartupState(successful_ ? StartupState::kSucceeded |
+ : StartupState::kFailed); |
} |
- ScopedFileHandle pipe_read_owner(pipe_read); |
- ScopedFileHandle pipe_write_owner(pipe_write); |
- // The new process only needs the write side of the pipe. |
- BOOL rv = SetHandleInformation(pipe_read, HANDLE_FLAG_INHERIT, 0); |
- PLOG_IF(WARNING, !rv) << "SetHandleInformation"; |
+ void SetSuccessful() { successful_ = true; } |
+ |
+ private: |
+ bool successful_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ScopedCallSetHandlerStartupState); |
+}; |
+ |
+bool StartHandlerProcess( |
+ std::unique_ptr<BackgroundHandlerStartThreadData> data) { |
+ ScopedCallSetHandlerStartupState scoped_startup_state_caller; |
std::wstring command_line; |
- AppendCommandLineArgument(handler.value(), &command_line); |
- for (const std::string& argument : arguments) { |
+ AppendCommandLineArgument(data->handler.value(), &command_line); |
+ for (const std::string& argument : data->arguments) { |
AppendCommandLineArgument(base::UTF8ToUTF16(argument), &command_line); |
} |
- if (!database.value().empty()) { |
- AppendCommandLineArgument(FormatArgumentString("database", |
- database.value()), |
- &command_line); |
+ if (!data->database.value().empty()) { |
+ AppendCommandLineArgument( |
+ FormatArgumentString("database", data->database.value()), |
+ &command_line); |
} |
- if (!metrics_dir.value().empty()) { |
+ if (!data->metrics_dir.value().empty()) { |
AppendCommandLineArgument( |
- FormatArgumentString("metrics-dir", metrics_dir.value()), |
+ FormatArgumentString("metrics-dir", data->metrics_dir.value()), |
&command_line); |
} |
- if (!url.empty()) { |
- AppendCommandLineArgument(FormatArgumentString("url", |
- base::UTF8ToUTF16(url)), |
- &command_line); |
+ if (!data->url.empty()) { |
+ AppendCommandLineArgument( |
+ FormatArgumentString("url", base::UTF8ToUTF16(data->url)), |
+ &command_line); |
} |
- for (const auto& kv : annotations) { |
+ for (const auto& kv : data->annotations) { |
AppendCommandLineArgument( |
FormatArgumentString("annotation", |
base::UTF8ToUTF16(kv.first + '=' + kv.second)), |
&command_line); |
} |
+ |
+ ScopedKernelHANDLE this_process( |
+ OpenProcess(kXPProcessAllAccess, true, GetCurrentProcessId())); |
+ if (!this_process.is_valid()) { |
+ PLOG(ERROR) << "OpenProcess"; |
+ return false; |
+ } |
+ |
+ InitialClientData initial_client_data( |
+ g_signal_exception, |
+ g_signal_non_crash_dump, |
+ g_non_crash_dump_done, |
+ data->ipc_pipe_handle.get(), |
+ this_process.get(), |
+ reinterpret_cast<WinVMAddress>(&g_crash_exception_information), |
+ reinterpret_cast<WinVMAddress>(&g_non_crash_exception_information), |
+ reinterpret_cast<WinVMAddress>(&g_critical_section_with_debug_info)); |
AppendCommandLineArgument( |
- base::UTF8ToUTF16(base::StringPrintf("--handshake-handle=0x%x", |
- HandleToInt(pipe_write))), |
+ base::UTF8ToUTF16(std::string("--initial-client-data=") + |
+ initial_client_data.StringRepresentation()), |
&command_line); |
+ BOOL rv; |
DWORD creation_flags; |
STARTUPINFOEX startup_info = {}; |
startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES; |
@@ -304,8 +413,12 @@ bool CrashpadClient::StartHandler( |
} |
proc_thread_attribute_list_owner.reset(startup_info.lpAttributeList); |
- handle_list.reserve(4); |
- handle_list.push_back(pipe_write); |
+ handle_list.reserve(8); |
+ handle_list.push_back(g_signal_exception); |
+ handle_list.push_back(g_signal_non_crash_dump); |
+ handle_list.push_back(g_non_crash_dump_done); |
+ handle_list.push_back(data->ipc_pipe_handle.get()); |
+ handle_list.push_back(this_process.get()); |
AddHandleToListIfValidAndInheritable(&handle_list, |
startup_info.StartupInfo.hStdInput); |
AddHandleToListIfValidAndInheritable(&handle_list, |
@@ -327,7 +440,7 @@ bool CrashpadClient::StartHandler( |
} |
PROCESS_INFORMATION process_info; |
- rv = CreateProcess(handler.value().c_str(), |
+ rv = CreateProcess(data->handler.value().c_str(), |
&command_line[0], |
nullptr, |
nullptr, |
@@ -348,38 +461,125 @@ bool CrashpadClient::StartHandler( |
rv = CloseHandle(process_info.hProcess); |
PLOG_IF(WARNING, !rv) << "CloseHandle process"; |
- pipe_write_owner.reset(); |
- |
- uint32_t ipc_pipe_length; |
- if (!LoggingReadFile(pipe_read, &ipc_pipe_length, sizeof(ipc_pipe_length))) { |
- return false; |
- } |
+ // It is important to close our side of the pipe here before confirming that |
+ // we can communicate with the server. By doing so, the only remaining copy of |
+ // the server side of the pipe belongs to the exception handler process we |
+ // just spawned. Otherwise, the pipe will continue to exist indefinitely, so |
+ // the connection loop will not detect that it will never be serviced. |
+ data->ipc_pipe_handle.reset(); |
- ipc_pipe_.resize(ipc_pipe_length); |
- if (ipc_pipe_length && |
- !LoggingReadFile( |
- pipe_read, &ipc_pipe_[0], ipc_pipe_length * sizeof(ipc_pipe_[0]))) { |
+ // Confirm that the server is waiting for connections before continuing. |
+ ClientToServerMessage message = {}; |
+ message.type = ClientToServerMessage::kPing; |
+ ServerToClientMessage response = {}; |
+ if (!SendToCrashHandlerServer(data->ipc_pipe, message, &response)) { |
return false; |
} |
+ scoped_startup_state_caller.SetSuccessful(); |
return true; |
} |
-bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) { |
- DCHECK(ipc_pipe_.empty()); |
- DCHECK(!ipc_pipe.empty()); |
+DWORD WINAPI BackgroundHandlerStartThreadProc(void* data) { |
+ std::unique_ptr<BackgroundHandlerStartThreadData> data_as_ptr( |
+ reinterpret_cast<BackgroundHandlerStartThreadData*>(data)); |
+ return StartHandlerProcess(std::move(data_as_ptr)) ? 0 : 1; |
+} |
- ipc_pipe_ = ipc_pipe; |
+void CommonInProcessInitialization() { |
+ // We create this dummy CRITICAL_SECTION with the |
+ // RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO flag set to have an entry point |
+ // into the doubly-linked list of RTL_CRITICAL_SECTION_DEBUG objects. This |
+ // allows us to walk the list at crash time to gather data for !locks. A |
+ // debugger would instead inspect ntdll!RtlCriticalSectionList to get the head |
+ // of the list. But that is not an exported symbol, so on an arbitrary client |
+ // machine, we don't have a way of getting that pointer. |
+ InitializeCriticalSectionWithDebugInfoIfPossible( |
+ &g_critical_section_with_debug_info); |
- return true; |
+ g_non_crash_dump_lock = new base::Lock(); |
} |
-std::wstring CrashpadClient::GetHandlerIPCPipe() const { |
- DCHECK(!ipc_pipe_.empty()); |
- return ipc_pipe_; |
+} // namespace |
+ |
+CrashpadClient::CrashpadClient() : ipc_pipe_(), handler_start_thread_() {} |
+ |
+CrashpadClient::~CrashpadClient() {} |
+ |
+bool CrashpadClient::StartHandler( |
+ const base::FilePath& handler, |
+ const base::FilePath& database, |
+ const base::FilePath& metrics_dir, |
+ const std::string& url, |
+ const std::map<std::string, std::string>& annotations, |
+ const std::vector<std::string>& arguments, |
+ bool restartable, |
+ bool asynchronous_start) { |
+ DCHECK(ipc_pipe_.empty()); |
+ |
+ // Both the pipe and the signalling events have to be created on the main |
+ // thread (not the spawning thread) so that they're valid after we return from |
+ // this function. |
+ ScopedFileHANDLE ipc_pipe_handle; |
+ CreatePipe(&ipc_pipe_, &ipc_pipe_handle); |
+ |
+ SECURITY_ATTRIBUTES security_attributes = {0}; |
+ security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); |
+ security_attributes.bInheritHandle = true; |
+ |
+ g_signal_exception = |
+ CreateEvent(&security_attributes, false /* auto reset */, false, nullptr); |
+ g_signal_non_crash_dump = |
+ CreateEvent(&security_attributes, false /* auto reset */, false, nullptr); |
+ g_non_crash_dump_done = |
+ CreateEvent(&security_attributes, false /* auto reset */, false, nullptr); |
+ |
+ CommonInProcessInitialization(); |
+ |
+ SetUnhandledExceptionFilter(&UnhandledExceptionHandler); |
+ |
+ auto data = new BackgroundHandlerStartThreadData(handler, |
+ database, |
+ metrics_dir, |
+ url, |
+ annotations, |
+ arguments, |
+ ipc_pipe_, |
+ std::move(ipc_pipe_handle)); |
+ |
+ if (asynchronous_start) { |
+ // It is important that the current thread not be synchronized with the |
+ // thread that is created here. StartHandler() needs to be callable inside a |
+ // DllMain(). In that case, the background thread will not start until the |
+ // current DllMain() completes, which would cause deadlock if it was waited |
+ // upon. |
+ handler_start_thread_.reset(CreateThread(nullptr, |
+ 0, |
+ &BackgroundHandlerStartThreadProc, |
+ reinterpret_cast<void*>(data), |
+ 0, |
+ nullptr)); |
+ if (!handler_start_thread_.is_valid()) { |
+ PLOG(ERROR) << "CreateThread"; |
+ SetHandlerStartupState(StartupState::kFailed); |
+ return false; |
+ } |
+ |
+ // In asynchronous mode, we can't report on the overall success or failure |
+ // of initialization at this point. |
+ return true; |
+ } else { |
+ return StartHandlerProcess( |
+ std::unique_ptr<BackgroundHandlerStartThreadData>(data)); |
+ } |
} |
-bool CrashpadClient::UseHandler() { |
+bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) { |
+ DCHECK(ipc_pipe_.empty()); |
+ DCHECK(!ipc_pipe.empty()); |
+ |
+ ipc_pipe_ = ipc_pipe; |
+ |
DCHECK(!ipc_pipe_.empty()); |
DCHECK_EQ(g_signal_exception, INVALID_HANDLE_VALUE); |
DCHECK_EQ(g_signal_non_crash_dump, INVALID_HANDLE_VALUE); |
@@ -397,18 +597,10 @@ bool CrashpadClient::UseHandler() { |
message.registration.non_crash_exception_information = |
reinterpret_cast<WinVMAddress>(&g_non_crash_exception_information); |
- // We create this dummy CRITICAL_SECTION with the |
- // RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO flag set to have an entry point |
- // into the doubly-linked list of RTL_CRITICAL_SECTION_DEBUG objects. This |
- // allows us to walk the list at crash time to gather data for !locks. A |
- // debugger would instead inspect ntdll!RtlCriticalSectionList to get the head |
- // of the list. But that is not an exported symbol, so on an arbitrary client |
- // machine, we don't have a way of getting that pointer. |
- if (InitializeCriticalSectionWithDebugInfoIfPossible( |
- &g_critical_section_with_debug_info)) { |
- message.registration.critical_section_address = |
- reinterpret_cast<WinVMAddress>(&g_critical_section_with_debug_info); |
- } |
+ CommonInProcessInitialization(); |
+ |
+ message.registration.critical_section_address = |
+ reinterpret_cast<WinVMAddress>(&g_critical_section_with_debug_info); |
ServerToClientMessage response = {}; |
@@ -416,6 +608,9 @@ bool CrashpadClient::UseHandler() { |
return false; |
} |
+ SetHandlerStartupState(StartupState::kSucceeded); |
+ SetUnhandledExceptionFilter(&UnhandledExceptionHandler); |
+ |
// The server returns these already duplicated to be valid in this process. |
g_signal_exception = |
IntToHandle(response.registration.request_crash_dump_event); |
@@ -424,19 +619,45 @@ bool CrashpadClient::UseHandler() { |
g_non_crash_dump_done = |
IntToHandle(response.registration.non_crash_dump_completed_event); |
- g_non_crash_dump_lock = new base::Lock(); |
- |
- // In theory we could store the previous handler but it is not clear what |
- // use we have for it. |
- SetUnhandledExceptionFilter(&UnhandledExceptionHandler); |
return true; |
} |
+std::wstring CrashpadClient::GetHandlerIPCPipe() const { |
+ DCHECK(!ipc_pipe_.empty()); |
+ return ipc_pipe_; |
+} |
+ |
+bool CrashpadClient::WaitForHandlerStart() { |
+ DCHECK(handler_start_thread_.is_valid()); |
+ if (WaitForSingleObject(handler_start_thread_.get(), INFINITE) != |
+ WAIT_OBJECT_0) { |
+ PLOG(ERROR) << "WaitForSingleObject"; |
+ return false; |
+ } |
+ |
+ DWORD exit_code; |
+ if (!GetExitCodeThread(handler_start_thread_.get(), &exit_code)) { |
+ PLOG(ERROR) << "GetExitCodeThread"; |
+ return false; |
+ } |
+ |
+ handler_start_thread_.reset(); |
+ return exit_code == 0; |
+} |
+ |
// 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 UseHandler()"; |
+ LOG(ERROR) << "not connected"; |
+ return; |
+ } |
+ |
+ if (BlockUntilHandlerStartedOrFailed() == StartupState::kFailed) { |
+ // If we know for certain that the handler has failed to start, then abort |
+ // here, as we would otherwise wait indefinitely for the |
+ // g_non_crash_dump_done event that would never be signalled. |
+ LOG(ERROR) << "crash server failed to launch, no dump captured"; |
return; |
} |
@@ -475,7 +696,7 @@ void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) { |
g_non_crash_exception_information.thread_id = GetCurrentThreadId(); |
g_non_crash_exception_information.exception_pointers = |
- reinterpret_cast<crashpad::WinVMAddress>(&exception_pointers); |
+ reinterpret_cast<WinVMAddress>(&exception_pointers); |
bool set_event_result = !!SetEvent(g_signal_non_crash_dump); |
PLOG_IF(ERROR, !set_event_result) << "SetEvent"; |
@@ -487,11 +708,15 @@ void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) { |
// static |
void CrashpadClient::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) { |
if (g_signal_exception == INVALID_HANDLE_VALUE) { |
- LOG(ERROR) << "haven't called UseHandler(), no dump captured"; |
- TerminateProcess(GetCurrentProcess(), kTerminationCodeUseHandlerNotCalled); |
+ LOG(ERROR) << "not connected"; |
+ TerminateProcess(GetCurrentProcess(), |
+ kTerminationCodeNotConnectedToHandler); |
return; |
} |
+ // We don't need to check for handler startup here, as |
+ // UnhandledExceptionHandler() necessarily does that. |
+ |
UnhandledExceptionHandler(exception_pointers); |
} |