summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.cc')
-rw-r--r--3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.cc401
1 files changed, 401 insertions, 0 deletions
diff --git a/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.cc b/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.cc
new file mode 100644
index 0000000..b0d3d04
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/windows/crash_generation/crash_generation_client.cc
@@ -0,0 +1,401 @@
+// Copyright (c) 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "client/windows/crash_generation/crash_generation_client.h"
+#include <cassert>
+#include <utility>
+#include "client/windows/common/ipc_protocol.h"
+
+namespace google_breakpad {
+
+const int kPipeBusyWaitTimeoutMs = 2000;
+
+#ifdef _DEBUG
+const DWORD kWaitForServerTimeoutMs = INFINITE;
+#else
+const DWORD kWaitForServerTimeoutMs = 15000;
+#endif
+
+const int kPipeConnectMaxAttempts = 2;
+
+const DWORD kPipeDesiredAccess = FILE_READ_DATA |
+ FILE_WRITE_DATA |
+ FILE_WRITE_ATTRIBUTES;
+
+const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
+ SECURITY_SQOS_PRESENT;
+
+const DWORD kPipeMode = PIPE_READMODE_MESSAGE;
+
+const size_t kWaitEventCount = 2;
+
+// This function is orphan for production code. It can be used
+// for debugging to help repro some scenarios like the client
+// is slow in writing to the pipe after connecting, the client
+// is slow in reading from the pipe after writing, etc. The parameter
+// overlapped below is not used and it is present to match the signature
+// of this function to TransactNamedPipe Win32 API. Uncomment if needed
+// for debugging.
+/**
+static bool TransactNamedPipeDebugHelper(HANDLE pipe,
+ const void* in_buffer,
+ DWORD in_size,
+ void* out_buffer,
+ DWORD out_size,
+ DWORD* bytes_count,
+ LPOVERLAPPED) {
+ // Uncomment the next sleep to create a gap before writing
+ // to pipe.
+ // Sleep(5000);
+
+ if (!WriteFile(pipe,
+ in_buffer,
+ in_size,
+ bytes_count,
+ NULL)) {
+ return false;
+ }
+
+ // Uncomment the next sleep to create a gap between write
+ // and read.
+ // Sleep(5000);
+
+ return ReadFile(pipe, out_buffer, out_size, bytes_count, NULL) != FALSE;
+}
+**/
+
+CrashGenerationClient::CrashGenerationClient(
+ const wchar_t* pipe_name,
+ MINIDUMP_TYPE dump_type,
+ const CustomClientInfo* custom_info)
+ : pipe_name_(pipe_name),
+ pipe_handle_(NULL),
+ dump_type_(dump_type),
+ thread_id_(0),
+ server_process_id_(0),
+ crash_event_(NULL),
+ crash_generated_(NULL),
+ server_alive_(NULL),
+ exception_pointers_(NULL),
+ custom_info_() {
+ memset(&assert_info_, 0, sizeof(assert_info_));
+ if (custom_info) {
+ custom_info_ = *custom_info;
+ }
+}
+
+CrashGenerationClient::CrashGenerationClient(
+ HANDLE pipe_handle,
+ MINIDUMP_TYPE dump_type,
+ const CustomClientInfo* custom_info)
+ : pipe_name_(),
+ pipe_handle_(pipe_handle),
+ dump_type_(dump_type),
+ thread_id_(0),
+ server_process_id_(0),
+ crash_event_(NULL),
+ crash_generated_(NULL),
+ server_alive_(NULL),
+ exception_pointers_(NULL),
+ custom_info_() {
+ memset(&assert_info_, 0, sizeof(assert_info_));
+ if (custom_info) {
+ custom_info_ = *custom_info;
+ }
+}
+
+CrashGenerationClient::~CrashGenerationClient() {
+ if (crash_event_) {
+ CloseHandle(crash_event_);
+ }
+
+ if (crash_generated_) {
+ CloseHandle(crash_generated_);
+ }
+
+ if (server_alive_) {
+ CloseHandle(server_alive_);
+ }
+}
+
+// Performs the registration step with the server process.
+// The registration step involves communicating with the server
+// via a named pipe. The client sends the following pieces of
+// data to the server:
+//
+// * Message tag indicating the client is requesting registration.
+// * Process id of the client process.
+// * Address of a DWORD variable in the client address space
+// that will contain the thread id of the client thread that
+// caused the crash.
+// * Address of a EXCEPTION_POINTERS* variable in the client
+// address space that will point to an instance of EXCEPTION_POINTERS
+// when the crash happens.
+// * Address of an instance of MDRawAssertionInfo that will contain
+// relevant information in case of non-exception crashes like assertion
+// failures and pure calls.
+//
+// In return the client expects the following information from the server:
+//
+// * Message tag indicating successful registration.
+// * Server process id.
+// * Handle to an object that client can signal to request dump
+// generation from the server.
+// * Handle to an object that client can wait on after requesting
+// dump generation for the server to finish dump generation.
+// * Handle to a mutex object that client can wait on to make sure
+// server is still alive.
+//
+// If any step of the expected behavior mentioned above fails, the
+// registration step is not considered successful and hence out-of-process
+// dump generation service is not available.
+//
+// Returns true if the registration is successful; false otherwise.
+bool CrashGenerationClient::Register() {
+ HANDLE pipe = ConnectToServer();
+ if (!pipe) {
+ return false;
+ }
+
+ bool success = RegisterClient(pipe);
+ CloseHandle(pipe);
+ return success;
+}
+
+bool CrashGenerationClient::RequestUpload(DWORD crash_id) {
+ HANDLE pipe = ConnectToServer();
+ if (!pipe) {
+ return false;
+ }
+
+ CustomClientInfo custom_info = {NULL, 0};
+ ProtocolMessage msg(MESSAGE_TAG_UPLOAD_REQUEST, crash_id,
+ static_cast<MINIDUMP_TYPE>(NULL), NULL, NULL, NULL,
+ custom_info, NULL, NULL, NULL);
+ DWORD bytes_count = 0;
+ bool success = WriteFile(pipe, &msg, sizeof(msg), &bytes_count, NULL) != 0;
+
+ CloseHandle(pipe);
+ return success;
+}
+
+HANDLE CrashGenerationClient::ConnectToServer() {
+ HANDLE pipe = ConnectToPipe(pipe_name_.c_str(),
+ kPipeDesiredAccess,
+ kPipeFlagsAndAttributes);
+ if (!pipe) {
+ return NULL;
+ }
+
+ DWORD mode = kPipeMode;
+ if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) {
+ CloseHandle(pipe);
+ pipe = NULL;
+ }
+
+ return pipe;
+}
+
+bool CrashGenerationClient::RegisterClient(HANDLE pipe) {
+ ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST,
+ GetCurrentProcessId(),
+ dump_type_,
+ &thread_id_,
+ &exception_pointers_,
+ &assert_info_,
+ custom_info_,
+ NULL,
+ NULL,
+ NULL);
+ ProtocolMessage reply;
+ DWORD bytes_count = 0;
+ // The call to TransactNamedPipe below can be changed to a call
+ // to TransactNamedPipeDebugHelper to help repro some scenarios.
+ // For details see comments for TransactNamedPipeDebugHelper.
+ if (!TransactNamedPipe(pipe,
+ &msg,
+ sizeof(msg),
+ &reply,
+ sizeof(ProtocolMessage),
+ &bytes_count,
+ NULL)) {
+ return false;
+ }
+
+ if (!ValidateResponse(reply)) {
+ return false;
+ }
+
+ ProtocolMessage ack_msg;
+ ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK;
+
+ if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) {
+ return false;
+ }
+ crash_event_ = reply.dump_request_handle;
+ crash_generated_ = reply.dump_generated_handle;
+ server_alive_ = reply.server_alive_handle;
+ server_process_id_ = reply.id;
+
+ return true;
+}
+
+HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name,
+ DWORD pipe_access,
+ DWORD flags_attrs) {
+ if (pipe_handle_) {
+ HANDLE t = pipe_handle_;
+ pipe_handle_ = NULL;
+ return t;
+ }
+
+ for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
+ HANDLE pipe = CreateFile(pipe_name,
+ pipe_access,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ flags_attrs,
+ NULL);
+ if (pipe != INVALID_HANDLE_VALUE) {
+ return pipe;
+ }
+
+ // Cannot continue retrying if error is something other than
+ // ERROR_PIPE_BUSY.
+ if (GetLastError() != ERROR_PIPE_BUSY) {
+ break;
+ }
+
+ // Cannot continue retrying if wait on pipe fails.
+ if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) {
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+bool CrashGenerationClient::ValidateResponse(
+ const ProtocolMessage& msg) const {
+ return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) &&
+ (msg.id != 0) &&
+ (msg.dump_request_handle != NULL) &&
+ (msg.dump_generated_handle != NULL) &&
+ (msg.server_alive_handle != NULL);
+}
+
+bool CrashGenerationClient::IsRegistered() const {
+ return crash_event_ != NULL;
+}
+
+bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info,
+ MDRawAssertionInfo* assert_info) {
+ if (!IsRegistered()) {
+ return false;
+ }
+
+ exception_pointers_ = ex_info;
+ thread_id_ = GetCurrentThreadId();
+
+ if (assert_info) {
+ memcpy(&assert_info_, assert_info, sizeof(assert_info_));
+ } else {
+ memset(&assert_info_, 0, sizeof(assert_info_));
+ }
+
+ return SignalCrashEventAndWait();
+}
+
+bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
+ return RequestDump(ex_info, NULL);
+}
+
+bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
+ return RequestDump(NULL, assert_info);
+}
+
+bool CrashGenerationClient::SignalCrashEventAndWait() {
+ assert(crash_event_);
+ assert(crash_generated_);
+ assert(server_alive_);
+
+ // Reset the dump generated event before signaling the crash
+ // event so that the server can set the dump generated event
+ // once it is done generating the event.
+ if (!ResetEvent(crash_generated_)) {
+ return false;
+ }
+
+ if (!SetEvent(crash_event_)) {
+ return false;
+ }
+
+ HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_};
+
+ DWORD result = WaitForMultipleObjects(kWaitEventCount,
+ wait_handles,
+ FALSE,
+ kWaitForServerTimeoutMs);
+
+ // Crash dump was successfully generated only if the server
+ // signaled the crash generated event.
+ return result == WAIT_OBJECT_0;
+}
+
+HANDLE CrashGenerationClient::DuplicatePipeToClientProcess(const wchar_t* pipe_name,
+ HANDLE hProcess) {
+ for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
+ HANDLE local_pipe = CreateFile(pipe_name, kPipeDesiredAccess,
+ 0, NULL, OPEN_EXISTING,
+ kPipeFlagsAndAttributes, NULL);
+ if (local_pipe != INVALID_HANDLE_VALUE) {
+ HANDLE remotePipe = INVALID_HANDLE_VALUE;
+ if (DuplicateHandle(GetCurrentProcess(), local_pipe,
+ hProcess, &remotePipe, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return remotePipe;
+ } else {
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+
+ // Cannot continue retrying if the error wasn't a busy pipe.
+ if (GetLastError() != ERROR_PIPE_BUSY) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) {
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+} // namespace google_breakpad