diff options
author | mmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2008-04-07 21:50:57 +0000 |
---|---|---|
committer | mmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2008-04-07 21:50:57 +0000 |
commit | eeca9921c563d802cccc5593bf55dcb7683e7250 (patch) | |
tree | dfd57d6d1c4d70e041e297e698b717b2f36a0717 /src/client/windows/crash_generation/crash_generation_server.cc | |
parent | Processor crashes on some truncated minidumps after #222. r=ted.mielczarek (diff) | |
download | breakpad-eeca9921c563d802cccc5593bf55dcb7683e7250.tar.xz |
Fix newlines (#253). rs=ted.mielczarek
http://groups.google.com/group/google-breakpad-dev/browse_thread/thread/7e62a299ce3fa222
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@255 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/client/windows/crash_generation/crash_generation_server.cc')
-rw-r--r-- | src/client/windows/crash_generation/crash_generation_server.cc | 1646 |
1 files changed, 823 insertions, 823 deletions
diff --git a/src/client/windows/crash_generation/crash_generation_server.cc b/src/client/windows/crash_generation/crash_generation_server.cc index 5409cc5a..c1929e50 100644 --- a/src/client/windows/crash_generation/crash_generation_server.cc +++ b/src/client/windows/crash_generation/crash_generation_server.cc @@ -1,823 +1,823 @@ -// 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 IsClientRequestValid(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 std::wstring& 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;
- 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 (!IsClientRequestValid(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;
- 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;
- 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
+// 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 IsClientRequestValid(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 std::wstring& 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; + 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 (!IsClientRequestValid(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; + 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; + 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 |