mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-14 01:08:01 +08:00
win: Start crashpad_handler by inheriting connection data to it
Previously, StartHandler() launched the handler process, then connected over a pipe to register for crash handling. Instead, the initial client can create and inherit handles to the handler and pass those handle values and other data (addresses, etc.) on the command line. This should improve startup time as there's no need to synchronize with the process at startup, and allows avoiding a call to CreateProcess() directly in StartHandler(), which is important for registration for crash reporting from DllMain(). Incidentally adds new utility functions for string/number conversion and string splitting. Note: API change; UseHandler() is removed for all platforms. BUG=chromium:567850,chromium:656800 Change-Id: I1602724183cb107f805f109674c53e95841b24fd Reviewed-on: https://chromium-review.googlesource.com/400015 Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
1e6dbcb300
commit
2d87606bb5
@ -29,6 +29,7 @@
|
||||
#include "base/mac/scoped_mach_port.h"
|
||||
#elif defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#include "util/win/scoped_handle.h"
|
||||
#endif
|
||||
|
||||
namespace crashpad {
|
||||
@ -43,14 +44,32 @@ class CrashpadClient {
|
||||
//! \brief Starts a Crashpad handler process, performing any necessary
|
||||
//! handshake to configure it.
|
||||
//!
|
||||
//! This method does not actually direct any crashes to the Crashpad handler,
|
||||
//! because there are alternative ways to use an existing Crashpad handler. To
|
||||
//! begin directing crashes to the handler started by this method, call
|
||||
//! UseHandler() after this method returns successfully.
|
||||
//! This method directs crashes to the Crashpad handler. On Mac OS X, this
|
||||
//! is applicable to this process and all child processes. On Windows, child
|
||||
//! processes must also register by using SetHandlerIPCPipe().
|
||||
//!
|
||||
//! On Mac OS X, this method starts a Crashpad handler and obtains a Mach
|
||||
//! send right corresponding to a receive right held by the handler process.
|
||||
//! The handler process runs an exception server on this port.
|
||||
//! On Mac OS X, this method starts a Crashpad handler and obtains a Mach send
|
||||
//! right corresponding to a receive right held by the handler process. The
|
||||
//! handler process runs an exception server on this port. This method sets
|
||||
//! the task’s exception port for `EXC_CRASH`, `EXC_RESOURCE`, and `EXC_GUARD`
|
||||
//! exceptions to the Mach send right obtained. The handler will be installed
|
||||
//! with behavior `EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES` and thread
|
||||
//! state flavor `MACHINE_THREAD_STATE`. Exception ports are inherited, so a
|
||||
//! Crashpad handler started here will remain the handler for any child
|
||||
//! processes created after StartHandler() is called. Child processes do not
|
||||
//! need to call StartHandler() or be aware of Crashpad in any way. The
|
||||
//! Crashpad handler will receive crashes from child processes that have
|
||||
//! inherited it as their exception handler even after the process that called
|
||||
//! StartHandler() exits.
|
||||
//!
|
||||
//! On Windows, if \a asynchronous_start is `true`, this function will not
|
||||
//! directly call `CreateProcess()`, making it suitable for use in a
|
||||
//! `DllMain()`. In that case, the handler is started from a background
|
||||
//! thread, deferring the handler's startup. Nevertheless, regardless of the
|
||||
//! value of \a asynchronous_start, after calling this method, the global
|
||||
//! unhandled exception filter is set up, and all crashes will be handled by
|
||||
//! Crashpad. Optionally, use WaitForHandlerStart() to join with the
|
||||
//! background thread and retrieve the status of handler startup.
|
||||
//!
|
||||
//! \param[in] handler The path to a Crashpad handler executable.
|
||||
//! \param[in] database The path to a Crashpad database. The handler will be
|
||||
@ -71,6 +90,10 @@ class CrashpadClient {
|
||||
//! dies, if this behavior is supported. This option is not available on
|
||||
//! all platforms, and does not function on all OS versions. If it is
|
||||
//! not supported, it will be ignored.
|
||||
//! \param[out] asynchronous_start If `true`, the handler will be started from
|
||||
//! a background thread. Optionally, WaitForHandlerStart() can be used at
|
||||
//! a suitable time to retreive the result of background startup. This
|
||||
//! option is only used on Windows.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
bool StartHandler(const base::FilePath& handler,
|
||||
@ -79,19 +102,18 @@ class CrashpadClient {
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
bool restartable);
|
||||
bool restartable,
|
||||
bool asynchronous_start);
|
||||
|
||||
#if defined(OS_MACOSX) || DOXYGEN
|
||||
//! \brief Sets the process’ crash handler to a Mach service registered with
|
||||
//! the bootstrap server.
|
||||
//!
|
||||
//! This method does not actually direct any crashes to the Crashpad handler,
|
||||
//! because there are alternative ways to start or use an existing Crashpad
|
||||
//! handler. To begin directing crashes to the handler set by this method,
|
||||
//! call UseHandler() after this method returns successfully.
|
||||
//!
|
||||
//! This method is only defined on OS X.
|
||||
//!
|
||||
//! See StartHandler() for more detail on how the port and handler are
|
||||
//! configured.
|
||||
//!
|
||||
//! \param[in] service_name The service name of a Crashpad exception handler
|
||||
//! service previously registered with the bootstrap server.
|
||||
//!
|
||||
@ -100,27 +122,30 @@ class CrashpadClient {
|
||||
|
||||
//! \brief Sets the process’ crash handler to a Mach port.
|
||||
//!
|
||||
//! This method does not actually direct any crashes to the Crashpad handler,
|
||||
//! because there are alternative ways to start or use an existing Crashpad
|
||||
//! handler. To begin directing crashes to the handler set by this method,
|
||||
//! call UseHandler() after this method.
|
||||
//!
|
||||
//! This method is only defined on OS X.
|
||||
//!
|
||||
//! See StartHandler() for more detail on how the port and handler are
|
||||
//! configured.
|
||||
//!
|
||||
//! \param[in] exception_port An `exception_port_t` corresponding to a
|
||||
//! Crashpad exception handler service.
|
||||
void SetHandlerMachPort(base::mac::ScopedMachSendRight exception_port);
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
bool SetHandlerMachPort(base::mac::ScopedMachSendRight exception_port);
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN) || DOXYGEN
|
||||
//! \brief Sets the IPC pipe of a presumably-running Crashpad handler process
|
||||
//! which was started with StartHandler() or by other compatible means
|
||||
//! and does an IPC message exchange to register this process with the
|
||||
//! handler. However, just like StartHandler(), crashes are not serviced
|
||||
//! until UseHandler() is called.
|
||||
//! handler. Crashes will be serviced once this method returns.
|
||||
//!
|
||||
//! This method is only defined on Windows.
|
||||
//!
|
||||
//! This method sets the unhandled exception handler to a local
|
||||
//! function that when reached will "signal and wait" for the crash handler
|
||||
//! process to create the dump.
|
||||
//!
|
||||
//! \param[in] ipc_pipe The full name of the crash handler IPC pipe. This is
|
||||
//! a string of the form `"\\.\pipe\NAME"`.
|
||||
//!
|
||||
@ -142,6 +167,16 @@ class CrashpadClient {
|
||||
//! `"\\.\pipe\NAME"`.
|
||||
std::wstring GetHandlerIPCPipe() const;
|
||||
|
||||
//! \brief When `asynchronous_start` is used with StartHandler(), this method
|
||||
//! can be used to block until the handler launch has been completed to
|
||||
//! retrieve status information.
|
||||
//!
|
||||
//! This method should not be used unless `asynchronous_start` was `true`.
|
||||
//!
|
||||
//! \return `true` if the hander startup succeeded, `false` otherwise, and an
|
||||
//! error message will have been logged.
|
||||
bool WaitForHandlerStart();
|
||||
|
||||
//! \brief Requests that the handler capture a dump even though there hasn't
|
||||
//! been a crash.
|
||||
//!
|
||||
@ -153,7 +188,7 @@ class CrashpadClient {
|
||||
//! exception_pointers to get the `EXCEPTION_RECORD` and `CONTEXT`.
|
||||
//!
|
||||
//! This function is not necessary in general usage as an unhandled exception
|
||||
//! filter is installed by UseHandler().
|
||||
//! filter is installed by StartHandler() or SetHandlerIPCPipe().
|
||||
//!
|
||||
//! \param[in] exception_pointers An `EXCEPTION_POINTERS`, as would generally
|
||||
//! passed to an unhandled exception filter.
|
||||
@ -195,44 +230,21 @@ class CrashpadClient {
|
||||
};
|
||||
#endif
|
||||
|
||||
//! \brief Configures the process to direct its crashes to a Crashpad handler.
|
||||
//!
|
||||
//! The Crashpad handler must previously have been started by StartHandler()
|
||||
//! or configured by SetHandlerMachService(), SetHandlerMachPort(), or
|
||||
//! SetHandlerIPCPipe().
|
||||
//!
|
||||
//! On Mac OS X, this method sets the task’s exception port for `EXC_CRASH`,
|
||||
//! `EXC_RESOURCE`, and `EXC_GUARD` exceptions to the Mach send right obtained
|
||||
//! by StartHandler(). The handler will be installed with behavior
|
||||
//! `EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES` and thread state flavor
|
||||
//! `MACHINE_THREAD_STATE`. Exception ports are inherited, so a Crashpad
|
||||
//! handler chosen by UseHandler() will remain the handler for any child
|
||||
//! processes created after UseHandler() is called. Child processes do not
|
||||
//! need to call StartHandler() or UseHandler() or be aware of Crashpad in any
|
||||
//! way. The Crashpad handler will receive crashes from child processes that
|
||||
//! have inherited it as their exception handler even after the process that
|
||||
//! called StartHandler() exits.
|
||||
//!
|
||||
//! On Windows, this method sets the unhandled exception handler to a local
|
||||
//! function that when reached will "signal and wait" for the crash handler
|
||||
//! process to create the dump.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
bool UseHandler();
|
||||
|
||||
#if defined(OS_MACOSX) || DOXYGEN
|
||||
//! \brief Configures the process to direct its crashes to the default handler
|
||||
//! for the operating system.
|
||||
//!
|
||||
//! On OS X, this sets the task’s exception port as in UseHandler(), but the
|
||||
//! exception handler used is obtained from SystemCrashReporterHandler(). If
|
||||
//! the system’s crash reporter handler cannot be determined or set, the
|
||||
//! task’s exception ports for crash-type exceptions are cleared.
|
||||
//! On OS X, this sets the task’s exception port as in SetHandlerMachPort(),
|
||||
//! but the exception handler used is obtained from
|
||||
//! SystemCrashReporterHandler(). If the system’s crash reporter handler
|
||||
//! cannot be determined or set, the task’s exception ports for crash-type
|
||||
//! exceptions are cleared.
|
||||
//!
|
||||
//! Use of this function is strongly discouraged.
|
||||
//!
|
||||
//! \warning After a call to this function, Crashpad will no longer monitor
|
||||
//! the process for crashes until a subsequent call to UseHandler().
|
||||
//! the process for crashes until a subsequent call to
|
||||
//! SetHandlerMachPort().
|
||||
//!
|
||||
//! \note This is provided as a static function to allow it to be used in
|
||||
//! situations where a CrashpadClient object is not otherwise available.
|
||||
@ -242,10 +254,9 @@ class CrashpadClient {
|
||||
#endif
|
||||
|
||||
private:
|
||||
#if defined(OS_MACOSX)
|
||||
base::mac::ScopedMachSendRight exception_port_;
|
||||
#elif defined(OS_WIN)
|
||||
#if defined(OS_WIN)
|
||||
std::wstring ipc_pipe_;
|
||||
ScopedKernelHANDLE handler_start_thread_;
|
||||
#endif
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashpadClient);
|
||||
|
@ -523,8 +523,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface {
|
||||
|
||||
} // namespace
|
||||
|
||||
CrashpadClient::CrashpadClient()
|
||||
: exception_port_() {
|
||||
CrashpadClient::CrashpadClient() {
|
||||
}
|
||||
|
||||
CrashpadClient::~CrashpadClient() {
|
||||
@ -537,9 +536,8 @@ bool CrashpadClient::StartHandler(
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
bool restartable) {
|
||||
DCHECK(!exception_port_.is_valid());
|
||||
|
||||
bool restartable,
|
||||
bool asynchronous_start) {
|
||||
// The “restartable” behavior can only be selected on OS X 10.10 and later. In
|
||||
// previous OS versions, if the initial client were to crash while attempting
|
||||
// to restart the handler, it would become an unkillable process.
|
||||
@ -569,16 +567,10 @@ bool CrashpadClient::SetHandlerMachService(const std::string& service_name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CrashpadClient::SetHandlerMachPort(
|
||||
bool CrashpadClient::SetHandlerMachPort(
|
||||
base::mac::ScopedMachSendRight exception_port) {
|
||||
DCHECK(exception_port.is_valid());
|
||||
exception_port_ = std::move(exception_port);
|
||||
}
|
||||
|
||||
bool CrashpadClient::UseHandler() {
|
||||
DCHECK(exception_port_.is_valid());
|
||||
|
||||
return SetCrashExceptionPorts(exception_port_.get());
|
||||
return SetCrashExceptionPorts(exception_port.get());
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -28,18 +28,22 @@
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/misc/random_string.h"
|
||||
#include "util/win/address_types.h"
|
||||
#include "util/win/command_line.h"
|
||||
#include "util/win/critical_section_with_debug_info.h"
|
||||
#include "util/win/get_function.h"
|
||||
#include "util/win/handle.h"
|
||||
#include "util/win/initial_client_data.h"
|
||||
#include "util/win/nt_internals.h"
|
||||
#include "util/win/ntstatus_logging.h"
|
||||
#include "util/win/process_info.h"
|
||||
#include "util/win/registration_protocol_win.h"
|
||||
#include "util/win/scoped_handle.h"
|
||||
#include "util/win/scoped_process_suspend.h"
|
||||
#include "util/win/termination_codes.h"
|
||||
#include "util/win/xp_compat.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
@ -48,7 +52,7 @@ namespace {
|
||||
HANDLE g_signal_exception = INVALID_HANDLE_VALUE;
|
||||
|
||||
// Where we store the exception information that the crash handler reads.
|
||||
crashpad::ExceptionInformation g_crash_exception_information;
|
||||
ExceptionInformation g_crash_exception_information;
|
||||
|
||||
// These handles are never closed. g_signal_non_crash_dump is used to signal to
|
||||
// the server to take a dump (not due to an exception), and the server will
|
||||
@ -61,7 +65,7 @@ base::Lock* g_non_crash_dump_lock;
|
||||
|
||||
// Where we store a pointer to the context information when taking a non-crash
|
||||
// dump.
|
||||
crashpad::ExceptionInformation g_non_crash_exception_information;
|
||||
ExceptionInformation g_non_crash_exception_information;
|
||||
|
||||
// A CRITICAL_SECTION initialized with
|
||||
// RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO to force it to be allocated with a
|
||||
@ -94,7 +98,7 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
||||
// signal the crash handler.
|
||||
g_crash_exception_information.thread_id = GetCurrentThreadId();
|
||||
g_crash_exception_information.exception_pointers =
|
||||
reinterpret_cast<crashpad::WinVMAddress>(exception_pointers);
|
||||
reinterpret_cast<WinVMAddress>(exception_pointers);
|
||||
|
||||
// Now signal the crash server, which will take a dump and then terminate us
|
||||
// when it's complete.
|
||||
@ -110,7 +114,7 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
||||
|
||||
LOG(ERROR) << "crash server did not respond, self-terminating";
|
||||
|
||||
TerminateProcess(GetCurrentProcess(), crashpad::kTerminationCodeCrashNoDump);
|
||||
TerminateProcess(GetCurrentProcess(), kTerminationCodeCrashNoDump);
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
@ -121,9 +125,7 @@ std::wstring FormatArgumentString(const std::string& name,
|
||||
}
|
||||
|
||||
struct ScopedProcThreadAttributeListTraits {
|
||||
static PPROC_THREAD_ATTRIBUTE_LIST InvalidValue() {
|
||||
return nullptr;
|
||||
}
|
||||
static PPROC_THREAD_ATTRIBUTE_LIST InvalidValue() { return nullptr; }
|
||||
|
||||
static void Free(PPROC_THREAD_ATTRIBUTE_LIST proc_thread_attribute_list) {
|
||||
// This is able to use GET_FUNCTION_REQUIRED() instead of GET_FUNCTION()
|
||||
@ -186,74 +188,113 @@ void AddUint64(std::vector<unsigned char>* data_vector, uint64_t data) {
|
||||
static_cast<uint32_t>((data & 0xffffffff00000000ULL) >> 32));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
//! \brief Creates a randomized pipe name to listen for client registrations
|
||||
//! on and returns its name.
|
||||
//!
|
||||
//! \param[out] pipe_name The pipe name that will be listened on.
|
||||
//! \param[out] pipe_handle The first pipe instance corresponding for the pipe.
|
||||
void CreatePipe(std::wstring* pipe_name, ScopedFileHANDLE* pipe_instance) {
|
||||
int tries = 5;
|
||||
std::string pipe_name_base =
|
||||
base::StringPrintf("\\\\.\\pipe\\crashpad_%d_", GetCurrentProcessId());
|
||||
do {
|
||||
*pipe_name = base::UTF8ToUTF16(pipe_name_base + RandomString());
|
||||
|
||||
namespace crashpad {
|
||||
pipe_instance->reset(CreateNamedPipeInstance(*pipe_name, true));
|
||||
|
||||
CrashpadClient::CrashpadClient()
|
||||
: ipc_pipe_() {
|
||||
// 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 (!pipe_instance->is_valid() && --tries);
|
||||
|
||||
PCHECK(pipe_instance->is_valid()) << "CreateNamedPipe";
|
||||
}
|
||||
|
||||
CrashpadClient::~CrashpadClient() {
|
||||
}
|
||||
struct BackgroundHandlerStartThreadData {
|
||||
BackgroundHandlerStartThreadData(
|
||||
const base::FilePath& handler,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
ScopedFileHANDLE ipc_pipe_handle)
|
||||
: handler(handler),
|
||||
database(database),
|
||||
metrics_dir(metrics_dir),
|
||||
url(url),
|
||||
annotations(annotations),
|
||||
arguments(arguments),
|
||||
ipc_pipe_handle(std::move(ipc_pipe_handle)) {}
|
||||
|
||||
bool CrashpadClient::StartHandler(
|
||||
const base::FilePath& handler,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
bool restartable) {
|
||||
DCHECK(ipc_pipe_.empty());
|
||||
|
||||
HANDLE pipe_read;
|
||||
HANDLE pipe_write;
|
||||
SECURITY_ATTRIBUTES security_attributes = {};
|
||||
security_attributes.nLength = sizeof(security_attributes);
|
||||
security_attributes.bInheritHandle = TRUE;
|
||||
if (!CreatePipe(&pipe_read, &pipe_write, &security_attributes, 0)) {
|
||||
PLOG(ERROR) << "CreatePipe";
|
||||
return false;
|
||||
}
|
||||
ScopedFileHandle pipe_read_owner(pipe_read);
|
||||
ScopedFileHandle pipe_write_owner(pipe_write);
|
||||
|
||||
// The new process only needs the write side of the pipe.
|
||||
BOOL rv = SetHandleInformation(pipe_read, HANDLE_FLAG_INHERIT, 0);
|
||||
PLOG_IF(WARNING, !rv) << "SetHandleInformation";
|
||||
base::FilePath handler;
|
||||
base::FilePath database;
|
||||
base::FilePath metrics_dir;
|
||||
std::string url;
|
||||
std::map<std::string, std::string> annotations;
|
||||
std::vector<std::string> arguments;
|
||||
ScopedFileHANDLE ipc_pipe_handle;
|
||||
};
|
||||
|
||||
bool StartHandlerProcess(
|
||||
std::unique_ptr<BackgroundHandlerStartThreadData> data) {
|
||||
std::wstring command_line;
|
||||
AppendCommandLineArgument(handler.value(), &command_line);
|
||||
for (const std::string& argument : arguments) {
|
||||
AppendCommandLineArgument(data->handler.value(), &command_line);
|
||||
for (const std::string& argument : data->arguments) {
|
||||
AppendCommandLineArgument(base::UTF8ToUTF16(argument), &command_line);
|
||||
}
|
||||
if (!database.value().empty()) {
|
||||
AppendCommandLineArgument(FormatArgumentString("database",
|
||||
database.value()),
|
||||
&command_line);
|
||||
}
|
||||
if (!metrics_dir.value().empty()) {
|
||||
if (!data->database.value().empty()) {
|
||||
AppendCommandLineArgument(
|
||||
FormatArgumentString("metrics-dir", metrics_dir.value()),
|
||||
FormatArgumentString("database", data->database.value()),
|
||||
&command_line);
|
||||
}
|
||||
if (!url.empty()) {
|
||||
AppendCommandLineArgument(FormatArgumentString("url",
|
||||
base::UTF8ToUTF16(url)),
|
||||
&command_line);
|
||||
if (!data->metrics_dir.value().empty()) {
|
||||
AppendCommandLineArgument(
|
||||
FormatArgumentString("metrics-dir", data->metrics_dir.value()),
|
||||
&command_line);
|
||||
}
|
||||
for (const auto& kv : annotations) {
|
||||
if (!data->url.empty()) {
|
||||
AppendCommandLineArgument(
|
||||
FormatArgumentString("url", base::UTF8ToUTF16(data->url)),
|
||||
&command_line);
|
||||
}
|
||||
for (const auto& kv : data->annotations) {
|
||||
AppendCommandLineArgument(
|
||||
FormatArgumentString("annotation",
|
||||
base::UTF8ToUTF16(kv.first + '=' + kv.second)),
|
||||
&command_line);
|
||||
}
|
||||
|
||||
ScopedKernelHANDLE this_process(
|
||||
OpenProcess(kXPProcessAllAccess, true, GetCurrentProcessId()));
|
||||
if (!this_process.is_valid()) {
|
||||
PLOG(ERROR) << "OpenProcess";
|
||||
return false;
|
||||
}
|
||||
|
||||
InitialClientData initial_client_data(
|
||||
g_signal_exception,
|
||||
g_signal_non_crash_dump,
|
||||
g_non_crash_dump_done,
|
||||
data->ipc_pipe_handle.get(),
|
||||
this_process.get(),
|
||||
reinterpret_cast<WinVMAddress>(&g_crash_exception_information),
|
||||
reinterpret_cast<WinVMAddress>(&g_non_crash_exception_information),
|
||||
reinterpret_cast<WinVMAddress>(&g_critical_section_with_debug_info));
|
||||
AppendCommandLineArgument(
|
||||
base::UTF8ToUTF16(base::StringPrintf("--handshake-handle=0x%x",
|
||||
HandleToInt(pipe_write))),
|
||||
base::UTF8ToUTF16(std::string("--initial-client-data=") +
|
||||
initial_client_data.StringRepresentation()),
|
||||
&command_line);
|
||||
|
||||
BOOL rv;
|
||||
DWORD creation_flags;
|
||||
STARTUPINFOEX startup_info = {};
|
||||
startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
@ -304,8 +345,12 @@ bool CrashpadClient::StartHandler(
|
||||
}
|
||||
proc_thread_attribute_list_owner.reset(startup_info.lpAttributeList);
|
||||
|
||||
handle_list.reserve(4);
|
||||
handle_list.push_back(pipe_write);
|
||||
handle_list.reserve(8);
|
||||
handle_list.push_back(g_signal_exception);
|
||||
handle_list.push_back(g_signal_non_crash_dump);
|
||||
handle_list.push_back(g_non_crash_dump_done);
|
||||
handle_list.push_back(data->ipc_pipe_handle.get());
|
||||
handle_list.push_back(this_process.get());
|
||||
AddHandleToListIfValidAndInheritable(&handle_list,
|
||||
startup_info.StartupInfo.hStdInput);
|
||||
AddHandleToListIfValidAndInheritable(&handle_list,
|
||||
@ -327,7 +372,7 @@ bool CrashpadClient::StartHandler(
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION process_info;
|
||||
rv = CreateProcess(handler.value().c_str(),
|
||||
rv = CreateProcess(data->handler.value().c_str(),
|
||||
&command_line[0],
|
||||
nullptr,
|
||||
nullptr,
|
||||
@ -348,38 +393,109 @@ bool CrashpadClient::StartHandler(
|
||||
rv = CloseHandle(process_info.hProcess);
|
||||
PLOG_IF(WARNING, !rv) << "CloseHandle process";
|
||||
|
||||
pipe_write_owner.reset();
|
||||
|
||||
uint32_t ipc_pipe_length;
|
||||
if (!LoggingReadFile(pipe_read, &ipc_pipe_length, sizeof(ipc_pipe_length))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ipc_pipe_.resize(ipc_pipe_length);
|
||||
if (ipc_pipe_length &&
|
||||
!LoggingReadFile(
|
||||
pipe_read, &ipc_pipe_[0], ipc_pipe_length * sizeof(ipc_pipe_[0]))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD WINAPI BackgroundHandlerStartThreadProc(void* data) {
|
||||
std::unique_ptr<BackgroundHandlerStartThreadData> data_as_ptr(
|
||||
reinterpret_cast<BackgroundHandlerStartThreadData*>(data));
|
||||
return StartHandlerProcess(std::move(data_as_ptr)) ? 0 : 1;
|
||||
}
|
||||
|
||||
void CommonInProcessInitialization() {
|
||||
// We create this dummy CRITICAL_SECTION with the
|
||||
// RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO flag set to have an entry point
|
||||
// into the doubly-linked list of RTL_CRITICAL_SECTION_DEBUG objects. This
|
||||
// allows us to walk the list at crash time to gather data for !locks. A
|
||||
// debugger would instead inspect ntdll!RtlCriticalSectionList to get the head
|
||||
// of the list. But that is not an exported symbol, so on an arbitrary client
|
||||
// machine, we don't have a way of getting that pointer.
|
||||
InitializeCriticalSectionWithDebugInfoIfPossible(
|
||||
&g_critical_section_with_debug_info);
|
||||
|
||||
g_non_crash_dump_lock = new base::Lock();
|
||||
|
||||
// In theory we could store the previous handler but it is not clear what
|
||||
// use we have for it.
|
||||
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CrashpadClient::CrashpadClient() : ipc_pipe_(), handler_start_thread_() {}
|
||||
|
||||
CrashpadClient::~CrashpadClient() {}
|
||||
|
||||
bool CrashpadClient::StartHandler(
|
||||
const base::FilePath& handler,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
bool restartable,
|
||||
bool asynchronous_start) {
|
||||
DCHECK(ipc_pipe_.empty());
|
||||
|
||||
// Both the pipe and the signalling events have to be created on the main
|
||||
// thread (not the spawning thread) so that they're valid after we return from
|
||||
// this function.
|
||||
ScopedFileHANDLE ipc_pipe_handle;
|
||||
CreatePipe(&ipc_pipe_, &ipc_pipe_handle);
|
||||
|
||||
SECURITY_ATTRIBUTES security_attributes = {0};
|
||||
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
security_attributes.bInheritHandle = true;
|
||||
|
||||
g_signal_exception =
|
||||
CreateEvent(&security_attributes, false /* auto reset */, false, nullptr);
|
||||
g_signal_non_crash_dump =
|
||||
CreateEvent(&security_attributes, false /* auto reset */, false, nullptr);
|
||||
g_non_crash_dump_done =
|
||||
CreateEvent(&security_attributes, false /* auto reset */, false, nullptr);
|
||||
|
||||
CommonInProcessInitialization();
|
||||
|
||||
auto data = new BackgroundHandlerStartThreadData(handler,
|
||||
database,
|
||||
metrics_dir,
|
||||
url,
|
||||
annotations,
|
||||
arguments,
|
||||
std::move(ipc_pipe_handle));
|
||||
|
||||
if (asynchronous_start) {
|
||||
// It is important that the current thread not be synchronized with the
|
||||
// thread that is created here. StartHandler() needs to be callable inside a
|
||||
// DllMain(). In that case, the background thread will not start until the
|
||||
// current DllMain() completes, which would cause deadlock if it was waited
|
||||
// upon.
|
||||
handler_start_thread_.reset(CreateThread(nullptr,
|
||||
0,
|
||||
&BackgroundHandlerStartThreadProc,
|
||||
reinterpret_cast<void*>(data),
|
||||
0,
|
||||
nullptr));
|
||||
if (!handler_start_thread_.is_valid()) {
|
||||
PLOG(ERROR) << "CreateThread";
|
||||
return false;
|
||||
}
|
||||
|
||||
// In asynchronous mode, we can't report on the overall success or failure
|
||||
// of initialization at this point.
|
||||
return true;
|
||||
} else {
|
||||
return StartHandlerProcess(
|
||||
std::unique_ptr<BackgroundHandlerStartThreadData>(data));
|
||||
}
|
||||
}
|
||||
|
||||
bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) {
|
||||
DCHECK(ipc_pipe_.empty());
|
||||
DCHECK(!ipc_pipe.empty());
|
||||
|
||||
ipc_pipe_ = ipc_pipe;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring CrashpadClient::GetHandlerIPCPipe() const {
|
||||
DCHECK(!ipc_pipe_.empty());
|
||||
return ipc_pipe_;
|
||||
}
|
||||
|
||||
bool CrashpadClient::UseHandler() {
|
||||
DCHECK(!ipc_pipe_.empty());
|
||||
DCHECK_EQ(g_signal_exception, INVALID_HANDLE_VALUE);
|
||||
DCHECK_EQ(g_signal_non_crash_dump, INVALID_HANDLE_VALUE);
|
||||
@ -397,18 +513,10 @@ bool CrashpadClient::UseHandler() {
|
||||
message.registration.non_crash_exception_information =
|
||||
reinterpret_cast<WinVMAddress>(&g_non_crash_exception_information);
|
||||
|
||||
// We create this dummy CRITICAL_SECTION with the
|
||||
// RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO flag set to have an entry point
|
||||
// into the doubly-linked list of RTL_CRITICAL_SECTION_DEBUG objects. This
|
||||
// allows us to walk the list at crash time to gather data for !locks. A
|
||||
// debugger would instead inspect ntdll!RtlCriticalSectionList to get the head
|
||||
// of the list. But that is not an exported symbol, so on an arbitrary client
|
||||
// machine, we don't have a way of getting that pointer.
|
||||
if (InitializeCriticalSectionWithDebugInfoIfPossible(
|
||||
&g_critical_section_with_debug_info)) {
|
||||
message.registration.critical_section_address =
|
||||
reinterpret_cast<WinVMAddress>(&g_critical_section_with_debug_info);
|
||||
}
|
||||
CommonInProcessInitialization();
|
||||
|
||||
message.registration.critical_section_address =
|
||||
reinterpret_cast<WinVMAddress>(&g_critical_section_with_debug_info);
|
||||
|
||||
ServerToClientMessage response = {};
|
||||
|
||||
@ -424,14 +532,32 @@ bool CrashpadClient::UseHandler() {
|
||||
g_non_crash_dump_done =
|
||||
IntToHandle(response.registration.non_crash_dump_completed_event);
|
||||
|
||||
g_non_crash_dump_lock = new base::Lock();
|
||||
|
||||
// In theory we could store the previous handler but it is not clear what
|
||||
// use we have for it.
|
||||
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring CrashpadClient::GetHandlerIPCPipe() const {
|
||||
DCHECK(!ipc_pipe_.empty());
|
||||
return ipc_pipe_;
|
||||
}
|
||||
|
||||
bool CrashpadClient::WaitForHandlerStart() {
|
||||
DCHECK(handler_start_thread_.is_valid());
|
||||
if (WaitForSingleObject(handler_start_thread_.get(), INFINITE) !=
|
||||
WAIT_OBJECT_0) {
|
||||
PLOG(ERROR) << "WaitForSingleObject";
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD exit_code;
|
||||
if (!GetExitCodeThread(handler_start_thread_.get(), &exit_code)) {
|
||||
PLOG(ERROR) << "GetExitCodeThread";
|
||||
return false;
|
||||
}
|
||||
|
||||
handler_start_thread_.reset();
|
||||
return exit_code == 0;
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) {
|
||||
if (g_signal_non_crash_dump == INVALID_HANDLE_VALUE ||
|
||||
@ -475,7 +601,7 @@ void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) {
|
||||
|
||||
g_non_crash_exception_information.thread_id = GetCurrentThreadId();
|
||||
g_non_crash_exception_information.exception_pointers =
|
||||
reinterpret_cast<crashpad::WinVMAddress>(&exception_pointers);
|
||||
reinterpret_cast<WinVMAddress>(&exception_pointers);
|
||||
|
||||
bool set_event_result = !!SetEvent(g_signal_non_crash_dump);
|
||||
PLOG_IF(ERROR, !set_event_result) << "SetEvent";
|
||||
|
@ -35,8 +35,9 @@ void StartAndUseHandler() {
|
||||
"",
|
||||
std::map<std::string, std::string>(),
|
||||
std::vector<std::string>(),
|
||||
false));
|
||||
EXPECT_TRUE(client.UseHandler());
|
||||
true,
|
||||
true));
|
||||
ASSERT_TRUE(client.WaitForHandlerStart());
|
||||
}
|
||||
|
||||
class StartWithInvalidHandles final : public WinMultiprocess {
|
||||
|
@ -51,14 +51,17 @@ when it determines that the server should exit.
|
||||
|
||||
On Windows, clients register with this server by communicating with it via the
|
||||
named pipe identified by the *--pipe-name* argument. Alternatively, the server
|
||||
can create a new pipe with a random name and inform a client of this name via
|
||||
the *--handshake-handle* mechanism; clients may then register by communicating
|
||||
with it via that named pipe. During registration, a client provides the server
|
||||
with an OS event object that it will signal should it crash. The server obtains
|
||||
the client’s process handle and waits on the crash event object for a crash, as
|
||||
well as the client’s process handle for the client to exit cleanly without
|
||||
crashing. When a server started via the *--handshake-handle* mechanism loses all
|
||||
of its clients, it exits after allowing any upload in progress to complete.
|
||||
can inherit an already-created pipe from a parent process by using the
|
||||
*--initial-client-data* mechanism. That argument also takes all of the arguments
|
||||
that would normally be passed in a registration message, and so constitutes
|
||||
registration of the first client. Subsequent clients may then register by
|
||||
communicating with the server via the pipe. During registration, a client
|
||||
provides the server with an OS event object that it will signal should it crash.
|
||||
The server obtains the client’s process handle and waits on the crash event
|
||||
object for a crash, as well as the client’s process handle for the client to
|
||||
exit cleanly without crashing. When a server started via the
|
||||
*--initial-client-data* mechanism loses all of its clients, it exits after
|
||||
allowing any upload in progress to complete.
|
||||
|
||||
On Windows, this executable is built by default as a Windows GUI app, so no
|
||||
console will appear in normal usage. This is the version that will typically be
|
||||
@ -107,10 +110,11 @@ Perform the handshake with the initial client on the file descriptor at 'FD'.
|
||||
Either this option or *--mach-service*, but not both, is required. This option
|
||||
is only valid on OS X.
|
||||
|
||||
*--handshake-handle*='HANDLE'::
|
||||
Perform the handshake with the initial client on the HANDLE at 'HANDLE'. Either
|
||||
this option or *--pipe-name*, but not both, is required. This option is only
|
||||
valid on Windows.
|
||||
*--initial-client-data*='HANDLE_request_crash_dump','HANDLE_request_non_crash_dump','HANDLE_non_crash_dump_completed','HANDLE_first_pipe_instance','HANDLE_client_process','Address_crash_exception_information','Address_non_crash_exception_information','Address_debug_critical_section'::
|
||||
Register the initial client using the inherited handles and data provided. For
|
||||
more information on the arguments, see the implementations of +CrashpadClient+
|
||||
and +ExceptionHandlerServer+. Either this option or *--pipe-name*, but not both,
|
||||
is required. This option is only valid on Windows.
|
||||
+
|
||||
When this option is present, the server creates a new named pipe at a random
|
||||
name and informs its client of the name. The server waits for at least one
|
||||
@ -134,8 +138,8 @@ attempt to upload all captured reports.
|
||||
|
||||
*--pipe-name*='PIPE'::
|
||||
Listen on the given pipe name for connections from clients. 'PIPE' must be of
|
||||
the form +\\.\pipe\<somename>+. Either this option or *--handshake-handle*, but
|
||||
not both, is required. This option is only valid on Windows.
|
||||
the form +\\.\pipe\<somename>+. Either this option or *--initial-client-data*,
|
||||
but not both, is required. This option is only valid on Windows.
|
||||
+
|
||||
When this option is present, the server creates a named pipe at 'PIPE', a name
|
||||
known to both the server and its clients. The server continues running even
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "handler/win/crash_report_exception_handler.h"
|
||||
#include "util/win/exception_handler_server.h"
|
||||
#include "util/win/handle.h"
|
||||
#include "util/win/initial_client_data.h"
|
||||
#endif // OS_MACOSX
|
||||
|
||||
namespace crashpad {
|
||||
@ -76,8 +77,15 @@ void Usage(const base::FilePath& me) {
|
||||
" --handshake-fd=FD establish communication with the client over FD\n"
|
||||
" --mach-service=SERVICE register SERVICE with the bootstrap server\n"
|
||||
#elif defined(OS_WIN)
|
||||
" --handshake-handle=HANDLE\n"
|
||||
" create a new pipe and send its name via HANDLE\n"
|
||||
" --initial-client-data=HANDLE_request_crash_dump,\n"
|
||||
" HANDLE_request_non_crash_dump,\n"
|
||||
" HANDLE_non_crash_dump_completed,\n"
|
||||
" HANDLE_pipe,\n"
|
||||
" HANDLE_client_process,\n"
|
||||
" Address_crash_exception_information,\n"
|
||||
" Address_non_crash_exception_information,\n"
|
||||
" Address_debug_critical_section\n"
|
||||
" use precreated data to register initial client\n"
|
||||
#endif // OS_MACOSX
|
||||
" --metrics-dir=DIR store metrics files in DIR (only in Chromium)\n"
|
||||
" --no-rate-limit don't rate limit crash uploads\n"
|
||||
@ -125,7 +133,7 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
||||
Metrics::HandlerCrashed(exception_pointers->ExceptionRecord->ExceptionCode);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
#endif
|
||||
#endif // OS_WIN
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -145,9 +153,12 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
kOptionDatabase,
|
||||
#if defined(OS_MACOSX)
|
||||
kOptionHandshakeFD,
|
||||
#endif // OS_MACOSX
|
||||
#if defined(OS_WIN)
|
||||
kOptionInitialClientData,
|
||||
#endif // OS_WIN
|
||||
#if defined(OS_MACOSX)
|
||||
kOptionMachService,
|
||||
#elif defined(OS_WIN)
|
||||
kOptionHandshakeHandle,
|
||||
#endif // OS_MACOSX
|
||||
kOptionMetrics,
|
||||
kOptionNoRateLimit,
|
||||
@ -173,41 +184,45 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
std::string mach_service;
|
||||
bool reset_own_crash_exception_port_to_system_default;
|
||||
#elif defined(OS_WIN)
|
||||
HANDLE handshake_handle;
|
||||
std::string pipe_name;
|
||||
InitialClientData initial_client_data;
|
||||
#endif // OS_MACOSX
|
||||
bool rate_limit;
|
||||
} options = {};
|
||||
#if defined(OS_MACOSX)
|
||||
options.handshake_fd = -1;
|
||||
#elif defined(OS_WIN)
|
||||
options.handshake_handle = INVALID_HANDLE_VALUE;
|
||||
#endif
|
||||
options.rate_limit = true;
|
||||
|
||||
const option long_options[] = {
|
||||
{"annotation", required_argument, nullptr, kOptionAnnotation},
|
||||
{"database", required_argument, nullptr, kOptionDatabase},
|
||||
{"annotation", required_argument, nullptr, kOptionAnnotation},
|
||||
{"database", required_argument, nullptr, kOptionDatabase},
|
||||
#if defined(OS_MACOSX)
|
||||
{"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
|
||||
{"mach-service", required_argument, nullptr, kOptionMachService},
|
||||
#elif defined(OS_WIN)
|
||||
{"handshake-handle", required_argument, nullptr, kOptionHandshakeHandle},
|
||||
{"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
|
||||
#endif // OS_MACOSX
|
||||
#if defined(OS_WIN)
|
||||
{"initial-client-data",
|
||||
required_argument,
|
||||
nullptr,
|
||||
kOptionInitialClientData},
|
||||
#endif // OS_MACOSX
|
||||
{"metrics-dir", required_argument, nullptr, kOptionMetrics},
|
||||
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
|
||||
#if defined(OS_MACOSX)
|
||||
{"reset-own-crash-exception-port-to-system-default",
|
||||
no_argument,
|
||||
nullptr,
|
||||
kOptionResetOwnCrashExceptionPortToSystemDefault},
|
||||
#elif defined(OS_WIN)
|
||||
{"pipe-name", required_argument, nullptr, kOptionPipeName},
|
||||
{"mach-service", required_argument, nullptr, kOptionMachService},
|
||||
#endif // OS_MACOSX
|
||||
{"url", required_argument, nullptr, kOptionURL},
|
||||
{"help", no_argument, nullptr, kOptionHelp},
|
||||
{"version", no_argument, nullptr, kOptionVersion},
|
||||
{nullptr, 0, nullptr, 0},
|
||||
{"metrics-dir", required_argument, nullptr, kOptionMetrics},
|
||||
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
|
||||
#if defined(OS_MACOSX)
|
||||
{"reset-own-crash-exception-port-to-system-default",
|
||||
no_argument,
|
||||
nullptr,
|
||||
kOptionResetOwnCrashExceptionPortToSystemDefault},
|
||||
#elif defined(OS_WIN)
|
||||
{"pipe-name", required_argument, nullptr, kOptionPipeName},
|
||||
#endif // OS_MACOSX
|
||||
{"url", required_argument, nullptr, kOptionURL},
|
||||
{"help", no_argument, nullptr, kOptionHelp},
|
||||
{"version", no_argument, nullptr, kOptionVersion},
|
||||
{nullptr, 0, nullptr, 0},
|
||||
};
|
||||
|
||||
int opt;
|
||||
@ -216,7 +231,7 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
case kOptionAnnotation: {
|
||||
std::string key;
|
||||
std::string value;
|
||||
if (!SplitString(optarg, '=', &key, &value)) {
|
||||
if (!SplitStringFirst(optarg, '=', &key, &value)) {
|
||||
ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@ -246,17 +261,12 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
break;
|
||||
}
|
||||
#elif defined(OS_WIN)
|
||||
case kOptionHandshakeHandle: {
|
||||
// Use unsigned int, because the handle was presented by the client in
|
||||
// 0x%x format.
|
||||
unsigned int handle_uint;
|
||||
if (!StringToNumber(optarg, &handle_uint) ||
|
||||
(options.handshake_handle = IntToHandle(handle_uint)) ==
|
||||
INVALID_HANDLE_VALUE) {
|
||||
ToolSupport::UsageHint(me, "--handshake-handle requires a HANDLE");
|
||||
case kOptionInitialClientData: {
|
||||
if (!options.initial_client_data.InitializeFromString(optarg)) {
|
||||
ToolSupport::UsageHint(
|
||||
me, "failed to parse --initial-client-data");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif // OS_MACOSX
|
||||
case kOptionMetrics: {
|
||||
@ -310,15 +320,14 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#elif defined(OS_WIN)
|
||||
if (options.handshake_handle == INVALID_HANDLE_VALUE &&
|
||||
options.pipe_name.empty()) {
|
||||
ToolSupport::UsageHint(me, "--handshake-handle or --pipe-name is required");
|
||||
if (!options.initial_client_data.IsValid() && options.pipe_name.empty()) {
|
||||
ToolSupport::UsageHint(me,
|
||||
"--initial-client-data or --pipe-name is required");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (options.handshake_handle != INVALID_HANDLE_VALUE &&
|
||||
!options.pipe_name.empty()) {
|
||||
if (options.initial_client_data.IsValid() && !options.pipe_name.empty()) {
|
||||
ToolSupport::UsageHint(
|
||||
me, "--handshake-handle and --pipe-name are incompatible");
|
||||
me, "--initial-client-data and --pipe-name are incompatible");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif // OS_MACOSX
|
||||
@ -389,20 +398,6 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
|
||||
if (!options.pipe_name.empty()) {
|
||||
exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name));
|
||||
} else if (options.handshake_handle != INVALID_HANDLE_VALUE) {
|
||||
std::wstring pipe_name = exception_handler_server.CreatePipe();
|
||||
|
||||
uint32_t pipe_name_length = static_cast<uint32_t>(pipe_name.size());
|
||||
if (!LoggingWriteFile(options.handshake_handle,
|
||||
&pipe_name_length,
|
||||
sizeof(pipe_name_length))) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!LoggingWriteFile(options.handshake_handle,
|
||||
pipe_name.c_str(),
|
||||
pipe_name.size() * sizeof(pipe_name[0]))) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
#endif // OS_MACOSX
|
||||
|
||||
@ -440,6 +435,13 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
CrashReportExceptionHandler exception_handler(
|
||||
database.get(), &upload_thread, &options.annotations);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
if (options.initial_client_data.IsValid()) {
|
||||
exception_handler_server.InitializeWithInheritedDataForInitialClient(
|
||||
options.initial_client_data, &exception_handler);
|
||||
}
|
||||
#endif // OS_WIN
|
||||
|
||||
exception_handler_server.Run(&exception_handler);
|
||||
|
||||
upload_thread.Stop();
|
||||
|
@ -81,11 +81,6 @@ int CrashOtherProgram(int argc, wchar_t* argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!client.UseHandler()) {
|
||||
LOG(ERROR) << "UseHandler";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Launch another process that hangs.
|
||||
base::FilePath test_executable = Paths::Executable();
|
||||
std::wstring child_test_executable =
|
||||
|
@ -182,7 +182,8 @@ int CrashyMain(int argc, wchar_t* argv[]) {
|
||||
std::string(),
|
||||
std::map<std::string, std::string>(),
|
||||
std::vector<std::string>(),
|
||||
false)) {
|
||||
false,
|
||||
true)) {
|
||||
LOG(ERROR) << "StartHandler";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@ -192,11 +193,6 @@ int CrashyMain(int argc, wchar_t* argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!client.UseHandler()) {
|
||||
LOG(ERROR) << "UseHandler";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
crashpad::SimpleAddressRangeBag* extra_ranges =
|
||||
new crashpad::SimpleAddressRangeBag();
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(
|
||||
|
@ -39,10 +39,6 @@ int CrashyLoadZ7Main(int argc, wchar_t* argv[]) {
|
||||
LOG(ERROR) << "SetHandler";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!client.UseHandler()) {
|
||||
LOG(ERROR) << "UseHandler";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// The DLL has /Z7 symbols embedded in the binary (rather than in a .pdb).
|
||||
// There's only an x86 version of this dll as newer x64 toolchains can't
|
||||
|
@ -56,11 +56,6 @@ int wmain(int argc, wchar_t* argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!client.UseHandler()) {
|
||||
LOG(ERROR) << "UseHandler";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Make sure this module has a CrashpadInfo structure.
|
||||
crashpad::CrashpadInfo* crashpad_info =
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo();
|
||||
|
@ -75,10 +75,6 @@ int SelfDestroyingMain(int argc, wchar_t* argv[]) {
|
||||
LOG(ERROR) << "SetHandler";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!client.UseHandler()) {
|
||||
LOG(ERROR) << "UseHandler";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!FreeOwnStackAndBreak())
|
||||
return EXIT_FAILURE;
|
||||
|
@ -33,7 +33,6 @@ int wmain(int argc, wchar_t* argv[]) {
|
||||
|
||||
crashpad::CrashpadClient client;
|
||||
CHECK(client.SetHandlerIPCPipe(argv[1]));
|
||||
CHECK(client.UseHandler());
|
||||
|
||||
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle";
|
||||
|
@ -33,7 +33,6 @@ int wmain(int argc, wchar_t* argv[]) {
|
||||
|
||||
crashpad::CrashpadClient client;
|
||||
CHECK(client.SetHandlerIPCPipe(argv[1]));
|
||||
CHECK(client.UseHandler());
|
||||
|
||||
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle";
|
||||
|
@ -25,6 +25,7 @@ import time
|
||||
|
||||
|
||||
g_temp_dirs = []
|
||||
g_had_failures = False
|
||||
|
||||
|
||||
def MakeTempDir():
|
||||
@ -181,7 +182,8 @@ class CdbRun(object):
|
||||
print >>sys.stderr, 'remaining output was:\n %s' % self.out
|
||||
print >>sys.stderr, '-' * 80
|
||||
sys.stderr.flush()
|
||||
sys.exit(1)
|
||||
global g_had_failures
|
||||
g_had_failures = True
|
||||
|
||||
|
||||
def RunTests(cdb_path,
|
||||
@ -397,7 +399,7 @@ def main(args):
|
||||
other_program_no_exception_path,
|
||||
pipe_name)
|
||||
|
||||
return 0
|
||||
return 1 if g_had_failures else 0
|
||||
finally:
|
||||
CleanUpTempDirs()
|
||||
|
||||
|
@ -126,7 +126,8 @@ void TestCrashingChild(const base::string16& directory_modification) {
|
||||
CrashingDelegate delegate(server_ready.get(), completed.get());
|
||||
|
||||
ExceptionHandlerServer exception_handler_server(true);
|
||||
std::wstring pipe_name = exception_handler_server.CreatePipe();
|
||||
std::wstring pipe_name(L"\\\\.\\pipe\\test_name");
|
||||
exception_handler_server.SetPipeName(pipe_name);
|
||||
RunServerThread server_thread(&exception_handler_server, &delegate);
|
||||
server_thread.Start();
|
||||
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
|
||||
@ -227,7 +228,8 @@ void TestDumpWithoutCrashingChild(
|
||||
SimulateDelegate delegate(server_ready.get(), completed.get());
|
||||
|
||||
ExceptionHandlerServer exception_handler_server(true);
|
||||
std::wstring pipe_name = exception_handler_server.CreatePipe();
|
||||
std::wstring pipe_name(L"\\\\.\\pipe\\test_name");
|
||||
exception_handler_server.SetPipeName(pipe_name);
|
||||
RunServerThread server_thread(&exception_handler_server, &delegate);
|
||||
server_thread.Start();
|
||||
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
|
||||
|
@ -142,7 +142,7 @@ WinChildProcess::WinChildProcess() {
|
||||
// the parent and so are open and have the same value as in the parent. The
|
||||
// values are passed to the child on the command line.
|
||||
std::string left, right;
|
||||
CHECK(SplitString(switch_value, '|', &left, &right));
|
||||
CHECK(SplitStringFirst(switch_value, '|', &left, &right));
|
||||
|
||||
// left and right were formatted as 0x%x, so they need to be converted as
|
||||
// unsigned ints.
|
||||
|
@ -115,7 +115,7 @@ int RunWithCrashpadMain(int argc, char* argv[]) {
|
||||
case kOptionAnnotation: {
|
||||
std::string key;
|
||||
std::string value;
|
||||
if (!SplitString(optarg, '=', &key, &value)) {
|
||||
if (!SplitStringFirst(optarg, '=', &key, &value)) {
|
||||
ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@ -168,14 +168,11 @@ int RunWithCrashpadMain(int argc, char* argv[]) {
|
||||
options.url,
|
||||
options.annotations,
|
||||
options.arguments,
|
||||
false,
|
||||
false)) {
|
||||
return kExitFailure;
|
||||
}
|
||||
|
||||
if (!crashpad_client.UseHandler()) {
|
||||
return kExitFailure;
|
||||
}
|
||||
|
||||
// Using the remaining arguments, start a new program with the new exception
|
||||
// port in effect.
|
||||
execvp(argv[0], argv);
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -108,6 +109,16 @@ struct StringToUnsignedIntTraits
|
||||
}
|
||||
};
|
||||
|
||||
struct StringToUnsignedInt64Traits
|
||||
: public StringToUnsignedIntegerTraits<uint64_t, uint64_t> {
|
||||
static LongType Convert(const char* str, char** end, int base) {
|
||||
if (str[0] == '-') {
|
||||
return 0;
|
||||
}
|
||||
return strtoull(str, end, base);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
bool StringToIntegerInternal(const base::StringPiece& string,
|
||||
typename Traits::IntType* number) {
|
||||
@ -153,4 +164,8 @@ bool StringToNumber(const base::StringPiece& string, unsigned int* number) {
|
||||
return StringToIntegerInternal<StringToUnsignedIntTraits>(string, number);
|
||||
}
|
||||
|
||||
bool StringToNumber(const base::StringPiece& string, uint64_t* number) {
|
||||
return StringToIntegerInternal<StringToUnsignedInt64Traits>(string, number);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -56,6 +56,7 @@ namespace crashpad {
|
||||
//! where such prefix recognition is desirable.
|
||||
bool StringToNumber(const base::StringPiece& string, int* number);
|
||||
bool StringToNumber(const base::StringPiece& string, unsigned int* number);
|
||||
bool StringToNumber(const base::StringPiece& string, uint64_t* number);
|
||||
//! \}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -18,10 +18,10 @@
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
bool SplitString(const std::string& string,
|
||||
char delimiter,
|
||||
std::string* left,
|
||||
std::string* right) {
|
||||
bool SplitStringFirst(const std::string& string,
|
||||
char delimiter,
|
||||
std::string* left,
|
||||
std::string* right) {
|
||||
size_t delimiter_pos = string.find(delimiter);
|
||||
if (delimiter_pos == 0 || delimiter_pos == std::string::npos) {
|
||||
return false;
|
||||
@ -32,4 +32,27 @@ bool SplitString(const std::string& string,
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& str, char delimiter) {
|
||||
std::vector<std::string> result;
|
||||
if (str.empty())
|
||||
return result;
|
||||
|
||||
size_t start = 0;
|
||||
while (start != std::string::npos) {
|
||||
size_t end = str.find_first_of(delimiter, start);
|
||||
|
||||
std::string part;
|
||||
if (end == std::string::npos) {
|
||||
part = str.substr(start);
|
||||
start = std::string::npos;
|
||||
} else {
|
||||
part = str.substr(start, end - start);
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
result.push_back(part);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -16,6 +16,7 @@
|
||||
#define CRASHPAD_UTIL_STRING_SPLIT_STRING_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -31,10 +32,17 @@ namespace crashpad {
|
||||
//! \return `true` if \a string was split successfully. `false` if \a string
|
||||
//! did not contain a \delimiter character or began with a \delimiter
|
||||
//! character.
|
||||
bool SplitString(const std::string& string,
|
||||
char delimiter,
|
||||
std::string* left,
|
||||
std::string* right);
|
||||
bool SplitStringFirst(const std::string& string,
|
||||
char delimiter,
|
||||
std::string* left,
|
||||
std::string* right);
|
||||
|
||||
//! \brief Splits a string into multiple parts on the given delimiter.
|
||||
//!
|
||||
//! \param[in] string The string to split.
|
||||
//! \param[in] delimiter The delimiter to split at.
|
||||
//! \return The individual parts of the string.
|
||||
std::vector<std::string> SplitString(const std::string& str, char delimiter);
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
|
@ -20,42 +20,74 @@ namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
TEST(SplitString, SplitString) {
|
||||
TEST(SplitString, SplitStringFirst) {
|
||||
std::string left;
|
||||
std::string right;
|
||||
|
||||
EXPECT_FALSE(SplitString("", '=', &left, &right));
|
||||
EXPECT_FALSE(SplitString("no equals", '=', &left, &right));
|
||||
EXPECT_FALSE(SplitString("=", '=', &left, &right));
|
||||
EXPECT_FALSE(SplitString("=beginequals", '=', &left, &right));
|
||||
EXPECT_FALSE(SplitStringFirst("", '=', &left, &right));
|
||||
EXPECT_FALSE(SplitStringFirst("no equals", '=', &left, &right));
|
||||
EXPECT_FALSE(SplitStringFirst("=", '=', &left, &right));
|
||||
EXPECT_FALSE(SplitStringFirst("=beginequals", '=', &left, &right));
|
||||
|
||||
ASSERT_TRUE(SplitString("a=b", '=', &left, &right));
|
||||
ASSERT_TRUE(SplitStringFirst("a=b", '=', &left, &right));
|
||||
EXPECT_EQ("a", left);
|
||||
EXPECT_EQ("b", right);
|
||||
|
||||
ASSERT_TRUE(SplitString("EndsEquals=", '=', &left, &right));
|
||||
ASSERT_TRUE(SplitStringFirst("EndsEquals=", '=', &left, &right));
|
||||
EXPECT_EQ("EndsEquals", left);
|
||||
EXPECT_TRUE(right.empty());
|
||||
|
||||
ASSERT_TRUE(SplitString("key=VALUE", '=', &left, &right));
|
||||
ASSERT_TRUE(SplitStringFirst("key=VALUE", '=', &left, &right));
|
||||
EXPECT_EQ("key", left);
|
||||
EXPECT_EQ("VALUE", right);
|
||||
|
||||
EXPECT_FALSE(SplitString("a=b", '|', &left, &right));
|
||||
EXPECT_FALSE(SplitStringFirst("a=b", '|', &left, &right));
|
||||
|
||||
ASSERT_TRUE(SplitString("ls | less", '|', &left, &right));
|
||||
ASSERT_TRUE(SplitStringFirst("ls | less", '|', &left, &right));
|
||||
EXPECT_EQ("ls ", left);
|
||||
EXPECT_EQ(" less", right);
|
||||
|
||||
ASSERT_TRUE(SplitString("when in", ' ', &left, &right));
|
||||
ASSERT_TRUE(SplitStringFirst("when in", ' ', &left, &right));
|
||||
EXPECT_EQ("when", left);
|
||||
EXPECT_EQ("in", right);
|
||||
|
||||
ASSERT_TRUE(SplitString("zoo", 'o', &left, &right));
|
||||
ASSERT_TRUE(SplitStringFirst("zoo", 'o', &left, &right));
|
||||
EXPECT_EQ("z", left);
|
||||
EXPECT_EQ("o", right);
|
||||
|
||||
ASSERT_FALSE(SplitString("ooze", 'o', &left, &right));
|
||||
ASSERT_FALSE(SplitStringFirst("ooze", 'o', &left, &right));
|
||||
}
|
||||
|
||||
TEST(SplitString, SplitString) {
|
||||
std::vector<std::string> parts;
|
||||
|
||||
parts = SplitString("", '.');
|
||||
EXPECT_EQ(0u, parts.size());
|
||||
|
||||
parts = SplitString(".", '.');
|
||||
ASSERT_EQ(2u, parts.size());
|
||||
EXPECT_EQ("", parts[0]);
|
||||
EXPECT_EQ("", parts[1]);
|
||||
|
||||
parts = SplitString("a,b", ',');
|
||||
ASSERT_EQ(2u, parts.size());
|
||||
EXPECT_EQ("a", parts[0]);
|
||||
EXPECT_EQ("b", parts[1]);
|
||||
|
||||
parts = SplitString("zoo", 'o');
|
||||
ASSERT_EQ(3u, parts.size());
|
||||
EXPECT_EQ("z", parts[0]);
|
||||
EXPECT_EQ("", parts[1]);
|
||||
EXPECT_EQ("", parts[2]);
|
||||
|
||||
parts = SplitString("0x100,0x200,0x300,0x400,0x500,0x600", ',');
|
||||
ASSERT_EQ(6u, parts.size());
|
||||
EXPECT_EQ("0x100", parts[0]);
|
||||
EXPECT_EQ("0x200", parts[1]);
|
||||
EXPECT_EQ("0x300", parts[2]);
|
||||
EXPECT_EQ("0x400", parts[3]);
|
||||
EXPECT_EQ("0x500", parts[4]);
|
||||
EXPECT_EQ("0x600", parts[5]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -172,6 +172,8 @@
|
||||
'win/get_module_information.h',
|
||||
'win/handle.cc',
|
||||
'win/handle.h',
|
||||
'win/initial_client_data.cc',
|
||||
'win/initial_client_data.h',
|
||||
'win/module_version.cc',
|
||||
'win/module_version.h',
|
||||
'win/nt_internals.cc',
|
||||
|
@ -88,6 +88,7 @@
|
||||
'win/exception_handler_server_test.cc',
|
||||
'win/get_function_test.cc',
|
||||
'win/handle_test.cc',
|
||||
'win/initial_client_data_test.cc',
|
||||
'win/process_info_test.cc',
|
||||
'win/scoped_process_suspend_test.cc',
|
||||
'win/time_test.cc',
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
#include "util/win/exception_handler_server.h"
|
||||
|
||||
#include <sddl.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
@ -30,73 +29,17 @@
|
||||
#include "snapshot/crashpad_info_client_options.h"
|
||||
#include "snapshot/win/process_snapshot_win.h"
|
||||
#include "util/file/file_writer.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);
|
||||
@ -165,6 +108,9 @@ class ClientData {
|
||||
ClientData(HANDLE port,
|
||||
ExceptionHandlerServer::Delegate* delegate,
|
||||
ScopedKernelHANDLE process,
|
||||
ScopedKernelHANDLE crash_dump_requested_event,
|
||||
ScopedKernelHANDLE non_crash_dump_requested_event,
|
||||
ScopedKernelHANDLE non_crash_dump_completed_event,
|
||||
WinVMAddress crash_exception_information_address,
|
||||
WinVMAddress non_crash_exception_information_address,
|
||||
WinVMAddress debug_critical_section_address,
|
||||
@ -177,12 +123,11 @@ class ClientData {
|
||||
lock_(),
|
||||
port_(port),
|
||||
delegate_(delegate),
|
||||
crash_dump_requested_event_(
|
||||
CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
|
||||
crash_dump_requested_event_(std::move(crash_dump_requested_event)),
|
||||
non_crash_dump_requested_event_(
|
||||
CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
|
||||
std::move(non_crash_dump_requested_event)),
|
||||
non_crash_dump_completed_event_(
|
||||
CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
|
||||
std::move(non_crash_dump_completed_event)),
|
||||
process_(std::move(process)),
|
||||
crash_exception_information_address_(
|
||||
crash_exception_information_address),
|
||||
@ -315,36 +260,45 @@ void ExceptionHandlerServer::SetPipeName(const std::wstring& pipe_name) {
|
||||
pipe_name_ = pipe_name;
|
||||
}
|
||||
|
||||
std::wstring ExceptionHandlerServer::CreatePipe() {
|
||||
void ExceptionHandlerServer::InitializeWithInheritedDataForInitialClient(
|
||||
const InitialClientData& initial_client_data,
|
||||
Delegate* delegate) {
|
||||
DCHECK(pipe_name_.empty());
|
||||
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(initial_client_data.first_pipe_instance());
|
||||
|
||||
first_pipe_instance_.reset(CreateNamedPipeInstance(pipe_name, true));
|
||||
// TODO(scottmg): Vista+. Might need to pass through or possibly find an Nt*.
|
||||
size_t bytes = sizeof(wchar_t) * _MAX_PATH + sizeof(FILE_NAME_INFO);
|
||||
std::unique_ptr<uint8_t[]> data(new uint8_t[bytes]);
|
||||
if (!GetFileInformationByHandleEx(
|
||||
first_pipe_instance_.get(), FileNameInfo, data.get(), bytes)) {
|
||||
PLOG(FATAL) << "GetFileInformationByHandleEx";
|
||||
}
|
||||
FILE_NAME_INFO* file_name_info =
|
||||
reinterpret_cast<FILE_NAME_INFO*>(data.get());
|
||||
pipe_name_ =
|
||||
L"\\\\.\\pipe" + std::wstring(file_name_info->FileName,
|
||||
file_name_info->FileNameLength /
|
||||
sizeof(file_name_info->FileName[0]));
|
||||
|
||||
// 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;
|
||||
{
|
||||
base::AutoLock lock(clients_lock_);
|
||||
internal::ClientData* client = new internal::ClientData(
|
||||
port_.get(),
|
||||
delegate,
|
||||
ScopedKernelHANDLE(initial_client_data.client_process()),
|
||||
ScopedKernelHANDLE(initial_client_data.request_crash_dump()),
|
||||
ScopedKernelHANDLE(initial_client_data.request_non_crash_dump()),
|
||||
ScopedKernelHANDLE(initial_client_data.non_crash_dump_completed()),
|
||||
initial_client_data.crash_exception_information(),
|
||||
initial_client_data.non_crash_exception_information(),
|
||||
initial_client_data.debug_critical_section_address(),
|
||||
&OnCrashDumpEvent,
|
||||
&OnNonCrashDumpEvent,
|
||||
&OnProcessEnd);
|
||||
clients_.insert(client);
|
||||
}
|
||||
}
|
||||
|
||||
void ExceptionHandlerServer::Run(Delegate* delegate) {
|
||||
@ -513,6 +467,12 @@ bool ExceptionHandlerServer::ServiceClientConnection(
|
||||
service_context.port(),
|
||||
service_context.delegate(),
|
||||
ScopedKernelHANDLE(client_process),
|
||||
ScopedKernelHANDLE(
|
||||
CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
|
||||
ScopedKernelHANDLE(
|
||||
CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
|
||||
ScopedKernelHANDLE(
|
||||
CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
|
||||
message.registration.crash_exception_information,
|
||||
message.registration.non_crash_exception_information,
|
||||
message.registration.critical_section_address,
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/win/address_types.h"
|
||||
#include "util/win/initial_client_data.h"
|
||||
#include "util/win/scoped_handle.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -72,21 +73,32 @@ class ExceptionHandlerServer {
|
||||
|
||||
//! \brief Sets the pipe name to listen for client registrations on.
|
||||
//!
|
||||
//! Either this method or CreatePipe(), but not both, must be called before
|
||||
//! Run().
|
||||
//! This method, or InitializeWithInheritedDataForInitialClient(), must be
|
||||
//! called before Run().
|
||||
//!
|
||||
//! \param[in] pipe_name The name of the pipe to listen on. Must be of the
|
||||
//! form "\\.\pipe\<some_name>".
|
||||
void SetPipeName(const std::wstring& pipe_name);
|
||||
|
||||
//! \brief Creates a randomized pipe name to listen for client registrations
|
||||
//! on and returns its name.
|
||||
//! \brief Sets the pipe to listen for client registrations on, providing
|
||||
//! the first precreated instance.
|
||||
//!
|
||||
//! Either this method or CreatePipe(), but not both, must be called before
|
||||
//! Run().
|
||||
//! This method, or SetPipeName(), must be called before Run(). All of these
|
||||
//! parameters are generally created in a parent process that launches the
|
||||
//! handler. For more details see the Windows implementation of
|
||||
//! CrashpadClient.
|
||||
//!
|
||||
//! \return The pipe name that will be listened on.
|
||||
std::wstring CreatePipe();
|
||||
//! \sa CrashpadClient
|
||||
//! \sa RegistrationRequest
|
||||
//!
|
||||
//! \param[in] initial_client_data The handles and addresses of data inherited
|
||||
//! from a parent process needed to initialize and register the first
|
||||
//! client. Ownership of these handles is taken.
|
||||
//! \param[in] delegate The interface to which the exceptions are delegated
|
||||
//! when they are caught in Run(). Ownership is not transferred.
|
||||
void InitializeWithInheritedDataForInitialClient(
|
||||
const InitialClientData& initial_client_data,
|
||||
Delegate* delegate);
|
||||
|
||||
//! \brief Runs the exception-handling server.
|
||||
//!
|
||||
@ -98,6 +110,10 @@ class ExceptionHandlerServer {
|
||||
//! object must not be destroyed until Run() returns.
|
||||
void Stop();
|
||||
|
||||
//! \brief The number of server-side pipe instances that the exception handler
|
||||
//! server creates to listen for connections from clients.
|
||||
static const size_t kPipeInstances = 2;
|
||||
|
||||
private:
|
||||
static bool ServiceClientConnection(
|
||||
const internal::PipeServiceContext& service_context);
|
||||
|
@ -81,10 +81,12 @@ class ExceptionHandlerServerTest : public testing::Test {
|
||||
public:
|
||||
ExceptionHandlerServerTest()
|
||||
: server_(true),
|
||||
pipe_name_(server_.CreatePipe()),
|
||||
pipe_name_(L"\\\\.\\pipe\\test_name"),
|
||||
server_ready_(CreateEvent(nullptr, false, false, nullptr)),
|
||||
delegate_(server_ready_.get()),
|
||||
server_thread_(&server_, &delegate_) {}
|
||||
server_thread_(&server_, &delegate_) {
|
||||
server_.SetPipeName(pipe_name_);
|
||||
}
|
||||
|
||||
TestDelegate& delegate() { return delegate_; }
|
||||
ExceptionHandlerServer& server() { return server_; }
|
||||
@ -171,10 +173,6 @@ class TestClient final : public WinChildProcess {
|
||||
ADD_FAILURE();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!client.UseHandler()) {
|
||||
ADD_FAILURE();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
WriteWString(WritePipeHandle(), L"OK");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
113
util/win/initial_client_data.cc
Normal file
113
util/win/initial_client_data.cc
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2016 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/initial_client_data.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "util/stdlib/string_number_conversion.h"
|
||||
#include "util/string/split_string.h"
|
||||
#include "util/win/handle.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
bool HandleFromString(const std::string& str, HANDLE* handle) {
|
||||
unsigned int handle_uint;
|
||||
if (!StringToNumber(str, &handle_uint) ||
|
||||
(*handle = IntToHandle(handle_uint)) == INVALID_HANDLE_VALUE) {
|
||||
LOG(ERROR) << "could not convert '" << str << "' to HANDLE";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddressFromString(const std::string& str, WinVMAddress* address) {
|
||||
if (!StringToNumber(str, address)) {
|
||||
LOG(ERROR) << "could not convert '" << str << "' to WinVMAddress";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
InitialClientData::InitialClientData()
|
||||
: crash_exception_information_(0),
|
||||
non_crash_exception_information_(0),
|
||||
debug_critical_section_address_(0),
|
||||
request_crash_dump_(nullptr),
|
||||
request_non_crash_dump_(nullptr),
|
||||
non_crash_dump_completed_(nullptr),
|
||||
first_pipe_instance_(INVALID_HANDLE_VALUE),
|
||||
client_process_(nullptr),
|
||||
is_valid_(false) {}
|
||||
|
||||
InitialClientData::InitialClientData(
|
||||
HANDLE request_crash_dump,
|
||||
HANDLE request_non_crash_dump,
|
||||
HANDLE non_crash_dump_completed,
|
||||
HANDLE first_pipe_instance,
|
||||
HANDLE client_process,
|
||||
WinVMAddress crash_exception_information,
|
||||
WinVMAddress non_crash_exception_information,
|
||||
WinVMAddress debug_critical_section_address)
|
||||
: crash_exception_information_(crash_exception_information),
|
||||
non_crash_exception_information_(non_crash_exception_information),
|
||||
debug_critical_section_address_(debug_critical_section_address),
|
||||
request_crash_dump_(request_crash_dump),
|
||||
request_non_crash_dump_(request_non_crash_dump),
|
||||
non_crash_dump_completed_(non_crash_dump_completed),
|
||||
first_pipe_instance_(first_pipe_instance),
|
||||
client_process_(client_process),
|
||||
is_valid_(true) {}
|
||||
|
||||
bool InitialClientData::InitializeFromString(const std::string& str) {
|
||||
std::vector<std::string> parts(SplitString(str, ','));
|
||||
if (parts.size() != 8) {
|
||||
LOG(ERROR) << "expected 8 comma separated arguments";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HandleFromString(parts[0], &request_crash_dump_) ||
|
||||
!HandleFromString(parts[1], &request_non_crash_dump_) ||
|
||||
!HandleFromString(parts[2], &non_crash_dump_completed_) ||
|
||||
!HandleFromString(parts[3], &first_pipe_instance_) ||
|
||||
!HandleFromString(parts[4], &client_process_) ||
|
||||
!AddressFromString(parts[5], &crash_exception_information_) ||
|
||||
!AddressFromString(parts[6], &non_crash_exception_information_) ||
|
||||
!AddressFromString(parts[7], &debug_critical_section_address_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string InitialClientData::StringRepresentation() const {
|
||||
return base::StringPrintf("0x%x,0x%x,0x%x,0x%x,0x%x,0x%I64x,0x%I64x,0x%I64x",
|
||||
HandleToInt(request_crash_dump_),
|
||||
HandleToInt(request_non_crash_dump_),
|
||||
HandleToInt(non_crash_dump_completed_),
|
||||
HandleToInt(first_pipe_instance_),
|
||||
HandleToInt(client_process_),
|
||||
crash_exception_information_,
|
||||
non_crash_exception_information_,
|
||||
debug_critical_section_address_);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
116
util/win/initial_client_data.h
Normal file
116
util/win/initial_client_data.h
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_WIN_INITIAL_CLIENT_DATA_H_
|
||||
#define CRASHPAD_UTIL_WIN_INITIAL_CLIENT_DATA_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "util/win/address_types.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief A container for the data associated with the `--initial-client-data`
|
||||
//! method for initializing the handler process on Windows.
|
||||
class InitialClientData {
|
||||
public:
|
||||
//! \brief Constructs an unintialized instance to be used with
|
||||
//! InitializeFromString().
|
||||
InitialClientData();
|
||||
|
||||
//! \brief Constructs an instance of InitialClientData. This object does not
|
||||
//! take ownership of any of the referenced HANDLEs.
|
||||
//!
|
||||
//! \param[in] request_crash_dump An event signalled from the client on crash.
|
||||
//! \param[in] request_non_crash_dump An event signalled from the client when
|
||||
//! it would like a dump to be taken, but allowed to continue afterwards.
|
||||
//! \param[in] non_crash_dump_completed An event signalled from the handler to
|
||||
//! tell the client that the non-crash dump has completed, and it can
|
||||
//! continue execution.
|
||||
//! \param[in] first_pipe_instance The server end and first instance of a pipe
|
||||
//! that will be used for communication with all other clients after this
|
||||
//! initial one.
|
||||
//! \param[in] client_process A process handle for the client being
|
||||
//! registered.
|
||||
//! \param[in] crash_exception_information The address, in the client's
|
||||
//! address space, of an ExceptionInformation structure, used when
|
||||
//! handling a crash dump request.
|
||||
//! \param[in] non_crash_exception_information The address, in the client's
|
||||
//! address space, of an ExceptionInformation structure, used when
|
||||
//! handling a non-crashing dump request.
|
||||
//! \param[in] debug_critical_section_address The address, in the client
|
||||
//! process's address space, of a `CRITICAL_SECTION` allocated with a
|
||||
//! valid .DebugInfo field. This can be accomplished by using
|
||||
//! InitializeCriticalSectionWithDebugInfoIfPossible() or equivalent. This
|
||||
//! value can be `0`, however then limited lock data will be available in
|
||||
//! minidumps.
|
||||
InitialClientData(HANDLE request_crash_dump,
|
||||
HANDLE request_non_crash_dump,
|
||||
HANDLE non_crash_dump_completed,
|
||||
HANDLE first_pipe_instance,
|
||||
HANDLE client_process,
|
||||
WinVMAddress crash_exception_information,
|
||||
WinVMAddress non_crash_exception_information,
|
||||
WinVMAddress debug_critical_section_address);
|
||||
|
||||
//! \brief Returns whether the object has been initialized successfully.
|
||||
bool IsValid() const { return is_valid_; }
|
||||
|
||||
//! Initializes this object from a string representation presumed to have been
|
||||
//! created by StringRepresentation().
|
||||
//!
|
||||
//! \param[in] str The output of StringRepresentation().
|
||||
//!
|
||||
//! \return `true` on success, or `false` with a message logged on failure.
|
||||
bool InitializeFromString(const std::string& str);
|
||||
|
||||
//! \brief Returns a string representation of the data of this object,
|
||||
//! suitable for passing on the command line.
|
||||
std::string StringRepresentation() const;
|
||||
|
||||
HANDLE request_crash_dump() const { return request_crash_dump_; }
|
||||
HANDLE request_non_crash_dump() const { return request_non_crash_dump_; }
|
||||
HANDLE non_crash_dump_completed() const { return non_crash_dump_completed_; }
|
||||
HANDLE first_pipe_instance() const { return first_pipe_instance_; }
|
||||
HANDLE client_process() const { return client_process_; }
|
||||
WinVMAddress crash_exception_information() const {
|
||||
return crash_exception_information_;
|
||||
}
|
||||
WinVMAddress non_crash_exception_information() const {
|
||||
return non_crash_exception_information_;
|
||||
}
|
||||
WinVMAddress debug_critical_section_address() const {
|
||||
return debug_critical_section_address_;
|
||||
}
|
||||
|
||||
private:
|
||||
WinVMAddress crash_exception_information_;
|
||||
WinVMAddress non_crash_exception_information_;
|
||||
WinVMAddress debug_critical_section_address_;
|
||||
HANDLE request_crash_dump_;
|
||||
HANDLE request_non_crash_dump_;
|
||||
HANDLE non_crash_dump_completed_;
|
||||
HANDLE first_pipe_instance_;
|
||||
HANDLE client_process_;
|
||||
bool is_valid_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(InitialClientData);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_WIN_INITIAL_CLIENT_DATA_H_
|
74
util/win/initial_client_data_test.cc
Normal file
74
util/win/initial_client_data_test.cc
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2016 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/initial_client_data.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
TEST(InitialClientData, Validity) {
|
||||
InitialClientData icd1;
|
||||
EXPECT_FALSE(icd1.IsValid());
|
||||
|
||||
InitialClientData icd2(
|
||||
reinterpret_cast<HANDLE>(0x123),
|
||||
reinterpret_cast<HANDLE>(0x456),
|
||||
reinterpret_cast<HANDLE>(0x789),
|
||||
reinterpret_cast<HANDLE>(0xabc),
|
||||
reinterpret_cast<HANDLE>(0xdef),
|
||||
0x7fff000012345678ull,
|
||||
0x100000ull,
|
||||
0xccccddddeeeeffffull);
|
||||
EXPECT_TRUE(icd2.IsValid());
|
||||
}
|
||||
|
||||
TEST(InitialClientData, RoundTrip) {
|
||||
InitialClientData first(
|
||||
reinterpret_cast<HANDLE>(0x123),
|
||||
reinterpret_cast<HANDLE>(0x456),
|
||||
reinterpret_cast<HANDLE>(0x789),
|
||||
reinterpret_cast<HANDLE>(0xabc),
|
||||
reinterpret_cast<HANDLE>(0xdef),
|
||||
0x7fff000012345678ull,
|
||||
0x100000ull,
|
||||
0xccccddddeeeeffffull);
|
||||
|
||||
std::string as_string = first.StringRepresentation();
|
||||
EXPECT_EQ(
|
||||
"0x123,0x456,0x789,0xabc,0xdef,"
|
||||
"0x7fff000012345678,0x100000,0xccccddddeeeeffff",
|
||||
as_string);
|
||||
|
||||
InitialClientData second;
|
||||
ASSERT_TRUE(second.InitializeFromString(as_string));
|
||||
EXPECT_EQ(first.request_crash_dump(), second.request_crash_dump());
|
||||
EXPECT_EQ(first.request_non_crash_dump(), second.request_non_crash_dump());
|
||||
EXPECT_EQ(first.non_crash_dump_completed(),
|
||||
second.non_crash_dump_completed());
|
||||
EXPECT_EQ(first.first_pipe_instance(), second.first_pipe_instance());
|
||||
EXPECT_EQ(first.client_process(), second.client_process());
|
||||
EXPECT_EQ(first.crash_exception_information(),
|
||||
second.crash_exception_information());
|
||||
EXPECT_EQ(first.non_crash_exception_information(),
|
||||
second.non_crash_exception_information());
|
||||
EXPECT_EQ(first.debug_critical_section_address(),
|
||||
second.debug_critical_section_address());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -15,9 +15,12 @@
|
||||
#include "util/win/registration_protocol_win.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <sddl.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "util/win/exception_handler_server.h"
|
||||
#include "util/win/scoped_handle.h"
|
||||
#include "util/win/scoped_local_alloc.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -91,4 +94,47 @@ bool SendToCrashHandlerServer(const base::string16& pipe_name,
|
||||
}
|
||||
}
|
||||
|
||||
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 = TRUE;
|
||||
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,
|
||||
ExceptionHandlerServer::kPipeInstances,
|
||||
512,
|
||||
512,
|
||||
0,
|
||||
security_attributes_pointer);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -128,6 +128,17 @@ bool SendToCrashHandlerServer(const base::string16& pipe_name,
|
||||
const ClientToServerMessage& message,
|
||||
ServerToClientMessage* response);
|
||||
|
||||
//! \brief Wraps CreateNamedPipe() to create a single named pipe instance.
|
||||
//!
|
||||
//! \param[in] pipe_name The name to use for the pipe.
|
||||
//! \param[in] first_instance If `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);
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_
|
||||
|
Loading…
x
Reference in New Issue
Block a user