aboutsummaryrefslogtreecommitdiff
path: root/src/client/windows/crash_generation/crash_generation_server.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/windows/crash_generation/crash_generation_server.cc')
-rw-r--r--src/client/windows/crash_generation/crash_generation_server.cc821
1 files changed, 821 insertions, 0 deletions
diff --git a/src/client/windows/crash_generation/crash_generation_server.cc b/src/client/windows/crash_generation/crash_generation_server.cc
new file mode 100644
index 00000000..dbdf0014
--- /dev/null
+++ b/src/client/windows/crash_generation/crash_generation_server.cc
@@ -0,0 +1,821 @@
+// 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_server.h"
+#include <Windows.h>
+#include <cassert>
+#include "client/windows/common/auto_critical_section.h"
+#include "processor/scoped_ptr.h"
+
+namespace google_breakpad {
+
+// Output buffer size.
+const size_t kOutBufferSize = 64;
+
+// Input buffer size.
+const size_t kInBufferSize = 64;
+
+// Access flags for the client on the dump request event.
+const DWORD kDumpRequestEventAccess = EVENT_MODIFY_STATE;
+
+// Access flags for the client on the dump generated event.
+const DWORD kDumpGeneratedEventAccess = EVENT_MODIFY_STATE |
+ SYNCHRONIZE;
+
+// Access flags for the client on the mutex.
+const DWORD kMutexAccess = SYNCHRONIZE;
+
+// Attribute flags for the pipe.
+const DWORD kPipeAttr = FILE_FLAG_FIRST_PIPE_INSTANCE |
+ PIPE_ACCESS_DUPLEX |
+ FILE_FLAG_OVERLAPPED;
+
+// Mode for the pipe.
+const DWORD kPipeMode = PIPE_TYPE_MESSAGE |
+ PIPE_READMODE_MESSAGE |
+ PIPE_WAIT;
+
+// For pipe I/O, execute the callback in the wait thread itself,
+// since the callback does very little work. The callback executes
+// the code for one of the states of the server state machine and
+// the code for all of the states perform async I/O and hence
+// finish very quickly.
+const ULONG kPipeIOThreadFlags = WT_EXECUTEINWAITTHREAD;
+
+// Dump request threads will, most likely, generate dumps. That may
+// take some time to finish, so specify WT_EXECUTELONGFUNCTION flag.
+const ULONG kDumpRequestThreadFlags = WT_EXECUTEINWAITTHREAD |
+ WT_EXECUTELONGFUNCTION;
+
+// Maximum delay during server shutdown if some work items
+// are still executing.
+const int kShutdownDelayMs = 10000;
+
+// Interval for each sleep during server shutdown.
+const int kShutdownSleepIntervalMs = 5;
+
+static bool ValidateClientRequest(const ProtocolMessage& msg) {
+ return msg.tag == MESSAGE_TAG_REGISTRATION_REQUEST &&
+ msg.pid != 0 &&
+ msg.thread_id != NULL &&
+ msg.exception_pointers != NULL &&
+ msg.assert_info != NULL;
+}
+
+CrashGenerationServer::CrashGenerationServer(
+ const wchar_t* pipe_name,
+ OnClientConnectedCallback connect_callback,
+ void* connect_context,
+ OnClientDumpRequestCallback dump_callback,
+ void* dump_context,
+ OnClientExitedCallback exit_callback,
+ void* exit_context,
+ bool generate_dumps,
+ const std::wstring* dump_path)
+ : pipe_name_(pipe_name),
+ pipe_(NULL),
+ pipe_wait_handle_(NULL),
+ server_alive_handle_(NULL),
+ connect_callback_(connect_callback),
+ connect_context_(connect_context),
+ dump_callback_(dump_callback),
+ dump_context_(dump_context),
+ exit_callback_(exit_callback),
+ exit_context_(exit_context),
+ generate_dumps_(generate_dumps),
+ dump_generator_(NULL),
+ server_state_(IPC_SERVER_STATE_INITIAL),
+ shutting_down_(false),
+ overlapped_(),
+ client_info_(NULL),
+ cleanup_item_count_(0) {
+ InitializeCriticalSection(&clients_sync_);
+
+ if (dump_path) {
+ dump_generator_.reset(new MinidumpGenerator(*dump_path));
+ }
+}
+
+CrashGenerationServer::~CrashGenerationServer() {
+ // Indicate to existing threads that server is shutting down.
+ shutting_down_ = true;
+
+ // Unregister wait on the pipe.
+ if (pipe_wait_handle_) {
+ // Wait for already executing callbacks to finish.
+ UnregisterWaitEx(pipe_wait_handle_, INVALID_HANDLE_VALUE);
+ }
+
+ // Close the pipe to avoid further client connections.
+ if (pipe_) {
+ CloseHandle(pipe_);
+ }
+
+ // Request all ClientInfo objects to unregister all waits.
+ // New scope to hold the lock for the shortest time.
+ {
+ AutoCriticalSection lock(&clients_sync_);
+
+ std::list<ClientInfo*>::iterator iter;
+ for (iter = clients_.begin(); iter != clients_.end(); ++iter) {
+ ClientInfo* client_info = *iter;
+ client_info->UnregisterWaits();
+ }
+ }
+
+ // Now that all waits have been unregistered, wait for some time
+ // for all pending work items to finish.
+ int total_wait = 0;
+ while (cleanup_item_count_ > 0) {
+ Sleep(kShutdownSleepIntervalMs);
+
+ total_wait += kShutdownSleepIntervalMs;
+
+ if (total_wait >= kShutdownDelayMs) {
+ break;
+ }
+ }
+
+ // Clean up all the ClientInfo objects.
+ // New scope to hold the lock for the shortest time.
+ {
+ AutoCriticalSection lock(&clients_sync_);
+
+ std::list<ClientInfo*>::iterator iter;
+ for (iter = clients_.begin(); iter != clients_.end(); ++iter) {
+ ClientInfo* client_info = *iter;
+ delete client_info;
+ }
+ }
+
+ if (server_alive_handle_) {
+ // Release the mutex before closing the handle so that clients requesting
+ // dumps wait for a long time for the server to generate a dump.
+ ReleaseMutex(server_alive_handle_);
+ CloseHandle(server_alive_handle_);
+ }
+
+ DeleteCriticalSection(&clients_sync_);
+}
+
+bool CrashGenerationServer::Start() {
+ server_state_ = IPC_SERVER_STATE_INITIAL;
+
+ server_alive_handle_ = CreateMutex(NULL, TRUE, NULL);
+ if (!server_alive_handle_) {
+ return false;
+ }
+
+ // Event to signal the client connection and pipe reads and writes.
+ overlapped_.hEvent = CreateEvent(NULL, // Security descriptor.
+ TRUE, // Manual reset.
+ FALSE, // Initially signaled.
+ NULL); // Name.
+ if (!overlapped_.hEvent) {
+ return false;
+ }
+
+ // Register a callback with the thread pool for the client connection.
+ RegisterWaitForSingleObject(&pipe_wait_handle_,
+ overlapped_.hEvent,
+ OnPipeConnected,
+ this,
+ INFINITE,
+ kPipeIOThreadFlags);
+
+ pipe_ = CreateNamedPipe(pipe_name_.c_str(),
+ kPipeAttr,
+ kPipeMode,
+ 1,
+ kOutBufferSize,
+ kInBufferSize,
+ 0,
+ NULL);
+ if (!pipe_) {
+ return false;
+ }
+
+ // Signal the event to start a separate thread to handle
+ // client connections.
+ return SetEvent(overlapped_.hEvent) != FALSE;
+}
+
+// If the server thread serving clients ever gets into the
+// ERROR state, reset the event, close the pipe and remain
+// in the error state forever. Error state means something
+// that we didn't account for has happened, and it's dangerous
+// to do anything unknowingly.
+void CrashGenerationServer::HandleErrorState() {
+ assert(server_state_ == IPC_SERVER_STATE_ERROR);
+
+ // If the server is shutting down anyway, don't clean up
+ // here since shut down process will clean up.
+ if (shutting_down_) {
+ return;
+ }
+
+ if (pipe_wait_handle_) {
+ UnregisterWait(pipe_wait_handle_);
+ pipe_wait_handle_ = NULL;
+ }
+
+ if (pipe_) {
+ CloseHandle(pipe_);
+ pipe_ = NULL;
+ }
+
+ if (overlapped_.hEvent) {
+ CloseHandle(overlapped_.hEvent);
+ overlapped_.hEvent = NULL;
+ }
+}
+
+// When the server thread serving clients is in the INITIAL state,
+// try to connect to the pipe asynchronously. If the connection
+// finishes synchronously, directly go into the CONNECTED state;
+// otherwise go into the CONNECTING state. For any problems, go
+// into the ERROR state.
+void CrashGenerationServer::HandleInitialState() {
+ assert(server_state_ == IPC_SERVER_STATE_INITIAL);
+
+ if (!ResetEvent(overlapped_.hEvent)) {
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ return;
+ }
+
+ bool success = ConnectNamedPipe(pipe_, &overlapped_) != FALSE;
+
+ // From MSDN, it is not clear that when ConnectNamedPipe is used
+ // in an overlapped mode, will it ever return non-zero value, and
+ // if so, in what cases.
+ assert(!success);
+
+ DWORD error_code = GetLastError();
+ switch (error_code) {
+ case ERROR_IO_PENDING:
+ server_state_ = IPC_SERVER_STATE_CONNECTING;
+ break;
+
+ case ERROR_PIPE_CONNECTED:
+ if (SetEvent(overlapped_.hEvent)) {
+ server_state_ = IPC_SERVER_STATE_CONNECTED;
+ } else {
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ }
+ break;
+
+ default:
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ break;
+ }
+}
+
+// When the server thread serving the clients is in the CONNECTING state,
+// try to get the result of the asynchronous connection request using
+// the OVERLAPPED object. If the result indicates the connection is done,
+// go into the CONNECTED state. If the result indicates I/O is still
+// INCOMPLETE, remain in the CONNECTING state. For any problems,
+// go into the DISCONNECTING state.
+void CrashGenerationServer::HandleConnectingState() {
+ assert(server_state_ == IPC_SERVER_STATE_CONNECTING);
+
+ DWORD bytes_count = 0;
+ bool success = GetOverlappedResult(pipe_,
+ &overlapped_,
+ &bytes_count,
+ FALSE) != FALSE;
+
+ if (success) {
+ server_state_ = IPC_SERVER_STATE_CONNECTED;
+ return;
+ }
+
+ if (GetLastError() != ERROR_IO_INCOMPLETE) {
+ server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+ }
+}
+
+// When the server thread serving the clients is in the CONNECTED state,
+// try to issue an asynchronous read from the pipe. If read completes
+// synchronously or if I/O is pending then go into the READING state.
+// For any problems, go into the DISCONNECTING state.
+void CrashGenerationServer::HandleConnectedState() {
+ assert(server_state_ == IPC_SERVER_STATE_CONNECTED);
+
+ DWORD bytes_count = 0;
+ memset(&msg_, 0, sizeof(msg_));
+ bool success = ReadFile(pipe_,
+ &msg_,
+ sizeof(msg_),
+ &bytes_count,
+ &overlapped_) != FALSE;
+
+ // Note that the asynchronous read issued above can finish before the
+ // code below executes. But, it is okay to change state after issuing
+ // the asynchronous read. This is because even if the asynchronous read
+ // is done, the callback for it would not be executed until the current
+ // thread finishes its execution.
+ if (success || GetLastError() == ERROR_IO_PENDING) {
+ server_state_ = IPC_SERVER_STATE_READING;
+ } else {
+ server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+ }
+}
+
+// When the server thread serving the clients is in the READING state,
+// try to get the result of the async read. If async read is done,
+// go into the READ_DONE state. For any problems, go into the
+// DISCONNECTING state.
+void CrashGenerationServer::HandleReadingState() {
+ assert(server_state_ == IPC_SERVER_STATE_READING);
+
+ DWORD bytes_count = 0;
+ bool success = GetOverlappedResult(pipe_,
+ &overlapped_,
+ &bytes_count,
+ FALSE) != FALSE;
+
+ if (success && bytes_count == sizeof(ProtocolMessage)){
+ server_state_ = IPC_SERVER_STATE_READ_DONE;
+ return;
+ }
+
+ DWORD error_code = GetLastError();
+
+ // We should never get an I/O incomplete since we should not execute this
+ // unless the Read has finished and the overlapped event is signaled. If
+ // we do get INCOMPLETE, we have a bug in our code.
+ assert(error_code != ERROR_IO_INCOMPLETE);
+
+ server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+}
+
+// When the server thread serving the client is in the READ_DONE state,
+// validate the client's request message, register the client by
+// creating appropriate objects and prepare the response. Then try to
+// write the response to the pipe asynchronously. If that succeeds,
+// go into the WRITING state. For any problems, go into the DISCONNECTING
+// state.
+void CrashGenerationServer::HandleReadDoneState() {
+ assert(server_state_ == IPC_SERVER_STATE_READ_DONE);
+
+ if (!ValidateClientRequest(msg_)) {
+ server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+ return;
+ }
+
+ scoped_ptr<ClientInfo> client_info(
+ new ClientInfo(this,
+ msg_.pid,
+ msg_.dump_type,
+ msg_.thread_id,
+ msg_.exception_pointers,
+ msg_.assert_info));
+
+ if (!client_info->Initialize()) {
+ server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+ return;
+ }
+
+ if (!RespondToClient(client_info.get())) {
+ server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+ return;
+ }
+
+ // Note that the asynchronous write issued by RespondToClient function
+ // can finish before the code below executes. But it is okay to change
+ // state after issuing the asynchronous write. This is because even if
+ // the asynchronous write is done, the callback for it would not be
+ // executed until the current thread finishes its execution.
+ server_state_ = IPC_SERVER_STATE_WRITING;
+ client_info_ = client_info.release();
+}
+
+// When the server thread serving the clients is in the WRITING state,
+// try to get the result of the async write. If the async write is done,
+// go into the WRITE_DONE state. For any problems, go into the
+// DISONNECTING state.
+void CrashGenerationServer::HandleWritingState() {
+ assert(server_state_ == IPC_SERVER_STATE_WRITING);
+
+ DWORD bytes_count = 0;
+ bool success = GetOverlappedResult(pipe_,
+ &overlapped_,
+ &bytes_count,
+ FALSE) != FALSE;
+
+ if (success) {
+ server_state_ = IPC_SERVER_STATE_WRITE_DONE;
+ return;
+ }
+
+ DWORD error_code = GetLastError();
+
+ // We should never get an I/O incomplete since we should not execute this
+ // unless the Write has finished and the overlapped event is signaled. If
+ // we do get INCOMPLETE, we have a bug in our code.
+ assert(error_code != ERROR_IO_INCOMPLETE);
+
+ server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+}
+
+// When the server thread serving the clients is in the WRITE_DONE state,
+// try to issue an async read on the pipe. If the read completes synchronously
+// or if I/O is still pending then go into the READING_ACK state. For any
+// issues, go into the DISCONNECTING state.
+void CrashGenerationServer::HandleWriteDoneState() {
+ assert(server_state_ == IPC_SERVER_STATE_WRITE_DONE);
+
+ server_state_ = IPC_SERVER_STATE_READING_ACK;
+
+ DWORD bytes_count = 0;
+ bool success = ReadFile(pipe_,
+ &msg_,
+ sizeof(msg_),
+ &bytes_count,
+ &overlapped_) != FALSE;
+
+ if (success) {
+ return;
+ }
+
+ DWORD error_code = GetLastError();
+
+ if (error_code != ERROR_IO_PENDING) {
+ server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+ }
+}
+
+// When the server thread serving the clients is in the READING_ACK state,
+// try to get result of async read. Go into the DISCONNECTING state.
+void CrashGenerationServer::HandleReadingAckState() {
+ assert(server_state_ == IPC_SERVER_STATE_READING_ACK);
+
+ DWORD bytes_count = 0;
+ bool success = GetOverlappedResult(pipe_,
+ &overlapped_,
+ &bytes_count,
+ FALSE) != FALSE;
+
+ if (success) {
+ // The connection handshake with the client is now complete; perform
+ // the callback.
+ if (connect_callback_) {
+ connect_callback_(connect_context_, client_info_);
+ }
+ } else {
+ DWORD error_code = GetLastError();
+
+ // We should never get an I/O incomplete since we should not execute this
+ // unless the Read has finished and the overlapped event is signaled. If
+ // we do get INCOMPLETE, we have a bug in our code.
+ assert(error_code != ERROR_IO_INCOMPLETE);
+ }
+
+ server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+}
+
+// When the server thread serving the client is in the DISCONNECTING state,
+// disconnect from the pipe and reset the event. If anything fails, go into
+// the ERROR state. If it goes well, go into the INITIAL state and set the
+// event to start all over again.
+void CrashGenerationServer::HandleDisconnectingState() {
+ assert(server_state_ == IPC_SERVER_STATE_DISCONNECTING);
+
+ // Done serving the client.
+ client_info_ = NULL;
+
+ overlapped_.Internal = NULL;
+ overlapped_.InternalHigh = NULL;
+ overlapped_.Offset = 0;
+ overlapped_.OffsetHigh = 0;
+ overlapped_.Pointer = NULL;
+
+ if (!ResetEvent(overlapped_.hEvent)) {
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ return;
+ }
+
+ if (!DisconnectNamedPipe(pipe_)) {
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ return;
+ }
+
+ // If the server is shutting down do not connect to the
+ // next client.
+ if (shutting_down_) {
+ return;
+ }
+
+ server_state_ = IPC_SERVER_STATE_INITIAL;
+ if (!SetEvent(overlapped_.hEvent)) {
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ }
+}
+
+bool CrashGenerationServer::PrepareReply(const ClientInfo& client_info,
+ ProtocolMessage* reply) const {
+ reply->tag = MESSAGE_TAG_REGISTRATION_RESPONSE;
+ reply->pid = GetCurrentProcessId();
+
+ if (CreateClientHandles(client_info, reply)) {
+ return true;
+ }
+
+ if (reply->dump_request_handle) {
+ CloseHandle(reply->dump_request_handle);
+ }
+
+ if (reply->dump_generated_handle) {
+ CloseHandle(reply->dump_generated_handle);
+ }
+
+ if (reply->server_alive_handle) {
+ CloseHandle(reply->server_alive_handle);
+ }
+
+ return false;
+}
+
+bool CrashGenerationServer::CreateClientHandles(const ClientInfo& client_info,
+ ProtocolMessage* reply) const {
+ HANDLE current_process = GetCurrentProcess();
+ if (!DuplicateHandle(current_process,
+ client_info.dump_requested_handle(),
+ client_info.process_handle(),
+ &reply->dump_request_handle,
+ kDumpRequestEventAccess,
+ FALSE,
+ 0)) {
+ return false;
+ }
+
+ if (!DuplicateHandle(current_process,
+ client_info.dump_generated_handle(),
+ client_info.process_handle(),
+ &reply->dump_generated_handle,
+ kDumpGeneratedEventAccess,
+ FALSE,
+ 0)) {
+ return false;
+ }
+
+ if (!DuplicateHandle(current_process,
+ server_alive_handle_,
+ client_info.process_handle(),
+ &reply->server_alive_handle,
+ kMutexAccess,
+ FALSE,
+ 0)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool CrashGenerationServer::RespondToClient(ClientInfo* client_info) {
+ ProtocolMessage reply;
+ if (!PrepareReply(*client_info, &reply)) {
+ return false;
+ }
+
+ if (!AddClient(client_info)) {
+ return false;
+ }
+
+ DWORD bytes_count = 0;
+ bool success = WriteFile(pipe_,
+ &reply,
+ sizeof(reply),
+ &bytes_count,
+ &overlapped_) != FALSE;
+
+ return success || GetLastError() == ERROR_IO_PENDING;
+}
+
+// The server thread servicing the clients runs this method. The method
+// implements the state machine described in ReadMe.txt along with the
+// helper methods HandleXXXState.
+void CrashGenerationServer::HandleConnectionRequest() {
+ switch (server_state_) {
+ case IPC_SERVER_STATE_ERROR:
+ HandleErrorState();
+ break;
+
+ case IPC_SERVER_STATE_INITIAL:
+ HandleInitialState();
+ break;
+
+ case IPC_SERVER_STATE_CONNECTING:
+ HandleConnectingState();
+ break;
+
+ case IPC_SERVER_STATE_CONNECTED:
+ HandleConnectedState();
+ break;
+
+ case IPC_SERVER_STATE_READING:
+ HandleReadingState();
+ break;
+
+ case IPC_SERVER_STATE_READ_DONE:
+ HandleReadDoneState();
+ break;
+
+ case IPC_SERVER_STATE_WRITING:
+ HandleWritingState();
+ break;
+
+ case IPC_SERVER_STATE_WRITE_DONE:
+ HandleWriteDoneState();
+ break;
+
+ case IPC_SERVER_STATE_READING_ACK:
+ HandleReadingAckState();
+ break;
+
+ case IPC_SERVER_STATE_DISCONNECTING:
+ HandleDisconnectingState();
+ break;
+
+ default:
+ assert(false);
+ // This indicates that we added one more state without
+ // adding handling code.
+ server_state_ = IPC_SERVER_STATE_ERROR;
+ break;
+ }
+}
+
+bool CrashGenerationServer::AddClient(ClientInfo* client_info) {
+ HANDLE request_wait_handle = NULL;
+ if (!RegisterWaitForSingleObject(&request_wait_handle,
+ client_info->dump_requested_handle(),
+ OnDumpRequest,
+ client_info,
+ INFINITE,
+ kDumpRequestThreadFlags)) {
+ return false;
+ }
+
+ client_info->set_dump_request_wait_handle(request_wait_handle);
+
+ // OnClientEnd will be called when the client process terminates.
+ HANDLE process_wait_handle = NULL;
+ if (!RegisterWaitForSingleObject(&process_wait_handle,
+ client_info->process_handle(),
+ OnClientEnd,
+ client_info,
+ INFINITE,
+ WT_EXECUTEONLYONCE)) {
+ return false;
+ }
+
+ client_info->set_process_exit_wait_handle(process_wait_handle);
+
+ // New scope to hold the lock for the shortest time.
+ {
+ AutoCriticalSection lock(&clients_sync_);
+ clients_.push_back(client_info);
+ }
+
+ return true;
+}
+
+// static
+void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) {
+ assert (context);
+
+ CrashGenerationServer* obj =
+ reinterpret_cast<CrashGenerationServer*>(context);
+ obj->HandleConnectionRequest();
+}
+
+// static
+void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) {
+ assert(context);
+ ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
+
+ CrashGenerationServer* crash_server = client_info->crash_server();
+ assert(crash_server);
+ crash_server->HandleDumpRequest(*client_info);
+
+ ResetEvent(client_info->dump_requested_handle());
+}
+
+// static
+void CALLBACK CrashGenerationServer::OnClientEnd(void* context, BOOLEAN) {
+ assert(context);
+ ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
+
+ CrashGenerationServer* crash_server = client_info->crash_server();
+ assert(crash_server);
+
+ InterlockedIncrement(&crash_server->cleanup_item_count_);
+
+ if (!QueueUserWorkItem(CleanupClient, context, WT_EXECUTEDEFAULT))
+ {
+ InterlockedDecrement(&crash_server->cleanup_item_count_);
+ }
+}
+
+// static
+DWORD WINAPI CrashGenerationServer::CleanupClient(void* context) {
+ assert(context);
+ ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
+
+ CrashGenerationServer* crash_server = client_info->crash_server();
+ assert(crash_server);
+
+ if (crash_server->exit_callback_) {
+ crash_server->exit_callback_(crash_server->exit_context_, client_info);
+ }
+
+ crash_server->DoCleanup(client_info);
+
+ InterlockedDecrement(&crash_server->cleanup_item_count_);
+ return 0;
+}
+
+void CrashGenerationServer::DoCleanup(ClientInfo* client_info) {
+ assert(client_info);
+
+ // Start a new scope to release lock automatically.
+ {
+ AutoCriticalSection lock(&clients_sync_);
+ clients_.remove(client_info);
+ }
+
+ delete client_info;
+}
+
+void CrashGenerationServer::HandleDumpRequest(const ClientInfo& client_info) {
+ // Generate the dump only if it's explicitly requested by the
+ // server application; otherwise the server might want to generate
+ // dump in the callback.
+ if (generate_dumps_) {
+ if (!GenerateDump(client_info)) {
+ return;
+ }
+ }
+
+ if (dump_callback_) {
+ dump_callback_(dump_context_, &client_info);
+ }
+
+ SetEvent(client_info.dump_generated_handle());
+}
+
+bool CrashGenerationServer::GenerateDump(const ClientInfo& client) {
+ assert(client.pid() != 0);
+ assert(client.process_handle());
+
+ // We have to get the address of EXCEPTION_INFORMATION from
+ // the client process address space.
+ EXCEPTION_POINTERS* client_ex_info = NULL;
+ if (!client.GetClientExceptionInfo(&client_ex_info)) {
+ return false;
+ }
+
+ DWORD client_thread_id = 0;
+ if (!client.GetClientThreadId(&client_thread_id)) {
+ return false;
+ }
+
+ return dump_generator_->WriteMinidump(client.process_handle(),
+ client.pid(),
+ client_thread_id,
+ GetCurrentThreadId(),
+ client_ex_info,
+ client.assert_info(),
+ client.dump_type(),
+ true);
+}
+
+} // namespace google_breakpad