| Index: third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.cc
|
| diff --git a/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.cc b/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.cc
|
| deleted file mode 100644
|
| index 61af1b2ddb03c8a4a2bfaf51e27206d0636c0426..0000000000000000000000000000000000000000
|
| --- a/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.cc
|
| +++ /dev/null
|
| @@ -1,880 +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 "processor/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;
|
| -
|
| -// Maximum delay during server shutdown if some work items
|
| -// are still executing.
|
| -static const int kShutdownDelayMs = 10000;
|
| -
|
| -// Interval for each sleep during server shutdown.
|
| -static 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,
|
| - SECURITY_ATTRIBUTES* pipe_sec_attrs,
|
| - 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_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),
|
| - generate_dumps_(generate_dumps),
|
| - dump_generator_(NULL),
|
| - server_state_(IPC_SERVER_STATE_UNINITIALIZED),
|
| - 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;
|
| -
|
| - // 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.
|
| - // 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_);
|
| - }
|
| -
|
| - if (overlapped_.hEvent) {
|
| - CloseHandle(overlapped_.hEvent);
|
| - }
|
| -
|
| - DeleteCriticalSection(&clients_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 signaled.
|
| - 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.
|
| - HandleInitialState();
|
| -
|
| - // If we are in error state, it's because we failed to start listening.
|
| - return server_state_ != IPC_SERVER_STATE_ERROR;
|
| -}
|
| -
|
| -// 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;
|
| - DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
| -
|
| - if (success && bytes_count == sizeof(ProtocolMessage)) {
|
| - EnterStateImmediately(IPC_SERVER_STATE_READ_DONE);
|
| - } else {
|
| - // 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);
|
| -
|
| - 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;
|
| - }
|
| -
|
| - scoped_ptr<ClientInfo> client_info(
|
| - new ClientInfo(this,
|
| - msg_.pid,
|
| - 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;
|
| - }
|
| -
|
| - 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;
|
| - DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
| -
|
| - if (success) {
|
| - EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE);
|
| - return;
|
| - }
|
| -
|
| - // 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);
|
| -
|
| - 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;
|
| - DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
| -
|
| - if (success) {
|
| - // The connection handshake with the client is now complete; perform
|
| - // the callback.
|
| - if (connect_callback_) {
|
| - connect_callback_(connect_context_, client_info_);
|
| - }
|
| - } else {
|
| - // 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);
|
| - }
|
| -
|
| - 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->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;
|
| - }
|
| -
|
| - 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.
|
| - if (!AddClient(client_info)) {
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// 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 we are shutting doen then 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(&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);
|
| - client_info->PopulateCustomInfo();
|
| -
|
| - 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.
|
| - std::wstring dump_path;
|
| - if (generate_dumps_) {
|
| - if (!GenerateDump(client_info, &dump_path)) {
|
| - return;
|
| - }
|
| - }
|
| -
|
| - if (dump_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;
|
| - }
|
| -
|
| - return dump_generator_->WriteMinidump(client.process_handle(),
|
| - client.pid(),
|
| - client_thread_id,
|
| - GetCurrentThreadId(),
|
| - client_ex_info,
|
| - client.assert_info(),
|
| - client.dump_type(),
|
| - true,
|
| - dump_path);
|
| -}
|
| -
|
| -} // namespace google_breakpad
|
|
|