| Index: third_party/crashpad/crashpad/util/win/exception_handler_server.cc
|
| diff --git a/third_party/crashpad/crashpad/util/win/exception_handler_server.cc b/third_party/crashpad/crashpad/util/win/exception_handler_server.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..47815ceae6554c8dd72fb8f11ea918254a3cfa58
|
| --- /dev/null
|
| +++ b/third_party/crashpad/crashpad/util/win/exception_handler_server.cc
|
| @@ -0,0 +1,602 @@
|
| +// Copyright 2015 The Crashpad Authors. All rights reserved.
|
| +//
|
| +// Licensed under the Apache License, Version 2.0 (the "License");
|
| +// you may not use this file except in compliance with the License.
|
| +// You may obtain a copy of the License at
|
| +//
|
| +// http://www.apache.org/licenses/LICENSE-2.0
|
| +//
|
| +// Unless required by applicable law or agreed to in writing, software
|
| +// distributed under the License is distributed on an "AS IS" BASIS,
|
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +// See the License for the specific language governing permissions and
|
| +// limitations under the License.
|
| +
|
| +#include "util/win/exception_handler_server.h"
|
| +
|
| +#include <sddl.h>
|
| +#include <string.h>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/numerics/safe_conversions.h"
|
| +#include "base/rand_util.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "minidump/minidump_file_writer.h"
|
| +#include "snapshot/crashpad_info_client_options.h"
|
| +#include "snapshot/win/process_snapshot_win.h"
|
| +#include "util/file/file_writer.h"
|
| +#include "util/stdlib/move.h"
|
| +#include "util/misc/random_string.h"
|
| +#include "util/misc/tri_state.h"
|
| +#include "util/misc/uuid.h"
|
| +#include "util/win/get_function.h"
|
| +#include "util/win/handle.h"
|
| +#include "util/win/registration_protocol_win.h"
|
| +#include "util/win/scoped_local_alloc.h"
|
| +#include "util/win/xp_compat.h"
|
| +
|
| +namespace crashpad {
|
| +
|
| +namespace {
|
| +
|
| +// We create two pipe instances, so that there's one listening while the
|
| +// PipeServiceProc is processing a registration.
|
| +const size_t kPipeInstances = 2;
|
| +
|
| +// Wraps CreateNamedPipe() to create a single named pipe instance.
|
| +//
|
| +// If first_instance is true, the named pipe instance will be created with
|
| +// FILE_FLAG_FIRST_PIPE_INSTANCE. This ensures that the the pipe name is not
|
| +// already in use when created. The first instance will be created with an
|
| +// untrusted integrity SACL so instances of this pipe can be connected to by
|
| +// processes of any integrity level.
|
| +HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name,
|
| + bool first_instance) {
|
| + SECURITY_ATTRIBUTES security_attributes;
|
| + SECURITY_ATTRIBUTES* security_attributes_pointer = nullptr;
|
| + ScopedLocalAlloc scoped_sec_desc;
|
| +
|
| + if (first_instance) {
|
| + // Pre-Vista does not have integrity levels.
|
| + const DWORD version = GetVersion();
|
| + const DWORD major_version = LOBYTE(LOWORD(version));
|
| + const bool is_vista_or_later = major_version >= 6;
|
| + if (is_vista_or_later) {
|
| + // Mandatory Label, no ACE flags, no ObjectType, integrity level
|
| + // untrusted.
|
| + const wchar_t kSddl[] = L"S:(ML;;;;;S-1-16-0)";
|
| +
|
| + PSECURITY_DESCRIPTOR sec_desc;
|
| + PCHECK(ConvertStringSecurityDescriptorToSecurityDescriptor(
|
| + kSddl, SDDL_REVISION_1, &sec_desc, nullptr))
|
| + << "ConvertStringSecurityDescriptorToSecurityDescriptor";
|
| +
|
| + // Take ownership of the allocated SECURITY_DESCRIPTOR.
|
| + scoped_sec_desc.reset(sec_desc);
|
| +
|
| + memset(&security_attributes, 0, sizeof(security_attributes));
|
| + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
| + security_attributes.lpSecurityDescriptor = sec_desc;
|
| + security_attributes.bInheritHandle = FALSE;
|
| + security_attributes_pointer = &security_attributes;
|
| + }
|
| + }
|
| +
|
| + return CreateNamedPipe(
|
| + pipe_name.c_str(),
|
| + PIPE_ACCESS_DUPLEX | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
|
| + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
| + kPipeInstances,
|
| + 512,
|
| + 512,
|
| + 0,
|
| + security_attributes_pointer);
|
| +}
|
| +
|
| +decltype(GetNamedPipeClientProcessId)* GetNamedPipeClientProcessIdFunction() {
|
| + static const auto get_named_pipe_client_process_id =
|
| + GET_FUNCTION(L"kernel32.dll", ::GetNamedPipeClientProcessId);
|
| + return get_named_pipe_client_process_id;
|
| +}
|
| +
|
| +HANDLE DuplicateEvent(HANDLE process, HANDLE event) {
|
| + HANDLE handle;
|
| + if (DuplicateHandle(GetCurrentProcess(),
|
| + event,
|
| + process,
|
| + &handle,
|
| + SYNCHRONIZE | EVENT_MODIFY_STATE,
|
| + false,
|
| + 0)) {
|
| + return handle;
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +namespace internal {
|
| +
|
| +//! \brief Context information for the named pipe handler threads.
|
| +class PipeServiceContext {
|
| + public:
|
| + PipeServiceContext(HANDLE port,
|
| + HANDLE pipe,
|
| + ExceptionHandlerServer::Delegate* delegate,
|
| + base::Lock* clients_lock,
|
| + std::set<internal::ClientData*>* clients,
|
| + uint64_t shutdown_token)
|
| + : port_(port),
|
| + pipe_(pipe),
|
| + delegate_(delegate),
|
| + clients_lock_(clients_lock),
|
| + clients_(clients),
|
| + shutdown_token_(shutdown_token) {}
|
| +
|
| + HANDLE port() const { return port_; }
|
| + HANDLE pipe() const { return pipe_.get(); }
|
| + ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
|
| + base::Lock* clients_lock() const { return clients_lock_; }
|
| + std::set<internal::ClientData*>* clients() const { return clients_; }
|
| + uint64_t shutdown_token() const { return shutdown_token_; }
|
| +
|
| + private:
|
| + HANDLE port_; // weak
|
| + ScopedKernelHANDLE pipe_;
|
| + ExceptionHandlerServer::Delegate* delegate_; // weak
|
| + base::Lock* clients_lock_; // weak
|
| + std::set<internal::ClientData*>* clients_; // weak
|
| + uint64_t shutdown_token_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(PipeServiceContext);
|
| +};
|
| +
|
| +//! \brief The context data for registered threadpool waits.
|
| +//!
|
| +//! This object must be created and destroyed on the main thread. Access must be
|
| +//! guarded by use of the lock() with the exception of the threadpool wait
|
| +//! variables which are accessed only by the main thread.
|
| +class ClientData {
|
| + public:
|
| + ClientData(HANDLE port,
|
| + ExceptionHandlerServer::Delegate* delegate,
|
| + ScopedKernelHANDLE process,
|
| + WinVMAddress crash_exception_information_address,
|
| + WinVMAddress non_crash_exception_information_address,
|
| + WinVMAddress debug_critical_section_address,
|
| + WAITORTIMERCALLBACK crash_dump_request_callback,
|
| + WAITORTIMERCALLBACK non_crash_dump_request_callback,
|
| + WAITORTIMERCALLBACK process_end_callback)
|
| + : crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),
|
| + non_crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),
|
| + process_end_thread_pool_wait_(INVALID_HANDLE_VALUE),
|
| + lock_(),
|
| + port_(port),
|
| + delegate_(delegate),
|
| + crash_dump_requested_event_(
|
| + CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
|
| + non_crash_dump_requested_event_(
|
| + CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
|
| + non_crash_dump_completed_event_(
|
| + CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
|
| + process_(crashpad::move(process)),
|
| + crash_exception_information_address_(
|
| + crash_exception_information_address),
|
| + non_crash_exception_information_address_(
|
| + non_crash_exception_information_address),
|
| + debug_critical_section_address_(debug_critical_section_address) {
|
| + RegisterThreadPoolWaits(crash_dump_request_callback,
|
| + non_crash_dump_request_callback,
|
| + process_end_callback);
|
| + }
|
| +
|
| + ~ClientData() {
|
| + // It is important that this only access the threadpool waits (it's called
|
| + // from the main thread) until the waits are unregistered, to ensure that
|
| + // any outstanding callbacks are complete.
|
| + UnregisterThreadPoolWaits();
|
| + }
|
| +
|
| + base::Lock* lock() { return &lock_; }
|
| + HANDLE port() const { return port_; }
|
| + ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
|
| + HANDLE crash_dump_requested_event() const {
|
| + return crash_dump_requested_event_.get();
|
| + }
|
| + HANDLE non_crash_dump_requested_event() const {
|
| + return non_crash_dump_requested_event_.get();
|
| + }
|
| + HANDLE non_crash_dump_completed_event() const {
|
| + return non_crash_dump_completed_event_.get();
|
| + }
|
| + WinVMAddress crash_exception_information_address() const {
|
| + return crash_exception_information_address_;
|
| + }
|
| + WinVMAddress non_crash_exception_information_address() const {
|
| + return non_crash_exception_information_address_;
|
| + }
|
| + WinVMAddress debug_critical_section_address() const {
|
| + return debug_critical_section_address_;
|
| + }
|
| + HANDLE process() const { return process_.get(); }
|
| +
|
| + private:
|
| + void RegisterThreadPoolWaits(
|
| + WAITORTIMERCALLBACK crash_dump_request_callback,
|
| + WAITORTIMERCALLBACK non_crash_dump_request_callback,
|
| + WAITORTIMERCALLBACK process_end_callback) {
|
| + if (!RegisterWaitForSingleObject(&crash_dump_request_thread_pool_wait_,
|
| + crash_dump_requested_event_.get(),
|
| + crash_dump_request_callback,
|
| + this,
|
| + INFINITE,
|
| + WT_EXECUTEDEFAULT)) {
|
| + LOG(ERROR) << "RegisterWaitForSingleObject crash dump requested";
|
| + }
|
| +
|
| + if (!RegisterWaitForSingleObject(&non_crash_dump_request_thread_pool_wait_,
|
| + non_crash_dump_requested_event_.get(),
|
| + non_crash_dump_request_callback,
|
| + this,
|
| + INFINITE,
|
| + WT_EXECUTEDEFAULT)) {
|
| + LOG(ERROR) << "RegisterWaitForSingleObject non-crash dump requested";
|
| + }
|
| +
|
| + if (!RegisterWaitForSingleObject(&process_end_thread_pool_wait_,
|
| + process_.get(),
|
| + process_end_callback,
|
| + this,
|
| + INFINITE,
|
| + WT_EXECUTEONLYONCE)) {
|
| + LOG(ERROR) << "RegisterWaitForSingleObject process end";
|
| + }
|
| + }
|
| +
|
| + // This blocks until outstanding calls complete so that we know it's safe to
|
| + // delete this object. Because of this, it must be executed on the main
|
| + // thread, not a threadpool thread.
|
| + void UnregisterThreadPoolWaits() {
|
| + UnregisterWaitEx(crash_dump_request_thread_pool_wait_,
|
| + INVALID_HANDLE_VALUE);
|
| + crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
|
| + UnregisterWaitEx(non_crash_dump_request_thread_pool_wait_,
|
| + INVALID_HANDLE_VALUE);
|
| + non_crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
|
| + UnregisterWaitEx(process_end_thread_pool_wait_, INVALID_HANDLE_VALUE);
|
| + process_end_thread_pool_wait_ = INVALID_HANDLE_VALUE;
|
| + }
|
| +
|
| + // These are only accessed on the main thread.
|
| + HANDLE crash_dump_request_thread_pool_wait_;
|
| + HANDLE non_crash_dump_request_thread_pool_wait_;
|
| + HANDLE process_end_thread_pool_wait_;
|
| +
|
| + base::Lock lock_;
|
| + // Access to these fields must be guarded by lock_.
|
| + HANDLE port_; // weak
|
| + ExceptionHandlerServer::Delegate* delegate_; // weak
|
| + ScopedKernelHANDLE crash_dump_requested_event_;
|
| + ScopedKernelHANDLE non_crash_dump_requested_event_;
|
| + ScopedKernelHANDLE non_crash_dump_completed_event_;
|
| + ScopedKernelHANDLE process_;
|
| + WinVMAddress crash_exception_information_address_;
|
| + WinVMAddress non_crash_exception_information_address_;
|
| + WinVMAddress debug_critical_section_address_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ClientData);
|
| +};
|
| +
|
| +} // namespace internal
|
| +
|
| +ExceptionHandlerServer::Delegate::~Delegate() {
|
| +}
|
| +
|
| +ExceptionHandlerServer::ExceptionHandlerServer(bool persistent)
|
| + : pipe_name_(),
|
| + port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)),
|
| + first_pipe_instance_(),
|
| + clients_lock_(),
|
| + clients_(),
|
| + persistent_(persistent) {
|
| +}
|
| +
|
| +ExceptionHandlerServer::~ExceptionHandlerServer() {
|
| +}
|
| +
|
| +void ExceptionHandlerServer::SetPipeName(const std::wstring& pipe_name) {
|
| + DCHECK(pipe_name_.empty());
|
| + DCHECK(!pipe_name.empty());
|
| +
|
| + pipe_name_ = pipe_name;
|
| +}
|
| +
|
| +std::wstring ExceptionHandlerServer::CreatePipe() {
|
| + DCHECK(!first_pipe_instance_.is_valid());
|
| +
|
| + int tries = 5;
|
| + std::string pipe_name_base =
|
| + base::StringPrintf("\\\\.\\pipe\\crashpad_%d_", GetCurrentProcessId());
|
| + std::wstring pipe_name;
|
| + do {
|
| + pipe_name = base::UTF8ToUTF16(pipe_name_base + RandomString());
|
| +
|
| + first_pipe_instance_.reset(CreateNamedPipeInstance(pipe_name, true));
|
| +
|
| + // CreateNamedPipe() is documented as setting the error to
|
| + // ERROR_ACCESS_DENIED if FILE_FLAG_FIRST_PIPE_INSTANCE is specified and the
|
| + // pipe name is already in use. However it may set the error to other codes
|
| + // such as ERROR_PIPE_BUSY (if the pipe already exists and has reached its
|
| + // maximum instance count) or ERROR_INVALID_PARAMETER (if the pipe already
|
| + // exists and its attributes differ from those specified to
|
| + // CreateNamedPipe()). Some of these errors may be ambiguous: for example,
|
| + // ERROR_INVALID_PARAMETER may also occur if CreateNamedPipe() is called
|
| + // incorrectly even in the absence of an existing pipe by the same name.
|
| + //
|
| + // Rather than chasing down all of the possible errors that might indicate
|
| + // that a pipe name is already in use, retry up to a few times on any error.
|
| + } while (!first_pipe_instance_.is_valid() && --tries);
|
| +
|
| + PCHECK(first_pipe_instance_.is_valid()) << "CreateNamedPipe";
|
| +
|
| + SetPipeName(pipe_name);
|
| + return pipe_name;
|
| +}
|
| +
|
| +void ExceptionHandlerServer::Run(Delegate* delegate) {
|
| + uint64_t shutdown_token = base::RandUint64();
|
| + ScopedKernelHANDLE thread_handles[kPipeInstances];
|
| + for (int i = 0; i < arraysize(thread_handles); ++i) {
|
| + HANDLE pipe;
|
| + if (first_pipe_instance_.is_valid()) {
|
| + pipe = first_pipe_instance_.release();
|
| + } else {
|
| + pipe = CreateNamedPipeInstance(pipe_name_, i == 0);
|
| + PCHECK(pipe != INVALID_HANDLE_VALUE) << "CreateNamedPipe";
|
| + }
|
| +
|
| + // Ownership of this object (and the pipe instance) is given to the new
|
| + // thread. We close the thread handles at the end of the scope. They clean
|
| + // up the context object and the pipe instance on termination.
|
| + internal::PipeServiceContext* context =
|
| + new internal::PipeServiceContext(port_.get(),
|
| + pipe,
|
| + delegate,
|
| + &clients_lock_,
|
| + &clients_,
|
| + shutdown_token);
|
| + thread_handles[i].reset(
|
| + CreateThread(nullptr, 0, &PipeServiceProc, context, 0, nullptr));
|
| + PCHECK(thread_handles[i].is_valid()) << "CreateThread";
|
| + }
|
| +
|
| + delegate->ExceptionHandlerServerStarted();
|
| +
|
| + // This is the main loop of the server. Most work is done on the threadpool,
|
| + // other than process end handling which is posted back to this main thread,
|
| + // as we must unregister the threadpool waits here.
|
| + for (;;) {
|
| + OVERLAPPED* ov = nullptr;
|
| + ULONG_PTR key = 0;
|
| + DWORD bytes = 0;
|
| + GetQueuedCompletionStatus(port_.get(), &bytes, &key, &ov, INFINITE);
|
| + if (!key) {
|
| + // Shutting down.
|
| + break;
|
| + }
|
| +
|
| + // Otherwise, this is a request to unregister and destroy the given client.
|
| + // delete'ing the ClientData blocks in UnregisterWaitEx to ensure all
|
| + // outstanding threadpool waits are complete. This is important because the
|
| + // process handle can be signalled *before* the dump request is signalled.
|
| + internal::ClientData* client = reinterpret_cast<internal::ClientData*>(key);
|
| + base::AutoLock lock(clients_lock_);
|
| + clients_.erase(client);
|
| + delete client;
|
| + if (!persistent_ && clients_.empty())
|
| + break;
|
| + }
|
| +
|
| + // Signal to the named pipe instances that they should terminate.
|
| + for (int i = 0; i < arraysize(thread_handles); ++i) {
|
| + ClientToServerMessage message;
|
| + memset(&message, 0, sizeof(message));
|
| + message.type = ClientToServerMessage::kShutdown;
|
| + message.shutdown.token = shutdown_token;
|
| + ServerToClientMessage response;
|
| + SendToCrashHandlerServer(pipe_name_,
|
| + reinterpret_cast<ClientToServerMessage&>(message),
|
| + &response);
|
| + }
|
| +
|
| + for (auto& handle : thread_handles)
|
| + WaitForSingleObject(handle.get(), INFINITE);
|
| +
|
| + // Deleting ClientData does a blocking wait until the threadpool executions
|
| + // have terminated when unregistering them.
|
| + {
|
| + base::AutoLock lock(clients_lock_);
|
| + for (auto* client : clients_)
|
| + delete client;
|
| + clients_.clear();
|
| + }
|
| +}
|
| +
|
| +void ExceptionHandlerServer::Stop() {
|
| + // Post a null key (third argument) to trigger shutdown.
|
| + PostQueuedCompletionStatus(port_.get(), 0, 0, nullptr);
|
| +}
|
| +
|
| +// This function must be called with service_context.pipe() already connected to
|
| +// a client pipe. It exchanges data with the client and adds a ClientData record
|
| +// to service_context->clients().
|
| +//
|
| +// static
|
| +bool ExceptionHandlerServer::ServiceClientConnection(
|
| + const internal::PipeServiceContext& service_context) {
|
| + ClientToServerMessage message;
|
| +
|
| + if (!LoggingReadFile(service_context.pipe(), &message, sizeof(message)))
|
| + return false;
|
| +
|
| + switch (message.type) {
|
| + case ClientToServerMessage::kShutdown: {
|
| + if (message.shutdown.token != service_context.shutdown_token()) {
|
| + LOG(ERROR) << "forged shutdown request, got: "
|
| + << message.shutdown.token;
|
| + return false;
|
| + }
|
| + ServerToClientMessage shutdown_response = {};
|
| + LoggingWriteFile(service_context.pipe(),
|
| + &shutdown_response,
|
| + sizeof(shutdown_response));
|
| + return true;
|
| + }
|
| +
|
| + case ClientToServerMessage::kRegister:
|
| + // Handled below.
|
| + break;
|
| +
|
| + default:
|
| + LOG(ERROR) << "unhandled message type: " << message.type;
|
| + return false;
|
| + }
|
| +
|
| + if (message.registration.version != RegistrationRequest::kMessageVersion) {
|
| + LOG(ERROR) << "unexpected version. got: " << message.registration.version
|
| + << " expecting: " << RegistrationRequest::kMessageVersion;
|
| + return false;
|
| + }
|
| +
|
| + decltype(GetNamedPipeClientProcessId)* get_named_pipe_client_process_id =
|
| + GetNamedPipeClientProcessIdFunction();
|
| + if (get_named_pipe_client_process_id) {
|
| + // GetNamedPipeClientProcessId is only available on Vista+.
|
| + DWORD real_pid = 0;
|
| + if (get_named_pipe_client_process_id(service_context.pipe(), &real_pid) &&
|
| + message.registration.client_process_id != real_pid) {
|
| + LOG(ERROR) << "forged client pid, real pid: " << real_pid
|
| + << ", got: " << message.registration.client_process_id;
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + // We attempt to open the process as us. This is the main case that should
|
| + // almost always succeed as the server will generally be more privileged. If
|
| + // we're running as a different user, it may be that we will fail to open
|
| + // the process, but the client will be able to, so we make a second attempt
|
| + // having impersonated the client.
|
| + HANDLE client_process = OpenProcess(
|
| + kXPProcessAllAccess, false, message.registration.client_process_id);
|
| + if (!client_process) {
|
| + if (!ImpersonateNamedPipeClient(service_context.pipe())) {
|
| + PLOG(ERROR) << "ImpersonateNamedPipeClient";
|
| + return false;
|
| + }
|
| + client_process = OpenProcess(
|
| + kXPProcessAllAccess, false, message.registration.client_process_id);
|
| + PCHECK(RevertToSelf());
|
| + if (!client_process) {
|
| + LOG(ERROR) << "failed to open " << message.registration.client_process_id;
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + internal::ClientData* client;
|
| + {
|
| + base::AutoLock lock(*service_context.clients_lock());
|
| + client = new internal::ClientData(
|
| + service_context.port(),
|
| + service_context.delegate(),
|
| + ScopedKernelHANDLE(client_process),
|
| + message.registration.crash_exception_information,
|
| + message.registration.non_crash_exception_information,
|
| + message.registration.critical_section_address,
|
| + &OnCrashDumpEvent,
|
| + &OnNonCrashDumpEvent,
|
| + &OnProcessEnd);
|
| + service_context.clients()->insert(client);
|
| + }
|
| +
|
| + // Duplicate the events back to the client so they can request a dump.
|
| + ServerToClientMessage response;
|
| + response.registration.request_crash_dump_event =
|
| + HandleToInt(DuplicateEvent(
|
| + client->process(), client->crash_dump_requested_event()));
|
| + response.registration.request_non_crash_dump_event =
|
| + HandleToInt(DuplicateEvent(
|
| + client->process(), client->non_crash_dump_requested_event()));
|
| + response.registration.non_crash_dump_completed_event =
|
| + HandleToInt(DuplicateEvent(
|
| + client->process(), client->non_crash_dump_completed_event()));
|
| +
|
| + if (!LoggingWriteFile(service_context.pipe(), &response, sizeof(response)))
|
| + return false;
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// static
|
| +DWORD __stdcall ExceptionHandlerServer::PipeServiceProc(void* ctx) {
|
| + internal::PipeServiceContext* service_context =
|
| + reinterpret_cast<internal::PipeServiceContext*>(ctx);
|
| + DCHECK(service_context);
|
| +
|
| + for (;;) {
|
| + bool ret = !!ConnectNamedPipe(service_context->pipe(), nullptr);
|
| + if (!ret && GetLastError() != ERROR_PIPE_CONNECTED) {
|
| + PLOG(ERROR) << "ConnectNamedPipe";
|
| + } else if (ServiceClientConnection(*service_context)) {
|
| + break;
|
| + }
|
| + DisconnectNamedPipe(service_context->pipe());
|
| + }
|
| +
|
| + delete service_context;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +// static
|
| +void __stdcall ExceptionHandlerServer::OnCrashDumpEvent(void* ctx, BOOLEAN) {
|
| + // This function is executed on the thread pool.
|
| + internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
|
| + base::AutoLock lock(*client->lock());
|
| +
|
| + // Capture the exception.
|
| + unsigned int exit_code = client->delegate()->ExceptionHandlerServerException(
|
| + client->process(),
|
| + client->crash_exception_information_address(),
|
| + client->debug_critical_section_address());
|
| +
|
| + TerminateProcess(client->process(), exit_code);
|
| +}
|
| +
|
| +// static
|
| +void __stdcall ExceptionHandlerServer::OnNonCrashDumpEvent(void* ctx, BOOLEAN) {
|
| + // This function is executed on the thread pool.
|
| + internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
|
| + base::AutoLock lock(*client->lock());
|
| +
|
| + // Capture the exception.
|
| + client->delegate()->ExceptionHandlerServerException(
|
| + client->process(),
|
| + client->non_crash_exception_information_address(),
|
| + client->debug_critical_section_address());
|
| +
|
| + bool result = !!SetEvent(client->non_crash_dump_completed_event());
|
| + PLOG_IF(ERROR, !result) << "SetEvent";
|
| +}
|
| +
|
| +// static
|
| +void __stdcall ExceptionHandlerServer::OnProcessEnd(void* ctx, BOOLEAN) {
|
| + // This function is executed on the thread pool.
|
| + internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
|
| + base::AutoLock lock(*client->lock());
|
| +
|
| + // Post back to the main thread to have it delete this client record.
|
| + PostQueuedCompletionStatus(client->port(), 0, ULONG_PTR(client), nullptr);
|
| +}
|
| +
|
| +} // namespace crashpad
|
|
|