diff options
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc')
-rw-r--r-- | toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc | 931 |
1 files changed, 0 insertions, 931 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc deleted file mode 100644 index bb0968fe0..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc +++ /dev/null @@ -1,931 +0,0 @@ -// 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 <list> -#include "client/windows/common/auto_critical_section.h" -#include "common/scoped_ptr.h" - -#include "client/windows/crash_generation/client_info.h" - -namespace google_breakpad { - -// Output buffer size. -static const size_t kOutBufferSize = 64; - -// Input buffer size. -static const size_t kInBufferSize = 64; - -// Access flags for the client on the dump request event. -static const DWORD kDumpRequestEventAccess = EVENT_MODIFY_STATE; - -// Access flags for the client on the dump generated event. -static const DWORD kDumpGeneratedEventAccess = EVENT_MODIFY_STATE | - SYNCHRONIZE; - -// Access flags for the client on the mutex. -static const DWORD kMutexAccess = SYNCHRONIZE; - -// Attribute flags for the pipe. -static const DWORD kPipeAttr = FILE_FLAG_FIRST_PIPE_INSTANCE | - PIPE_ACCESS_DUPLEX | - FILE_FLAG_OVERLAPPED; - -// Mode for the pipe. -static 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. -static 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. -static const ULONG kDumpRequestThreadFlags = WT_EXECUTEINWAITTHREAD | - WT_EXECUTELONGFUNCTION; - -static bool IsClientRequestValid(const ProtocolMessage& msg) { - return msg.tag == MESSAGE_TAG_UPLOAD_REQUEST || - (msg.tag == MESSAGE_TAG_REGISTRATION_REQUEST && - msg.id != 0 && - msg.thread_id != NULL && - msg.exception_pointers != NULL && - msg.assert_info != NULL); -} - -#ifndef NDEBUG -static bool CheckForIOIncomplete(bool success) { - // We should never get an I/O incomplete since we should not execute this - // unless the operation has finished and the overlapped event is signaled. If - // we do get INCOMPLETE, we have a bug in our code. - return success ? false : (GetLastError() == ERROR_IO_INCOMPLETE); -} -#endif - -CrashGenerationServer::CrashGenerationServer( - const std::wstring& pipe_name, - SECURITY_ATTRIBUTES* pipe_sec_attrs, - OnClientConnectedCallback connect_callback, - void* connect_context, - OnClientDumpRequestCallback dump_callback, - void* dump_context, - OnClientExitedCallback exit_callback, - void* exit_context, - OnClientUploadRequestCallback upload_request_callback, - void* upload_context, - bool generate_dumps, - const std::wstring* dump_path) - : pipe_name_(pipe_name), - pipe_sec_attrs_(pipe_sec_attrs), - 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), - upload_request_callback_(upload_request_callback), - upload_context_(upload_context), - generate_dumps_(generate_dumps), - pre_fetch_custom_info_(true), - dump_path_(dump_path ? *dump_path : L""), - server_state_(IPC_SERVER_STATE_UNINITIALIZED), - shutting_down_(false), - overlapped_(), - client_info_(NULL) { - InitializeCriticalSection(&sync_); -} - -// This should never be called from the OnPipeConnected callback. -// Otherwise the UnregisterWaitEx call below will cause a deadlock. -CrashGenerationServer::~CrashGenerationServer() { - // New scope to release the lock automatically. - { - // Make sure no clients are added or removed beyond this point. - // Before adding or removing any clients, the critical section - // must be entered and the shutting_down_ flag checked. The - // critical section is then exited only after the clients_ list - // modifications are done and the list is in a consistent state. - AutoCriticalSection lock(&sync_); - - // Indicate to existing threads that server is shutting down. - shutting_down_ = true; - } - // No one will modify the clients_ list beyond this point - - // not even from another thread. - - // Even if there are no current worker threads running, it is possible that - // an I/O request is pending on the pipe right now but not yet done. - // In fact, it's very likely this is the case unless we are in an ERROR - // state. If we don't wait for the pending I/O to be done, then when the I/O - // completes, it may write to invalid memory. AppVerifier will flag this - // problem too. So we disconnect from the pipe and then wait for the server - // to get into error state so that the pending I/O will fail and get - // cleared. - DisconnectNamedPipe(pipe_); - int num_tries = 100; - while (num_tries-- && server_state_ != IPC_SERVER_STATE_ERROR) { - Sleep(10); - } - - // 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. - // No need to enter the critical section because no one is allowed to modify - // the clients_ list once the shutting_down_ flag is set. - std::list<ClientInfo*>::iterator iter; - for (iter = clients_.begin(); iter != clients_.end(); ++iter) { - ClientInfo* client_info = *iter; - // Unregister waits. Wait for already executing callbacks to finish. - // Unregister the client process exit wait first and only then unregister - // the dump request wait. The reason is that the OnClientExit callback - // also unregisters the dump request wait and such a race (doing the same - // unregistration from two threads) is undesirable. - client_info->UnregisterProcessExitWait(true); - client_info->UnregisterDumpRequestWaitAndBlockUntilNoPending(); - - // Destroying the ClientInfo here is safe because all wait operations for - // this ClientInfo were unregistered and no pending or running callbacks - // for this ClientInfo can possible exist (block_until_no_pending option - // was used). - 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_); - } - - if (overlapped_.hEvent) { - CloseHandle(overlapped_.hEvent); - } - - DeleteCriticalSection(&sync_); -} - -bool CrashGenerationServer::Start() { - if (server_state_ != IPC_SERVER_STATE_UNINITIALIZED) { - return false; - } - - 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 nonsignaled. - NULL); // Name. - if (!overlapped_.hEvent) { - return false; - } - - // Register a callback with the thread pool for the client connection. - if (!RegisterWaitForSingleObject(&pipe_wait_handle_, - overlapped_.hEvent, - OnPipeConnected, - this, - INFINITE, - kPipeIOThreadFlags)) { - return false; - } - - pipe_ = CreateNamedPipe(pipe_name_.c_str(), - kPipeAttr, - kPipeMode, - 1, - kOutBufferSize, - kInBufferSize, - 0, - pipe_sec_attrs_); - if (pipe_ == INVALID_HANDLE_VALUE) { - return false; - } - - // Kick-start the state machine. This will initiate an asynchronous wait - // for client connections. - if (!SetEvent(overlapped_.hEvent)) { - server_state_ = IPC_SERVER_STATE_ERROR; - return false; - } - - // If we are in error state, it's because we failed to start listening. - return true; -} - -// 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)) { - EnterErrorState(); - return; - } - - bool success = ConnectNamedPipe(pipe_, &overlapped_) != FALSE; - DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); - - // 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); - - switch (error_code) { - case ERROR_IO_PENDING: - EnterStateWhenSignaled(IPC_SERVER_STATE_CONNECTING); - break; - - case ERROR_PIPE_CONNECTED: - EnterStateImmediately(IPC_SERVER_STATE_CONNECTED); - break; - - default: - EnterErrorState(); - 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; - DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); - - if (success) { - EnterStateImmediately(IPC_SERVER_STATE_CONNECTED); - } else if (error_code != ERROR_IO_INCOMPLETE) { - EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); - } else { - // remain in CONNECTING state - } -} - -// 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; - DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); - - // 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 || error_code == ERROR_IO_PENDING) { - EnterStateWhenSignaled(IPC_SERVER_STATE_READING); - } else { - EnterStateImmediately(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)) { - EnterStateImmediately(IPC_SERVER_STATE_READ_DONE); - return; - } - - assert(!CheckForIOIncomplete(success)); - EnterStateImmediately(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_)) { - EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); - return; - } - - if (msg_.tag == MESSAGE_TAG_UPLOAD_REQUEST) { - if (upload_request_callback_) - upload_request_callback_(upload_context_, msg_.id); - EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); - return; - } - - scoped_ptr<ClientInfo> client_info( - new ClientInfo(this, - msg_.id, - msg_.dump_type, - msg_.thread_id, - msg_.exception_pointers, - msg_.assert_info, - msg_.custom_client_info)); - - if (!client_info->Initialize()) { - EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); - return; - } - - // Issues an asynchronous WriteFile call if successful. - // Iff successful, assigns ownership of the client_info pointer to the server - // instance, in which case we must be sure not to free it in this function. - if (!RespondToClient(client_info.get())) { - EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); - return; - } - - // This is only valid as long as it can be found in the clients_ list - client_info_ = client_info.release(); - - // 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. - EnterStateWhenSignaled(IPC_SERVER_STATE_WRITING); -} - -// 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) { - EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE); - return; - } - - assert(!CheckForIOIncomplete(success)); - EnterStateImmediately(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); - - DWORD bytes_count = 0; - bool success = ReadFile(pipe_, - &msg_, - sizeof(msg_), - &bytes_count, - &overlapped_) != FALSE; - DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); - - if (success) { - EnterStateImmediately(IPC_SERVER_STATE_READING_ACK); - } else if (error_code == ERROR_IO_PENDING) { - EnterStateWhenSignaled(IPC_SERVER_STATE_READING_ACK); - } else { - EnterStateImmediately(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_) { - // Note that there is only a single copy of the ClientInfo of the - // currently connected client. However it is being referenced from - // two different places: - // - the client_info_ member - // - the clients_ list - // The lifetime of this ClientInfo depends on the lifetime of the - // client process - basically it can go away at any time. - // However, as long as it is referenced by the clients_ list it - // is guaranteed to be valid. Enter the critical section and check - // to see whether the client_info_ can be found in the list. - // If found, execute the callback and only then leave the critical - // section. - AutoCriticalSection lock(&sync_); - - bool client_is_still_alive = false; - std::list<ClientInfo*>::iterator iter; - for (iter = clients_.begin(); iter != clients_.end(); ++iter) { - if (client_info_ == *iter) { - client_is_still_alive = true; - break; - } - } - - if (client_is_still_alive) { - connect_callback_(connect_context_, client_info_); - } - } - } else { - assert(!CheckForIOIncomplete(success)); - } - - EnterStateImmediately(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)) { - EnterErrorState(); - return; - } - - if (!DisconnectNamedPipe(pipe_)) { - EnterErrorState(); - return; - } - - // If the server is shutting down do not connect to the - // next client. - if (shutting_down_) { - return; - } - - EnterStateImmediately(IPC_SERVER_STATE_INITIAL); -} - -void CrashGenerationServer::EnterErrorState() { - SetEvent(overlapped_.hEvent); - server_state_ = IPC_SERVER_STATE_ERROR; -} - -void CrashGenerationServer::EnterStateWhenSignaled(IPCServerState state) { - server_state_ = state; -} - -void CrashGenerationServer::EnterStateImmediately(IPCServerState state) { - server_state_ = state; - - 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->id = GetCurrentProcessId(); - - if (CreateClientHandles(client_info, reply)) { - return true; - } - - // Closing of remote handles (belonging to a different process) can - // only be done through DuplicateHandle. - if (reply->dump_request_handle) { - DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle - reply->dump_request_handle, // hSourceHandle - NULL, // hTargetProcessHandle - 0, // lpTargetHandle - 0, // dwDesiredAccess - FALSE, // bInheritHandle - DUPLICATE_CLOSE_SOURCE); // dwOptions - reply->dump_request_handle = NULL; - } - - if (reply->dump_generated_handle) { - DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle - reply->dump_generated_handle, // hSourceHandle - NULL, // hTargetProcessHandle - 0, // lpTargetHandle - 0, // dwDesiredAccess - FALSE, // bInheritHandle - DUPLICATE_CLOSE_SOURCE); // dwOptions - reply->dump_generated_handle = NULL; - } - - if (reply->server_alive_handle) { - DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle - reply->server_alive_handle, // hSourceHandle - NULL, // hTargetProcessHandle - 0, // lpTargetHandle - 0, // dwDesiredAccess - FALSE, // bInheritHandle - DUPLICATE_CLOSE_SOURCE); // dwOptions - reply->server_alive_handle = NULL; - } - - 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; - } - - DWORD bytes_count = 0; - bool success = WriteFile(pipe_, - &reply, - sizeof(reply), - &bytes_count, - &overlapped_) != FALSE; - DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); - - if (!success && error_code != ERROR_IO_PENDING) { - return false; - } - - // Takes over ownership of client_info. We MUST return true if AddClient - // succeeds. - return AddClient(client_info); -} - -// 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() { - // If the server is shutting down, get into ERROR state, reset the event so - // more workers don't run and return immediately. - if (shutting_down_) { - server_state_ = IPC_SERVER_STATE_ERROR; - ResetEvent(overlapped_.hEvent); - return; - } - - 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(&sync_); - if (shutting_down_) { - // If server is shutting down, don't add new clients - return false; - } - 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); - if (crash_server->pre_fetch_custom_info_) { - client_info->PopulateCustomInfo(); - } - 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); - - crash_server->HandleClientProcessExit(client_info); -} - -void CrashGenerationServer::HandleClientProcessExit(ClientInfo* client_info) { - assert(client_info); - - // Must unregister the dump request wait operation and wait for any - // dump requests that might be pending to finish before proceeding - // with the client_info cleanup. - client_info->UnregisterDumpRequestWaitAndBlockUntilNoPending(); - - if (exit_callback_) { - exit_callback_(exit_context_, client_info); - } - - // Start a new scope to release lock automatically. - { - AutoCriticalSection lock(&sync_); - if (shutting_down_) { - // The crash generation server is shutting down and as part of the - // shutdown process it will delete all clients from the clients_ list. - return; - } - clients_.remove(client_info); - } - - // Explicitly unregister the process exit wait using the non-blocking method. - // Otherwise, the destructor will attempt to unregister it using the blocking - // method which will lead to a deadlock because it is being called from the - // callback of the same wait operation - client_info->UnregisterProcessExitWait(false); - - delete client_info; -} - -void CrashGenerationServer::HandleDumpRequest(const ClientInfo& client_info) { - bool execute_callback = true; - // Generate the dump only if it's explicitly requested by the - // server application; otherwise the server might want to generate - // dump in the callback. - std::wstring dump_path; - if (generate_dumps_) { - if (!GenerateDump(client_info, &dump_path)) { - // client proccess terminated or some other error - execute_callback = false; - } - } - - if (dump_callback_ && execute_callback) { - std::wstring* ptr_dump_path = (dump_path == L"") ? NULL : &dump_path; - dump_callback_(dump_context_, &client_info, ptr_dump_path); - } - - SetEvent(client_info.dump_generated_handle()); -} - -bool CrashGenerationServer::GenerateDump(const ClientInfo& client, - std::wstring* dump_path) { - 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; - } - - MinidumpGenerator dump_generator(dump_path_, - client.process_handle(), - client.pid(), - client_thread_id, - GetCurrentThreadId(), - client_ex_info, - client.assert_info(), - client.dump_type(), - true); - if (!dump_generator.GenerateDumpFile(dump_path)) { - return false; - } - return dump_generator.WriteMinidump(); -} - -} // namespace google_breakpad |