summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc
diff options
context:
space:
mode:
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.cc931
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