Update Crashpad to b47bf6c250c6b825dee1c5fbad9152c2c962e828
2d87606bb56b win: Start crashpad_handler by inheriting connection data
to it
cc0b7deef27d Get VS2013 compilation working again for Crashpad
f735d050c487 Port the util library to Linux
e956a8252fc1 Port the util library to Android
d5a759c900ae Update mini_chromium to 8e8d3cc9a245
d1aafe78ea46 Port the test library and crashpad_test_test to
Linux/Android
b978b03fa188 Port most of crashpad_util_test to Linux/Android
c2814e251912 Don't throttle explicitly requested uploads
fd751f4708cc Correct StringToUnsignedInt[64]()
e7bd798af438 Update build/test and status documentation to reflect
Android
e616638c9d87 Replace Rietveld with Gerrit in the developer documentation
47a830465f78 Port the minidump library to Android and ARM
88e3b6b02271 Omit platform-specific assembler source from builds as
needed
96b9857aceb4 Fix the crashpad_minidump library for 32-bit ARM
c55e49c13d5c doc: Remove errant parenthesis
76ef9b5c2b00 win: Address failure-to-start-handler case for async
startup
55ba6b67801b break; after handling --initial-client-data on command line
bb7d249d65a1 Partially port the crashpad_client library to Linux/Android
375082098deb mac: Fix tests on 10.12.1
c4cdec3d72a2 Handle non-crashing cases for server failure to start
b47bf6c250c6 Fix tests when running on Win10
Also update .GN files, and adapt Chrome side code for changes in API.
Only the first three files are modified here, the rest are unmodified
from upstream Crashpad repo.
This does not yet make handler startup asynchronous, or move it back to
chrome_elf. Those will be done in smaller followup CLs.
R=mark@chromium.org
TBR=rsesek
BUG=660955,565063,656800,655788
Review-Url: https://codereview.chromium.org/2478633002
Cr-Commit-Position: refs/heads/master@{#429983}
diff --git a/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn b/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn
index e2d0a856..15869f8 100644
--- a/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn
+++ b/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn
@@ -151,6 +151,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",
diff --git a/components/crash/content/app/crashpad_mac.mm b/components/crash/content/app/crashpad_mac.mm
index 3223ddd..7df66ea 100644
--- a/components/crash/content/app/crashpad_mac.mm
+++ b/components/crash/content/app/crashpad_mac.mm
@@ -97,15 +97,13 @@
url,
process_annotations,
arguments,
- true);
- if (result) {
- result = crashpad_client.UseHandler();
- }
+ true,
+ false);
// If this is an initial client that's not the browser process, it's
// important to sever the connection to any existing handler. If
- // StartHandler() or UseHandler() failed, call UseSystemDefaultHandler()
- // in that case to drop the link to the existing handler.
+ // StartHandler() failed, call UseSystemDefaultHandler() to drop the link
+ // to the existing handler.
if (!result && !browser_process) {
crashpad::CrashpadClient::UseSystemDefaultHandler();
}
diff --git a/components/crash/content/app/crashpad_win.cc b/components/crash/content/app/crashpad_win.cc
index d9a4089..a22af313a 100644
--- a/components/crash/content/app/crashpad_win.cc
+++ b/components/crash/content/app/crashpad_win.cc
@@ -58,7 +58,6 @@
bool embedded_handler) {
base::FilePath database_path; // Only valid in the browser process.
base::FilePath metrics_path; // Only valid in the browser process.
- bool result = false;
const char kPipeNameVar[] = "CHROME_CRASHPAD_PIPE_NAME";
const char kServerUrlVar[] = "CHROME_CRASHPAD_SERVER_URL";
@@ -119,9 +118,9 @@
exe_file = exe_dir.Append(FILE_PATH_LITERAL("crashpad_handler.exe"));
}
- result = g_crashpad_client.Get().StartHandler(
+ g_crashpad_client.Get().StartHandler(
exe_file, database_path, metrics_path, url, process_annotations,
- arguments, false);
+ arguments, false, false);
// If we're the browser, push the pipe name into the environment so child
// processes can connect to it. If we inherited another crashpad_handler's
@@ -130,16 +129,12 @@
base::UTF16ToUTF8(g_crashpad_client.Get().GetHandlerIPCPipe()));
} else {
std::string pipe_name_utf8;
- result = env->GetVar(kPipeNameVar, &pipe_name_utf8);
- if (result) {
- result = g_crashpad_client.Get().SetHandlerIPCPipe(
+ if (env->GetVar(kPipeNameVar, &pipe_name_utf8)) {
+ g_crashpad_client.Get().SetHandlerIPCPipe(
base::UTF8ToUTF16(pipe_name_utf8));
}
}
- if (result) {
- result = g_crashpad_client.Get().UseHandler();
- }
return database_path;
}
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index 034e6355..d7382be3 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
Short Name: crashpad
URL: https://crashpad.chromium.org/
Version: unknown
-Revision: 1e6dbcb3008f6eebe02ca33d8c36c8922931dad3
+Revision: b47bf6c250c6b825dee1c5fbad9152c2c962e828
License: Apache 2.0
License File: crashpad/LICENSE
Security Critical: yes
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS
index 5233398..79677f3 100644
--- a/third_party/crashpad/crashpad/DEPS
+++ b/third_party/crashpad/crashpad/DEPS
@@ -25,7 +25,7 @@
'93cc6e2c23e4d5ebd179f388e67aa907d0dfd43d',
'crashpad/third_party/mini_chromium/mini_chromium':
Var('chromium_git') + '/chromium/mini_chromium@' +
- '88e0a3e1a965c698d7ead8bcfc0cfb6aacdc3524',
+ '8e8d3cc9a245f1bf63296e97fb6ac1c90f6d86f5',
'buildtools':
Var('chromium_git') + '/chromium/buildtools.git@' +
'f8fc76ea5ce4a60cda2fa5d7df3d4a62935b3113',
diff --git a/third_party/crashpad/crashpad/client/client.gyp b/third_party/crashpad/crashpad/client/client.gyp
index 9892fb6..45ae382 100644
--- a/third_party/crashpad/crashpad/client/client.gyp
+++ b/third_party/crashpad/crashpad/client/client.gyp
@@ -61,6 +61,11 @@
],
},
}],
+ ['OS!="mac"', {
+ 'sources!': [
+ 'capture_context_mac.S',
+ ],
+ }],
],
'direct_dependent_settings': {
'include_dirs': [
diff --git a/third_party/crashpad/crashpad/client/crashpad_client.h b/third_party/crashpad/crashpad/client/crashpad_client.h
index f8f1a61..693557e 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client.h
+++ b/third_party/crashpad/crashpad/client/crashpad_client.h
@@ -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 @@
//! \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 @@
//! 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 @@
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 @@
//! \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 @@
//! `"\\.\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 @@
//! 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 @@
};
#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 @@
#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);
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
index c1e305a..cc50969 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
@@ -523,8 +523,7 @@
} // namespace
-CrashpadClient::CrashpadClient()
- : exception_port_() {
+CrashpadClient::CrashpadClient() {
}
CrashpadClient::~CrashpadClient() {
@@ -537,9 +536,8 @@
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 @@
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
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_win.cc b/third_party/crashpad/crashpad/client/crashpad_client_win.cc
index b9e42c67..235ef185 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_win.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_win.cc
@@ -22,24 +22,29 @@
#include "base/atomicops.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "base/scoped_generic.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#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 +53,7 @@
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 +66,20 @@
// 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;
+
+enum class StartupState : int {
+ kNotReady = 0, // This must be value 0 because it is the initial value of a
+ // global AtomicWord.
+ kSucceeded = 1, // The CreateProcess() for the handler succeeded.
+ kFailed = 2, // The handler failed to start.
+};
+
+// This is a tri-state of type StartupState. It starts at 0 == kNotReady, and
+// when the handler is known to have started successfully, or failed to start
+// the value will be updated. The unhandled exception filter will not proceed
+// until one of those two cases happens.
+base::subtle::AtomicWord g_handler_startup_state;
// A CRITICAL_SECTION initialized with
// RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO to force it to be allocated with a
@@ -70,7 +88,37 @@
// list, so this allows the handler to capture all of them.
CRITICAL_SECTION g_critical_section_with_debug_info;
+void SetHandlerStartupState(StartupState state) {
+ DCHECK(state == StartupState::kSucceeded ||
+ state == StartupState::kFailed);
+ base::subtle::Acquire_Store(&g_handler_startup_state,
+ static_cast<base::subtle::AtomicWord>(state));
+}
+
+StartupState BlockUntilHandlerStartedOrFailed() {
+ // Wait until we know the handler has either succeeded or failed to start.
+ base::subtle::AtomicWord startup_state;
+ while (
+ (startup_state = base::subtle::Release_Load(&g_handler_startup_state)) ==
+ static_cast<int>(StartupState::kNotReady)) {
+ Sleep(1);
+ }
+
+ return static_cast<StartupState>(startup_state);
+}
+
LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
+ if (BlockUntilHandlerStartedOrFailed() == StartupState::kFailed) {
+ // If we know for certain that the handler has failed to start, then abort
+ // here, rather than trying to signal to a handler that will never arrive,
+ // and then sleeping unnecessarily.
+ LOG(ERROR) << "crash server failed to launch, self-terminating";
+ TerminateProcess(GetCurrentProcess(), kTerminationCodeCrashNoDump);
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ // Otherwise, we know the handler startup has succeeded, and we can continue.
+
// Tracks whether a thread has already entered UnhandledExceptionHandler.
static base::subtle::AtomicWord have_crashed;
@@ -94,7 +142,7 @@
// 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 +158,7 @@
LOG(ERROR) << "crash server did not respond, self-terminating";
- TerminateProcess(GetCurrentProcess(), crashpad::kTerminationCodeCrashNoDump);
+ TerminateProcess(GetCurrentProcess(), kTerminationCodeCrashNoDump);
return EXCEPTION_CONTINUE_SEARCH;
}
@@ -121,9 +169,7 @@
}
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 +232,137 @@
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,
+ const std::wstring& ipc_pipe,
+ ScopedFileHANDLE ipc_pipe_handle)
+ : handler(handler),
+ database(database),
+ metrics_dir(metrics_dir),
+ url(url),
+ annotations(annotations),
+ arguments(arguments),
+ ipc_pipe(ipc_pipe),
+ 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());
+ 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;
+ std::wstring ipc_pipe;
+ ScopedFileHANDLE ipc_pipe_handle;
+};
- 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;
+// Ensures that SetHandlerStartupState() is called on scope exit. Assumes
+// failure, and on success, SetSuccessful() should be called.
+class ScopedCallSetHandlerStartupState {
+ public:
+ ScopedCallSetHandlerStartupState() : successful_(false) {}
+
+ ~ScopedCallSetHandlerStartupState() {
+ SetHandlerStartupState(successful_ ? StartupState::kSucceeded
+ : StartupState::kFailed);
}
- 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";
+ void SetSuccessful() { successful_ = true; }
+
+ private:
+ bool successful_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCallSetHandlerStartupState);
+};
+
+bool StartHandlerProcess(
+ std::unique_ptr<BackgroundHandlerStartThreadData> data) {
+ ScopedCallSetHandlerStartupState scoped_startup_state_caller;
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 +413,12 @@
}
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 +440,7 @@
}
PROCESS_INFORMATION process_info;
- rv = CreateProcess(handler.value().c_str(),
+ rv = CreateProcess(data->handler.value().c_str(),
&command_line[0],
nullptr,
nullptr,
@@ -348,38 +461,125 @@
rv = CloseHandle(process_info.hProcess);
PLOG_IF(WARNING, !rv) << "CloseHandle process";
- pipe_write_owner.reset();
+ // It is important to close our side of the pipe here before confirming that
+ // we can communicate with the server. By doing so, the only remaining copy of
+ // the server side of the pipe belongs to the exception handler process we
+ // just spawned. Otherwise, the pipe will continue to exist indefinitely, so
+ // the connection loop will not detect that it will never be serviced.
+ data->ipc_pipe_handle.reset();
- uint32_t ipc_pipe_length;
- if (!LoggingReadFile(pipe_read, &ipc_pipe_length, sizeof(ipc_pipe_length))) {
+ // Confirm that the server is waiting for connections before continuing.
+ ClientToServerMessage message = {};
+ message.type = ClientToServerMessage::kPing;
+ ServerToClientMessage response = {};
+ if (!SendToCrashHandlerServer(data->ipc_pipe, message, &response)) {
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;
- }
-
+ scoped_startup_state_caller.SetSuccessful();
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();
+}
+
+} // 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();
+
+ SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
+
+ auto data = new BackgroundHandlerStartThreadData(handler,
+ database,
+ metrics_dir,
+ url,
+ annotations,
+ arguments,
+ ipc_pipe_,
+ 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";
+ SetHandlerStartupState(StartupState::kFailed);
+ 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 +597,10 @@
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 = {};
@@ -416,6 +608,9 @@
return false;
}
+ SetHandlerStartupState(StartupState::kSucceeded);
+ SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
+
// The server returns these already duplicated to be valid in this process.
g_signal_exception =
IntToHandle(response.registration.request_crash_dump_event);
@@ -424,19 +619,45 @@
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 ||
g_non_crash_dump_done == INVALID_HANDLE_VALUE) {
- LOG(ERROR) << "haven't called UseHandler()";
+ LOG(ERROR) << "not connected";
+ return;
+ }
+
+ if (BlockUntilHandlerStartedOrFailed() == StartupState::kFailed) {
+ // If we know for certain that the handler has failed to start, then abort
+ // here, as we would otherwise wait indefinitely for the
+ // g_non_crash_dump_done event that would never be signalled.
+ LOG(ERROR) << "crash server failed to launch, no dump captured";
return;
}
@@ -475,7 +696,7 @@
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";
@@ -487,11 +708,15 @@
// static
void CrashpadClient::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) {
if (g_signal_exception == INVALID_HANDLE_VALUE) {
- LOG(ERROR) << "haven't called UseHandler(), no dump captured";
- TerminateProcess(GetCurrentProcess(), kTerminationCodeUseHandlerNotCalled);
+ LOG(ERROR) << "not connected";
+ TerminateProcess(GetCurrentProcess(),
+ kTerminationCodeNotConnectedToHandler);
return;
}
+ // We don't need to check for handler startup here, as
+ // UnhandledExceptionHandler() necessarily does that.
+
UnhandledExceptionHandler(exception_pointers);
}
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc b/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc
index 0c222cc..94a9b0cc 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc
@@ -15,10 +15,12 @@
#include "client/crashpad_client.h"
#include "base/files/file_path.h"
+#include "base/macros.h"
#include "gtest/gtest.h"
#include "test/paths.h"
#include "test/scoped_temp_dir.h"
#include "test/win/win_multiprocess.h"
+#include "util/win/termination_codes.h"
namespace crashpad {
namespace test {
@@ -35,8 +37,9 @@
"",
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 {
@@ -87,6 +90,92 @@
WinMultiprocess::Run<StartWithSameStdoutStderr>();
}
+void StartAndUseBrokenHandler(CrashpadClient* client) {
+ ScopedTempDir temp_dir;
+ base::FilePath handler_path = Paths::Executable().DirName().Append(
+ FILE_PATH_LITERAL("fake_handler_that_crashes_at_startup.exe"));
+ ASSERT_TRUE(client->StartHandler(handler_path,
+ temp_dir.path(),
+ base::FilePath(),
+ "",
+ std::map<std::string, std::string>(),
+ std::vector<std::string>(),
+ false,
+ true));
+}
+
+class HandlerLaunchFailureCrash : public WinMultiprocess {
+ public:
+ HandlerLaunchFailureCrash() : WinMultiprocess() {}
+
+ private:
+ void WinMultiprocessParent() override {
+ SetExpectedChildExitCode(crashpad::kTerminationCodeCrashNoDump);
+ }
+
+ void WinMultiprocessChild() override {
+ CrashpadClient client;
+ StartAndUseBrokenHandler(&client);
+ __debugbreak();
+ exit(0);
+ }
+};
+
+TEST(CrashpadClient, HandlerLaunchFailureCrash) {
+ WinMultiprocess::Run<HandlerLaunchFailureCrash>();
+}
+
+class HandlerLaunchFailureDumpAndCrash : public WinMultiprocess {
+ public:
+ HandlerLaunchFailureDumpAndCrash() : WinMultiprocess() {}
+
+ private:
+ void WinMultiprocessParent() override {
+ SetExpectedChildExitCode(crashpad::kTerminationCodeCrashNoDump);
+ }
+
+ void WinMultiprocessChild() override {
+ CrashpadClient client;
+ StartAndUseBrokenHandler(&client);
+ // We don't need to fill this out as we're only checking that we're
+ // terminated with the correct failure code.
+ EXCEPTION_POINTERS info = {};
+ client.DumpAndCrash(&info);
+ exit(0);
+ }
+};
+
+TEST(CrashpadClient, HandlerLaunchFailureDumpAndCrash) {
+ WinMultiprocess::Run<HandlerLaunchFailureDumpAndCrash>();
+}
+
+class HandlerLaunchFailureDumpWithoutCrash : public WinMultiprocess {
+ public:
+ HandlerLaunchFailureDumpWithoutCrash() : WinMultiprocess() {}
+
+ private:
+ void WinMultiprocessParent() override {
+ // DumpWithoutCrash() normally blocks indefinitely. There's no return value,
+ // but confirm that it exits cleanly because it'll return right away if the
+ // handler didn't start.
+ SetExpectedChildExitCode(0);
+ }
+
+ void WinMultiprocessChild() override {
+ CrashpadClient client;
+ StartAndUseBrokenHandler(&client);
+ // We don't need to fill this out as we're only checking that we're
+ // terminated with the correct failure code.
+ CONTEXT context = {};
+ client.DumpWithoutCrash(context);
+ exit(0);
+ }
+};
+
+TEST(CrashpadClient, HandlerLaunchFailureDumpWithoutCrash) {
+ WinMultiprocess::Run<HandlerLaunchFailureDumpWithoutCrash>();
+}
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/crashpad_info.cc b/third_party/crashpad/crashpad/client/crashpad_info.cc
index 9ad41a67..e8a6a9e 100644
--- a/third_party/crashpad/crashpad/client/crashpad_info.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_info.cc
@@ -59,33 +59,47 @@
// This may result in a static module initializer in debug-mode builds, but
// because it’s POD, no code should need to run to initialize this under
// release-mode optimization.
-#if defined(OS_MACOSX)
+#if defined(OS_POSIX)
+__attribute__((
-// Put the structure in __DATA,__crashpad_info where it can be easily found
-// without having to consult the symbol table. The “used” attribute prevents it
-// from being dead-stripped.
-__attribute__((section(SEG_DATA ",__crashpad_info"),
- used,
- visibility("hidden")
+ // Put the structure in a well-known section name where it can be easily
+ // found without having to consult the symbol table.
+#if defined(OS_MACOSX)
+ section(SEG_DATA ",crashpad_info"),
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+ section("crashpad_info"),
+#else // !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID)
+#error Port
+#endif // !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID)
+
#if __has_feature(address_sanitizer)
-// AddressSanitizer would add a trailing red zone of at least 32 bytes, which
-// would be reflected in the size of the custom section. This confuses
-// MachOImageReader::GetCrashpadInfo(), which finds that the section’s size
-// disagrees with the structure’s size_ field. By specifying an alignment
-// greater than the red zone size, the red zone will be suppressed.
- ,
- aligned(64)
-#endif
- )) CrashpadInfo g_crashpad_info;
+ // AddressSanitizer would add a trailing red zone of at least 32 bytes,
+ // which would be reflected in the size of the custom section. This confuses
+ // MachOImageReader::GetCrashpadInfo(), which finds that the section’s size
+ // disagrees with the structure’s size_ field. By specifying an alignment
+ // greater than the red zone size, the red zone will be suppressed.
+ aligned(64),
+#endif // __has_feature(address_sanitizer)
+
+ // The “used” attribute prevents the structure from being dead-stripped.
+ used,
+
+ // There’s no need to expose this as a public symbol from the symbol table.
+ // All accesses from the outside can locate the well-known section name.
+ visibility("hidden")))
#elif defined(OS_WIN)
// Put the struct in a section name CPADinfo where it can be found without the
// symbol table.
#pragma section("CPADinfo", read, write)
-__declspec(allocate("CPADinfo")) CrashpadInfo g_crashpad_info;
+__declspec(allocate("CPADinfo"))
-#endif
+#else // !defined(OS_POSIX) && !defined(OS_WIN)
+#error Port
+#endif // !defined(OS_POSIX) && !defined(OS_WIN)
+
+CrashpadInfo g_crashpad_info;
// static
CrashpadInfo* CrashpadInfo::GetCrashpadInfo() {
diff --git a/third_party/crashpad/crashpad/doc/developing.ad b/third_party/crashpad/crashpad/doc/developing.ad
index 9174f6b1..a8aafcf 100644
--- a/third_party/crashpad/crashpad/doc/developing.ad
+++ b/third_party/crashpad/crashpad/doc/developing.ad
@@ -38,7 +38,7 @@
* Chromium’s
https://dev.chromium.org/developers/how-tos/depottools[depot_tools].
- * http://git-scm.com/[Git]. This is provided by Xcode on Mac OS X and by
+ * https://git-scm.com/[Git]. This is provided by Xcode on Mac OS X and by
depot_tools on Windows.
* https://www.python.org/[Python]. This is provided by the operating system on
Mac OS X, and by depot_tools on Windows.
@@ -69,8 +69,8 @@
$ *fetch crashpad*
----
-`fetch crashpad` performs the initial `gclient sync`, establishing a
-fully-functional local checkout.
+`fetch crashpad` performs the initial `git clone` and `gclient sync`,
+establishing a fully-functional local checkout.
=== Subsequent Checkouts
@@ -84,10 +84,10 @@
== Building
Crashpad uses https://gyp.gsrc.io/[GYP] to generate
-https://martine.github.io/ninja/[Ninja] build files. The build is described by
-`.gyp` files throughout the Crashpad source code tree. The
-`build/gyp_crashpad.py` script runs GYP properly for Crashpad, and is also
-called when you run `fetch crashpad`, `gclient sync`, or `gclient runhooks`.
+https://ninja-build.org/[Ninja] build files. The build is described by `.gyp`
+files throughout the Crashpad source code tree. The `build/gyp_crashpad.py`
+script runs GYP properly for Crashpad, and is also called when you run `fetch
+crashpad`, `gclient sync`, or `gclient runhooks`.
The Ninja build files and build output are in the `out` directory. Both debug-
and release-mode configurations are available. The examples below show the debug
@@ -104,6 +104,80 @@
https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. There’s no
need to install it separately.
+=== Android
+
+Crashpad’s Android port is in its early stages. This build relies on
+cross-compilation. It’s possible to develop Crashpad for Android on any platform
+that the https://developer.android.com/ndk/[Android NDK (Native Development
+Kit)] runs on.
+
+If it’s not already present on your system,
+https://developer.android.com/ndk/downloads/[download the NDK package for your
+system] and expand it to a suitable location. These instructions assume that
+it’s been expanded to `~/android-ndk-r13`.
+
+To build Crashpad, portions of the NDK must be reassembled into a
+https://developer.android.com/ndk/guides/standalone_toolchain.html[standalone
+toolchain]. This is a repackaged subset of the NDK suitable for cross-compiling
+for a single Android architecture (such as `arm`, `arm64`, `x86`, and `x86_64`)
+targeting a specific
+https://source.android.com/source/build-numbers.html[Android API level]. The
+standalone toolchain only needs to be built from the NDK one time for each set
+of options desired. To build a standalone toolchain targeting 64-bit ARM and API
+level 21 (Android 5.0 “Lollipop”), run:
+
+[subs="verbatim,quotes"]
+----
+$ *cd ~*
+$ *python android-ndk-r13/build/tools/make_standalone_toolchain.py \
+ --arch=arm64 --api=21 --install-dir=android-ndk-r13_arm64_api21*
+----
+
+Note that Chrome uses Android API level 21 for 64-bit platforms and 16 for
+32-bit platforms. See Chrome’s
+https://chromium.googlesource.com/chromium/src/+/master/build/config/android/config.gni[`build/config/android/config.gni`]
+which sets `_android_api_level` and `_android64_api_level`.
+
+To configure a Crashpad build for Android using this standalone toolchain,
+set several environment variables directing the build to the standalone
+toolchain, along with GYP options to identify an Android build. This must be
+done after any `gclient sync`, or instead of any `gclient runhooks` operation.
+The environment variables only need to be set for this `gyp_crashpad.py`
+invocation, and need not be permanent.
+
+[subs="verbatim,quotes"]
+----
+$ *cd \~/crashpad/crashpad*
+$ *CC_target=\~/android-ndk-r13_arm64_api21/bin/clang \
+ CXX_target=\~/android-ndk-r13_arm64_api21/bin/clang++ \
+ AR_target=\~/android-ndk-r13_arm64_api21/bin/aarch64-linux-android-ar \
+ NM_target=\~/android-ndk-r13_arm64_api21/bin/aarch64-linux-android-nm \
+ READELF_target=~/android-ndk-r13_arm64_api21/bin/aarch64-linux-android-readelf \
+ python build/gyp_crashpad.py \
+ -DOS=android -Dtarget_arch=arm64 -Dclang=1 \
+ --generator-output=out_android_arm64_api21 -f ninja-android*
+----
+
+Target “triplets” to use for `ar`, `nm`, and `readelf` are:
+
+[width="40%",cols="1,3",frame="topbot"]
+|===
+|`arm` |`arm-linux-androideabi`
+|`arm64` |`aarch64-linux-android`
+|`x86` |`i686-linux-android`
+|`x86_64` |`x86_64-linux-android`
+|===
+
+The port is incomplete, but targets known to be working include `crashpad_util`,
+`crashpad_test`, and `crashpad_test_test`. This list will grow over time. To
+build, direct `ninja` to the specific `out` directory chosen by
+`--generator-output` above.
+
+[subs="verbatim,quotes"]
+----
+$ *ninja -C out_android_arm64_api21/out/Debug crashpad_test_test*
+----
+
== Testing
Crashpad uses https://github.com/google/googletest/[Google Test] as its
@@ -130,6 +204,39 @@
$ *python build/run_tests.py Debug*
----
+=== Android
+
+To test on Android, use
+https://developer.android.com/studio/command-line/adb.html[ADB (Android Debug
+Bridge)] to `adb push` test executables and test data to a device or emulator,
+then use `adb shell` to get a shell to run the test executables from. ADB is
+part of the https://developer.android.com/sdk/[Android SDK]. Note that it is
+sufficient to install just the command-line tools. The entire Android Studio IDE
+is not necessary to obtain ADB.
+
+This example runs `crashpad_test_test` on a device. This test executable has a
+run-time dependency on a second executable and a test data file, which are also
+transferred to the device prior to running the test.
+
+[subs="verbatim,quotes"]
+----
+$ *cd ~/crashpad/crashpad*
+$ *adb push out_android_arm64_api21/out/Debug/crashpad_test_test /data/local/tmp/*
+[100%] /data/local/tmp/crashpad_test_test
+$ *adb push \
+ out_android_arm64_api21/out/Debug/crashpad_test_test_multiprocess_exec_test_child \
+ /data/local/tmp/*
+[100%] /data/local/tmp/crashpad_test_test_multiprocess_exec_test_child
+$ *adb shell mkdir -p /data/local/tmp/crashpad_test_data_root/test*
+$ *adb push test/paths_test_data_root.txt \
+ /data/local/tmp/crashpad_test_data_root/test/*
+[100%] /data/local/tmp/crashpad_test_data_root/test/paths_test_data_root.txt
+$ *adb shell*
+device:/ $ *cd /data/local/tmp*
+device:/data/local/tmp $ *CRASHPAD_TEST_DATA_ROOT=crashpad_test_data_root \
+ ./crashpad_test_test*
+----
+
== Contributing
Crashpad’s contribution process is very similar to
@@ -139,11 +246,12 @@
=== Code Review
A code review must be conducted for every change to Crashpad’s source code. Code
-review is conducted on https://codereview.chromium.org/[Chromium’s Rietveld]
-system, and all code reviews must be sent to an appropriate reviewer, with a Cc
-sent to
+review is conducted on https://chromium-review.googlesource.com/[Chromium’s
+Gerrit] system, and all code reviews must be sent to an appropriate reviewer,
+with a Cc sent to
https://groups.google.com/a/chromium.org/group/crashpad-dev[crashpad-dev]. The
-`codereview.settings` file specifies this environment to `git-cl`.
+https://chromium.googlesource.com/crashpad/crashpad/+/master/codereview.settings[`codereview.settings`]
+file specifies this environment to `git-cl`.
`git-cl` is part of the
https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. There’s no
@@ -159,14 +267,18 @@
$ *git cl upload*
----
-Uploading a patch to Rietveld does not automatically request a review. You must
-select a reviewer and mail your request to them (with a Cc to crashpad-dev) from
-the Rietveld issue page after running `git cl upload`. If you have lost track of
-the issue page, `git cl issue` will remind you of its URL. Alternatively, you
-can request review when uploading to Rietveld by using `git cl upload
---send-mail`
+The https://polygerrit.appspot.com/[PolyGerrit interface] to Gerrit, undergoing
+active development, is recommended. To switch from the classic GWT-based Gerrit
+UI to PolyGerrit, click the PolyGerrit link in a Gerrit review page’s footer.
-Git branches maintain their association with Rietveld issues, so if you need to
+Uploading a patch to Gerrit does not automatically request a review. You must
+select a reviewer on the Gerrit review page after running `git cl upload`. This
+action notifies your reviewer of the code review request. If you have lost track
+of the review page, `git cl issue` will remind you of its URL. Alternatively,
+you can request review when uploading to Gerrit by using `git cl upload
+--send-mail`.
+
+Git branches maintain their association with Gerrit reviews, so if you need to
make changes based on review feedback, you can do so on the correct Git branch,
committing your changes locally with `git commit`. You can then upload a new
patch set with `git cl upload` and let your reviewer know you’ve addressed the
@@ -174,8 +286,8 @@
=== Landing Changes
-After code review is complete and “LGTM” (“looks good to me”) has been received
-from all reviewers, project members can commit the patch themselves:
+After code review is complete and “Code-Review: +1” has been received from all
+reviewers, project members can commit the patch themselves:
[subs="verbatim,quotes"]
----
@@ -184,19 +296,14 @@
$ *git cl land*
----
+Alternatively, patches can be committed by clicking the “Submit” button in the
+Gerrit UI.
+
Crashpad does not currently have a
https://dev.chromium.org/developers/testing/commit-queue[commit queue], so
-contributors that are not project members will have to ask a project member to
+contributors who are not project members will have to ask a project member to
commit the patch for them. Project members can commit changes on behalf of
-external contributors by patching the change into a local branch and landing it:
-
-[subs="verbatim,quotes"]
-----
-$ *cd ~/crashpad/crashpad*
-$ *git checkout -b for_external_contributor origin/master*
-$ *git cl patch 12345678* _# 12345678 is the Rietveld issue number_
-$ *git cl land -c \'External Contributor <external@contributor.org>'*
-----
+external contributors by clicking the “Submit” button in the Gerrit UI.
=== External Contributions
diff --git a/third_party/crashpad/crashpad/doc/status.ad b/third_party/crashpad/crashpad/doc/status.ad
index 9098214..6d45dd45 100644
--- a/third_party/crashpad/crashpad/doc/status.ad
+++ b/third_party/crashpad/crashpad/doc/status.ad
@@ -31,7 +31,7 @@
Initial work on a Crashpad client for
https://crashpad.chromium.org/bug/30[Android] has begun. This is currently in
-the design phase.
+the early implementation phase.
== Future
diff --git a/third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc
index 23dda48..5bd1fbf 100644
--- a/third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc
+++ b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc
@@ -207,9 +207,12 @@
// hour, and retire reports that would exceed this limit or for which the
// upload fails on the first attempt.
//
+ // If upload was requested explicitly (i.e. by user action), we do not
+ // throttle the upload.
+ //
// TODO(mark): Provide a proper rate-limiting strategy and allow for failed
// upload attempts to be retried.
- if (rate_limit_) {
+ if (!report.upload_explicitly_requested && rate_limit_) {
time_t last_upload_attempt_time;
if (settings->GetLastUploadAttemptTime(&last_upload_attempt_time)) {
time_t now = time(nullptr);
diff --git a/third_party/crashpad/crashpad/handler/crashpad_handler.ad b/third_party/crashpad/crashpad/handler/crashpad_handler.ad
index 06f62563..a4918d6f 100644
--- a/third_party/crashpad/crashpad/handler/crashpad_handler.ad
+++ b/third_party/crashpad/crashpad/handler/crashpad_handler.ad
@@ -51,14 +51,17 @@
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 @@
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 @@
*--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
diff --git a/third_party/crashpad/crashpad/handler/handler.gyp b/third_party/crashpad/crashpad/handler/handler.gyp
index cb219fb7..d6900531 100644
--- a/third_party/crashpad/crashpad/handler/handler.gyp
+++ b/third_party/crashpad/crashpad/handler/handler.gyp
@@ -161,6 +161,13 @@
],
},
{
+ 'target_name': 'fake_handler_that_crashes_at_startup',
+ 'type': 'executable',
+ 'sources': [
+ 'win/fake_handler_that_crashes_at_startup.cc',
+ ],
+ },
+ {
'target_name': 'hanging_program',
'type': 'executable',
'dependencies': [
diff --git a/third_party/crashpad/crashpad/handler/handler_main.cc b/third_party/crashpad/crashpad/handler/handler_main.cc
index 8749c58..29c5ddc 100644
--- a/third_party/crashpad/crashpad/handler/handler_main.cc
+++ b/third_party/crashpad/crashpad/handler/handler_main.cc
@@ -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 @@
" --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 @@
Metrics::HandlerCrashed(exception_pointers->ExceptionRecord->ExceptionCode);
return EXCEPTION_CONTINUE_SEARCH;
}
-#endif
+#endif // OS_WIN
} // namespace
@@ -145,9 +153,12 @@
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 @@
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
- {"metrics-dir", required_argument, nullptr, kOptionMetrics},
- {"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
+#if defined(OS_WIN)
+ {"initial-client-data",
+ required_argument,
+ nullptr,
+ kOptionInitialClientData},
+#endif // OS_MACOSX
#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 @@
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,14 +261,10 @@
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;
@@ -310,15 +321,14 @@
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 +399,6 @@
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 +436,13 @@
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();
diff --git a/third_party/crashpad/crashpad/handler/win/crash_other_program.cc b/third_party/crashpad/crashpad/handler/win/crash_other_program.cc
index db532bb5..d191aac0 100644
--- a/third_party/crashpad/crashpad/handler/win/crash_other_program.cc
+++ b/third_party/crashpad/crashpad/handler/win/crash_other_program.cc
@@ -46,13 +46,13 @@
return false;
}
- int thread_count = 0;
do {
if (te32.th32OwnerProcessID == target_pid) {
- thread_count++;
- if (thread_count == 2) {
- // Nominate this lucky thread as our blamee, and dump it. This will be
- // "Thread1" in the child.
+ // We set the thread priority of "Thread1" to a non-default value before
+ // going to sleep. Dump and blame this thread. For an explanation of
+ // "9", see
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100.aspx.
+ if (te32.tpBasePri == 9) {
ScopedKernelHANDLE thread(
OpenThread(kXPThreadAllAccess, false, te32.th32ThreadID));
if (!client.DumpAndCrashTargetProcess(
@@ -81,11 +81,6 @@
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 =
diff --git a/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc b/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc
index 021233c..b6e044c8 100644
--- a/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc
+++ b/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc
@@ -182,7 +182,8 @@
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 @@
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(
diff --git a/third_party/crashpad/crashpad/handler/win/crashy_test_z7_loader.cc b/third_party/crashpad/crashpad/handler/win/crashy_test_z7_loader.cc
index 284afa86..79802cd 100644
--- a/third_party/crashpad/crashpad/handler/win/crashy_test_z7_loader.cc
+++ b/third_party/crashpad/crashpad/handler/win/crashy_test_z7_loader.cc
@@ -39,10 +39,6 @@
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
diff --git a/third_party/crashpad/crashpad/handler/win/fake_handler_that_crashes_at_startup.cc b/third_party/crashpad/crashpad/handler/win/fake_handler_that_crashes_at_startup.cc
new file mode 100644
index 0000000..028190a
--- /dev/null
+++ b/third_party/crashpad/crashpad/handler/win/fake_handler_that_crashes_at_startup.cc
@@ -0,0 +1,20 @@
+// 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.
+
+// This is used to test a crashpad_handler that launches successfully, but then
+// crashes before setting up.
+int wmain() {
+ __debugbreak();
+ return 0;
+}
diff --git a/third_party/crashpad/crashpad/handler/win/hanging_program.cc b/third_party/crashpad/crashpad/handler/win/hanging_program.cc
index 877578e..6387663 100644
--- a/third_party/crashpad/crashpad/handler/win/hanging_program.cc
+++ b/third_party/crashpad/crashpad/handler/win/hanging_program.cc
@@ -22,6 +22,9 @@
#include "client/crashpad_info.h"
DWORD WINAPI Thread1(LPVOID dummy) {
+ // We set the thread priority up by one as a hacky way to signal to the other
+ // test program that this is the thread we want to dump.
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
Sleep(INFINITE);
return 0;
}
@@ -56,11 +59,6 @@
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();
diff --git a/third_party/crashpad/crashpad/handler/win/self_destroying_test_program.cc b/third_party/crashpad/crashpad/handler/win/self_destroying_test_program.cc
index ac358ef0..672e4df3 100644
--- a/third_party/crashpad/crashpad/handler/win/self_destroying_test_program.cc
+++ b/third_party/crashpad/crashpad/handler/win/self_destroying_test_program.cc
@@ -75,10 +75,6 @@
LOG(ERROR) << "SetHandler";
return EXIT_FAILURE;
}
- if (!client.UseHandler()) {
- LOG(ERROR) << "UseHandler";
- return EXIT_FAILURE;
- }
if (!FreeOwnStackAndBreak())
return EXIT_FAILURE;
diff --git a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc
index 1d25b11..e880aa7 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc
@@ -31,6 +31,8 @@
#if defined(OS_MACOSX)
#include <AvailabilityMacros.h>
+#elif defined(OS_ANDROID)
+#include <android/api-level.h>
#endif
namespace crashpad {
@@ -99,6 +101,8 @@
// they will be truncated and a message will be logged.
#if defined(OS_MACOSX)
const char kOS[] = "mac";
+#elif defined(OS_ANDROID)
+ const char kOS[] = "android";
#elif defined(OS_LINUX)
const char kOS[] = "linux";
#elif defined(OS_WIN)
@@ -111,6 +115,10 @@
const char kCPU[] = "i386";
#elif defined(ARCH_CPU_X86_64)
const char kCPU[] = "amd64";
+#elif defined(ARCH_CPU_ARMEL)
+ const char kCPU[] = "arm";
+#elif defined(ARCH_CPU_ARM64)
+ const char kCPU[] = "arm64";
#else
#error define kCPU for this CPU
#endif
@@ -126,6 +134,8 @@
",%d,%d",
AvailabilityVersionToMacOSXMinorVersion(MAC_OS_X_VERSION_MIN_REQUIRED),
AvailabilityVersionToMacOSXMinorVersion(MAC_OS_X_VERSION_MAX_ALLOWED));
+#elif defined(OS_ANDROID)
+ debug_build_string += base::StringPrintf(",%d", __ANDROID_API__);
#endif
return debug_build_string;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc
index 5d5908d..d25ba43 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc
@@ -117,7 +117,18 @@
bool* destroy_complex_request) override {
*destroy_complex_request = true;
- EXPECT_EQ(ChildTask(), task);
+ // In 10.12, dyld fatal errors as tested by test_type_ = kCrashDyld are via
+ // abort_with_payload(). In 10.12.1, the task port delivered in an exception
+ // message for this termination type is a corpse, even when the exception is
+ // EXC_CRASH and not EXC_CORPSE_NOTIFY. The corpse task port (here, |task|)
+ // is distinct from the process’ original task port (ChildTask()). This is
+ // filed as https://openradar.appspot.com/29079442.
+ //
+ // Instead of comparing task ports, compare PIDs.
+ pid_t task_pid;
+ kern_return_t kr = pid_for_task(task, &task_pid);
+ EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "pid_for_task");
+ EXPECT_EQ(ChildPID(), task_pid);
ProcessReader process_reader;
bool rv = process_reader.Initialize(task);
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
index 3e944f7..d0b7a08 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
@@ -477,7 +477,7 @@
mach_vm_address_t crashpad_info_address;
const process_types::section* crashpad_info_section =
- GetSectionByName(SEG_DATA, "__crashpad_info", &crashpad_info_address);
+ GetSectionByName(SEG_DATA, "crashpad_info", &crashpad_info_address);
if (!crashpad_info_section) {
return false;
}
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h
index d211ce9c..40eddc2 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h
+++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h
@@ -270,8 +270,9 @@
//! \brief Obtains the module’s CrashpadInfo structure.
//!
//! \return `true` on success, `false` on failure. If the module does not have
- //! a `__crashpad_info` section, this will return `false` without logging
- //! any messages. Other failures will result in messages being logged.
+ //! a `__DATA,crashpad_info` section, this will return `false` without
+ //! logging any messages. Other failures will result in messages being
+ //! logged.
bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const;
private:
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype b/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype
index 7bae0872..713bb09 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype
@@ -21,7 +21,7 @@
// snapshot/mac/process_types.cc to produce process type struct definitions and
// accessors.
-// Client Mach-O images will contain a __DATA,__crashpad_info section formatted
+// Client Mach-O images will contain a __DATA,crashpad_info section formatted
// according to this structure.
PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo)
PROCESS_TYPE_STRUCT_MEMBER(uint32_t, signature)
diff --git a/third_party/crashpad/crashpad/snapshot/module_snapshot.h b/third_party/crashpad/crashpad/snapshot/module_snapshot.h
index 870eb20..5989e4b 100644
--- a/third_party/crashpad/crashpad/snapshot/module_snapshot.h
+++ b/third_party/crashpad/crashpad/snapshot/module_snapshot.h
@@ -165,7 +165,7 @@
//! contain multiple annotations, so they are returned in a vector.
//!
//! For Mac OS X snapshots, these annotations are found by interpreting the
- //! module’s `__DATA, __crash_info` section as `crashreporter_annotations_t`.
+ //! module’s `__DATA,__crash_info` section as `crashreporter_annotations_t`.
//! System libraries using the crash reporter client interface may reference
//! annotations in this structure. Additional annotations messages may be
//! found in other locations, which may be module-specific. The dynamic linker
@@ -184,7 +184,7 @@
//! keys.”
//!
//! For Mac OS X snapshots, these annotations are found by interpreting the
- //! `__DATA, __crashpad_info` section as `CrashpadInfo`. Clients can use the
+ //! `__DATA,crashpad_info` section as `CrashpadInfo`. Clients can use the
//! Crashpad client interface to store annotations in this structure. Most
//! annotations under the client’s direct control will be retrievable by this
//! method. For clients such as Chrome, this includes the process type.
diff --git a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc
index d4023c65..2cfb9906 100644
--- a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc
@@ -33,7 +33,6 @@
crashpad::CrashpadClient client;
CHECK(client.SetHandlerIPCPipe(argv[1]));
- CHECK(client.UseHandler());
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle";
diff --git a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc
index cf5ab95..b6c9814a 100644
--- a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc
@@ -33,7 +33,6 @@
crashpad::CrashpadClient client;
CHECK(client.SetHandlerIPCPipe(argv[1]));
- CHECK(client.UseHandler());
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle";
diff --git a/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py b/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py
index 62e92ea..f469bcf 100755
--- a/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py
+++ b/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py
@@ -25,6 +25,7 @@
g_temp_dirs = []
+g_had_failures = False
def MakeTempDir():
@@ -181,7 +182,16 @@
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 Find(self, pattern, re_flags=0):
+ match_obj = re.search(pattern, self.out, re_flags)
+ if match_obj:
+ # Matched. Consume up to end of match.
+ self.out = self.out[match_obj.end(0):]
+ return match_obj
+ return None
def RunTests(cdb_path,
@@ -265,9 +275,15 @@
r'FreeOwnStackAndBreak.*\nquit:',
'at correct location, no additional stack entries')
- # Switch to the other thread after jumping to the exception, and examine
- # memory.
- out = CdbRun(cdb_path, dump_path, '.ecxr; ~1s; db /c14 edi')
+ # Dump memory pointed to be EDI on the background suspended thread. We don't
+ # know the index of the thread because the system may have started other
+ # threads, so first do a run to extract the thread index that's suspended, and
+ # then another run to dump the data pointed to by EDI for that thread.
+ out = CdbRun(cdb_path, dump_path, '.ecxr;~')
+ match_obj = out.Find(r'(\d+)\s+Id: [0-9a-f.]+ Suspend: 1 Teb:')
+ if match_obj:
+ thread = match_obj.group(1)
+ out = CdbRun(cdb_path, dump_path, '.ecxr;~' + thread + 's;db /c14 edi')
out.Check(r'63 62 61 60 5f 5e 5d 5c-5b 5a 59 58 57 56 55 54 53 52 51 50',
'data pointed to by registers captured')
@@ -328,10 +344,17 @@
'other program dump exception code')
out.Check('!Sleep', 'other program reasonable location')
out.Check('hanging_program!Thread1', 'other program dump right thread')
- out.Check('\. 1 Id.*Suspend: 0 ',
- 'other program exception on correct thread and correct suspend')
- out.Check(' 4 Id.*Suspend: 0 ',
- 'other program injection thread correct suspend')
+ count = 0
+ while True:
+ match_obj = out.Find(r'Id.*Suspend: (\d+) ')
+ if match_obj:
+ if match_obj.group(1) != '0':
+ out.Check(r'FAILED', 'all suspend counts should be 0')
+ else:
+ count += 1
+ else:
+ break
+ assert count > 2
out = CdbRun(cdb_path, other_program_no_exception_path, '.ecxr;k')
out.Check('Unknown exception - code 0cca11ed',
@@ -397,7 +420,7 @@
other_program_no_exception_path,
pipe_name)
- return 0
+ return 1 if g_had_failures else 0
finally:
CleanUpTempDirs()
diff --git a/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc
index 88612df..af0cc3a 100644
--- a/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc
@@ -126,7 +126,8 @@
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 @@
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(
diff --git a/third_party/crashpad/crashpad/test/multiprocess_exec_posix.cc b/third_party/crashpad/crashpad/test/multiprocess_exec_posix.cc
index caf2b476..93363a0 100644
--- a/third_party/crashpad/crashpad/test/multiprocess_exec_posix.cc
+++ b/third_party/crashpad/crashpad/test/multiprocess_exec_posix.cc
@@ -25,6 +25,10 @@
#include "util/misc/scoped_forbid_return.h"
#include "util/posix/close_multiple.h"
+#if defined(OS_LINUX)
+#include <stdio_ext.h>
+#endif
+
namespace crashpad {
namespace test {
@@ -78,8 +82,14 @@
ASSERT_NE(read_handle, STDOUT_FILENO);
ASSERT_EQ(STDIN_FILENO, fileno(stdin));
- int rv = fpurge(stdin);
+ int rv;
+
+#if defined(OS_LINUX)
+ __fpurge(stdin);
+#else
+ rv = fpurge(stdin);
ASSERT_EQ(0, rv) << ErrnoMessage("fpurge");
+#endif
rv = HANDLE_EINTR(dup2(read_handle, STDIN_FILENO));
ASSERT_EQ(STDIN_FILENO, rv) << ErrnoMessage("dup2");
diff --git a/third_party/crashpad/crashpad/test/multiprocess_exec_test_child.cc b/third_party/crashpad/crashpad/test/multiprocess_exec_test_child.cc
index c796e4a3..011a37d 100644
--- a/third_party/crashpad/crashpad/test/multiprocess_exec_test_child.cc
+++ b/third_party/crashpad/crashpad/test/multiprocess_exec_test_child.cc
@@ -27,6 +27,7 @@
#endif
#if defined(OS_POSIX)
+#include <sys/resource.h>
#include <unistd.h>
#elif defined(OS_WIN)
#include <windows.h>
@@ -37,9 +38,13 @@
// Make sure that there’s nothing open at any FD higher than 3. All FDs other
// than stdin, stdout, and stderr should have been closed prior to or at
// exec().
- int max_fd = std::max(static_cast<int>(sysconf(_SC_OPEN_MAX)), OPEN_MAX);
- max_fd = std::max(max_fd, getdtablesize());
- for (int fd = STDERR_FILENO + 1; fd < max_fd; ++fd) {
+ rlimit rlimit_nofile;
+ if (getrlimit(RLIMIT_NOFILE, &rlimit_nofile) != 0) {
+ abort();
+ }
+ for (int fd = STDERR_FILENO + 1;
+ fd < static_cast<int>(rlimit_nofile.rlim_cur);
+ ++fd) {
if (close(fd) == 0 || errno != EBADF) {
abort();
}
diff --git a/third_party/crashpad/crashpad/test/multiprocess_posix_test.cc b/third_party/crashpad/crashpad/test/multiprocess_posix_test.cc
index 191bdeff..be927111 100644
--- a/third_party/crashpad/crashpad/test/multiprocess_posix_test.cc
+++ b/third_party/crashpad/crashpad/test/multiprocess_posix_test.cc
@@ -14,8 +14,8 @@
#include "test/multiprocess.h"
+#include <signal.h>
#include <stdlib.h>
-#include <sys/signal.h>
#include <unistd.h>
#include "base/macros.h"
diff --git a/third_party/crashpad/crashpad/test/paths_linux.cc b/third_party/crashpad/crashpad/test/paths_linux.cc
new file mode 100644
index 0000000..6e6da0e
--- /dev/null
+++ b/third_party/crashpad/crashpad/test/paths_linux.cc
@@ -0,0 +1,62 @@
+// 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 "test/paths.h"
+
+#include <limits.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/logging.h"
+#include "util/misc/implicit_cast.h"
+
+namespace crashpad {
+namespace test {
+
+// static
+base::FilePath Paths::Executable() {
+ // Linux does not provide a straightforward way to size the buffer before
+ // calling readlink(). Normally, the st_size field returned by lstat() could
+ // be used, but this is usually zero for things in /proc.
+ //
+ // The /proc filesystem does not provide any way to read “exe” links for
+ // pathnames longer than a page. See linux-4.4.27/fs/proc/base.c
+ // do_proc_readlink(), which allocates a single page to receive the path
+ // string. Coincidentally, the page size and PATH_MAX are normally the same
+ // value, although neither is strictly a limit on the length of a pathname.
+ //
+ // On Android, the smaller of the page size and PATH_MAX actually does serve
+ // as an effective limit on the length of an executable’s pathname. See
+ // Android 7.0.0 bionic/linker/linker.cpp get_executable_path(), which aborts
+ // via __libc_fatal() if the “exe” link can’t be read into a PATH_MAX-sized
+ // buffer.
+ std::string exe_path(std::max(implicit_cast<size_t>(sysconf(_SC_PAGESIZE)),
+ implicit_cast<size_t>(PATH_MAX)),
+ std::string::value_type());
+ ssize_t exe_path_len =
+ readlink("/proc/self/exe", &exe_path[0], exe_path.size());
+ if (exe_path_len < 0) {
+ PLOG(FATAL) << "readlink";
+ } else if (static_cast<size_t>(exe_path_len) >= exe_path.size()) {
+ LOG(FATAL) << "readlink";
+ }
+
+ exe_path.resize(exe_path_len);
+ return base::FilePath(exe_path);
+}
+
+} // namespace test
+} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/paths_mac.cc b/third_party/crashpad/crashpad/test/paths_mac.cc
index f3dca57..d16049e 100644
--- a/third_party/crashpad/crashpad/test/paths_mac.cc
+++ b/third_party/crashpad/crashpad/test/paths_mac.cc
@@ -26,10 +26,11 @@
base::FilePath Paths::Executable() {
uint32_t executable_length = 0;
_NSGetExecutablePath(nullptr, &executable_length);
- DCHECK_GT(executable_length, 1u);
+ CHECK_GT(executable_length, 1u);
+
std::string executable_path(executable_length - 1, std::string::value_type());
int rv = _NSGetExecutablePath(&executable_path[0], &executable_length);
- DCHECK_EQ(rv, 0);
+ CHECK_EQ(rv, 0);
return base::FilePath(executable_path);
}
diff --git a/third_party/crashpad/crashpad/test/paths_win.cc b/third_party/crashpad/crashpad/test/paths_win.cc
index 95d0264..947acd5 100644
--- a/third_party/crashpad/crashpad/test/paths_win.cc
+++ b/third_party/crashpad/crashpad/test/paths_win.cc
@@ -16,13 +16,17 @@
#include <windows.h>
+#include "base/logging.h"
+
namespace crashpad {
namespace test {
// static
base::FilePath Paths::Executable() {
wchar_t executable_path[_MAX_PATH];
- GetModuleFileName(nullptr, executable_path, sizeof(executable_path));
+ unsigned int len =
+ GetModuleFileName(nullptr, executable_path, arraysize(executable_path));
+ PCHECK(len != 0 && len < arraysize(executable_path)) << "GetModuleFileName";
return base::FilePath(executable_path);
}
diff --git a/third_party/crashpad/crashpad/test/scoped_temp_dir_posix.cc b/third_party/crashpad/crashpad/test/scoped_temp_dir_posix.cc
index 34db76f..036810369 100644
--- a/third_party/crashpad/crashpad/test/scoped_temp_dir_posix.cc
+++ b/third_party/crashpad/crashpad/test/scoped_temp_dir_posix.cc
@@ -15,10 +15,12 @@
#include "test/scoped_temp_dir.h"
#include <dirent.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "base/logging.h"
+#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/errors.h"
@@ -33,9 +35,25 @@
// static
base::FilePath ScopedTempDir::CreateTemporaryDirectory() {
- char dir_template[] = "/tmp/org.chromium.crashpad.test.XXXXXX";
- PCHECK(mkdtemp(dir_template)) << "mkdtemp " << dir_template;
- return base::FilePath(dir_template);
+ char* tmpdir = getenv("TMPDIR");
+ std::string dir;
+ if (tmpdir && tmpdir[0] != '\0') {
+ dir.assign(tmpdir);
+ } else {
+#if defined(OS_ANDROID)
+ dir.assign("/data/local/tmp");
+#else
+ dir.assign("/tmp");
+#endif
+ }
+
+ if (dir[dir.size() - 1] != '/') {
+ dir.append(1, '/');
+ }
+ dir.append("org.chromium.crashpad.test.XXXXXX");
+
+ PCHECK(mkdtemp(&dir[0])) << "mkdtemp " << dir;
+ return base::FilePath(dir);
}
// static
diff --git a/third_party/crashpad/crashpad/test/test.gyp b/third_party/crashpad/crashpad/test/test.gyp
index e8c1709c..666a68a 100644
--- a/third_party/crashpad/crashpad/test/test.gyp
+++ b/third_party/crashpad/crashpad/test/test.gyp
@@ -47,6 +47,7 @@
'multiprocess_posix.cc',
'paths.cc',
'paths.h',
+ 'paths_linux.cc',
'paths_mac.cc',
'paths_win.cc',
'scoped_temp_dir.cc',
@@ -76,6 +77,13 @@
},
}],
],
+ 'target_conditions': [
+ ['OS=="android"', {
+ 'sources/': [
+ ['include', '^paths_linux\\.cc$'],
+ ],
+ }],
+ ],
},
],
}
diff --git a/third_party/crashpad/crashpad/test/win/win_child_process.cc b/third_party/crashpad/crashpad/test/win/win_child_process.cc
index 73761aa..156aa6dc 100644
--- a/third_party/crashpad/crashpad/test/win/win_child_process.cc
+++ b/third_party/crashpad/crashpad/test/win/win_child_process.cc
@@ -142,7 +142,7 @@
// 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.
diff --git a/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc b/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc
index 9b38ed8..135d6d98 100644
--- a/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc
+++ b/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc
@@ -115,7 +115,7 @@
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 @@
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);
diff --git a/third_party/crashpad/crashpad/util/file/file_writer.cc b/third_party/crashpad/crashpad/util/file/file_writer.cc
index edbdb8d..94f41f14 100644
--- a/third_party/crashpad/crashpad/util/file/file_writer.cc
+++ b/third_party/crashpad/crashpad/util/file/file_writer.cc
@@ -73,9 +73,19 @@
iovec* iov = reinterpret_cast<iovec*>(&(*iovecs)[0]);
size_t remaining_iovecs = iovecs->size();
+#if defined(OS_ANDROID)
+ // Android does not expose the IOV_MAX macro, but makes its value available
+ // via sysconf(). See Android 7.0.0 bionic/libc/bionic/sysconf.cpp sysconf().
+ // Bionic defines IOV_MAX at bionic/libc/include/limits.h, but does not ship
+ // this file to the NDK as <limits.h>, substituting
+ // bionic/libc/include/bits/posix_limits.h.
+ const size_t kIovMax = sysconf(_SC_IOV_MAX);
+#else
+ const size_t kIovMax = IOV_MAX;
+#endif
+
while (size > 0) {
- size_t writev_iovec_count =
- std::min(remaining_iovecs, implicit_cast<size_t>(IOV_MAX));
+ size_t writev_iovec_count = std::min(remaining_iovecs, kIovMax);
ssize_t written =
HANDLE_EINTR(writev(file_handle_, iov, writev_iovec_count));
if (written < 0) {
diff --git a/third_party/crashpad/crashpad/util/misc/clock_test.cc b/third_party/crashpad/crashpad/util/misc/clock_test.cc
index c6e2c02a..d7f9cd7 100644
--- a/third_party/crashpad/crashpad/util/misc/clock_test.cc
+++ b/third_party/crashpad/crashpad/util/misc/clock_test.cc
@@ -14,11 +14,11 @@
#include "util/misc/clock.h"
-#include <stdint.h>
#include <sys/types.h>
#include <algorithm>
+#include "base/format_macros.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
@@ -85,8 +85,8 @@
for (size_t index = 0; index < arraysize(kTestData); ++index) {
const uint64_t nanoseconds = kTestData[index];
- SCOPED_TRACE(
- base::StringPrintf("index %zu, nanoseconds %llu", index, nanoseconds));
+ SCOPED_TRACE(base::StringPrintf(
+ "index %zu, nanoseconds %" PRIu64, index, nanoseconds));
TestSleepNanoseconds(nanoseconds);
}
diff --git a/third_party/crashpad/crashpad/util/misc/metrics.cc b/third_party/crashpad/crashpad/util/misc/metrics.cc
index f5ab07b..48be7ecd7 100644
--- a/third_party/crashpad/crashpad/util/misc/metrics.cc
+++ b/third_party/crashpad/crashpad/util/misc/metrics.cc
@@ -18,6 +18,16 @@
#include "base/metrics/sparse_histogram.h"
#include "build/build_config.h"
+#if defined(OS_MACOSX)
+#define METRICS_OS_NAME "Mac"
+#elif defined(OS_WIN)
+#define METRICS_OS_NAME "Win"
+#elif defined(OS_ANDROID)
+#define METRICS_OS_NAME "Android"
+#elif defined(OS_LINUX)
+#define METRICS_OS_NAME "Linux"
+#endif
+
namespace crashpad {
namespace {
@@ -79,12 +89,7 @@
// static
void Metrics::ExceptionCode(uint32_t exception_code) {
-#if defined(OS_WIN)
- static const char kExceptionCodeString[] = "Crashpad.ExceptionCode.Win";
-#elif defined(OS_MACOSX)
- static const char kExceptionCodeString[] = "Crashpad.ExceptionCode.Mac";
-#endif
- UMA_HISTOGRAM_SPARSE_SLOWLY(kExceptionCodeString,
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Crashpad.ExceptionCode." METRICS_OS_NAME,
static_cast<int32_t>(exception_code));
}
@@ -94,15 +99,9 @@
}
void Metrics::HandlerCrashed(uint32_t exception_code) {
-#if defined(OS_WIN)
- static const char kExceptionCodeString[] =
- "Crashpad.HandlerCrash.ExceptionCode.Win";
-#elif defined(OS_MACOSX)
- static const char kExceptionCodeString[] =
- "Crashpad.HandlerCrash.ExceptionCode.Mac";
-#endif
- UMA_HISTOGRAM_SPARSE_SLOWLY(kExceptionCodeString,
- static_cast<int32_t>(exception_code));
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Crashpad.HandlerCrash.ExceptionCode." METRICS_OS_NAME,
+ static_cast<int32_t>(exception_code));
}
} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/uuid.cc b/third_party/crashpad/crashpad/util/misc/uuid.cc
index 9ef55a6..e4b4b31 100644
--- a/third_party/crashpad/crashpad/util/misc/uuid.cc
+++ b/third_party/crashpad/crashpad/util/misc/uuid.cc
@@ -23,6 +23,7 @@
#include <string.h>
#include "base/logging.h"
+#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/sys_byteorder.h"
@@ -110,6 +111,16 @@
}
InitializeFromSystemUUID(&system_uuid);
return true;
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+ // Linux does not provide a UUID generator in a widely-available system
+ // library. uuid_generate() from libuuid is not available everywhere.
+ base::RandBytes(this, sizeof(*this));
+
+ // Set six bits per RFC 4122 §4.4 to identify this as a pseudo-random UUID.
+ data_3 = (4 << 12) | (data_3 & 0x0fff); // §4.1.3
+ data_4[0] = 0x80 | (data_4[0] & 0x3f); // §4.1.1
+
+ return true;
#else
#error Port.
#endif // OS_MACOSX
diff --git a/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc b/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc
index 6b7e4c43..403c000 100644
--- a/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc
+++ b/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc
@@ -122,7 +122,7 @@
for (size_t index = 0; index < arraysize(kTestData); ++index) {
const TestData& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %" PRIuS
- ", base 0x%llx, size 0x%llx",
+ ", base 0x%" PRIx64 ", size 0x%" PRIx64,
index,
testcase.base,
testcase.size));
@@ -173,7 +173,7 @@
for (size_t index = 0; index < arraysize(kTestData); ++index) {
const TestData& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf(
- "index %" PRIuS ", value 0x%llx", index, testcase.value));
+ "index %" PRIuS ", value 0x%" PRIx64, index, testcase.value));
EXPECT_EQ(testcase.expectation,
parent_range_32.ContainsValue(testcase.value));
@@ -230,7 +230,7 @@
for (size_t index = 0; index < arraysize(kTestData); ++index) {
const TestData& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %" PRIuS
- ", base 0x%llx, size 0x%llx",
+ ", base 0x%" PRIx64 ", size 0x%" PRIx64,
index,
testcase.base,
testcase.size));
diff --git a/third_party/crashpad/crashpad/util/posix/close_multiple.cc b/third_party/crashpad/crashpad/util/posix/close_multiple.cc
index fea7ca0..f1d7773 100644
--- a/third_party/crashpad/crashpad/util/posix/close_multiple.cc
+++ b/third_party/crashpad/crashpad/util/posix/close_multiple.cc
@@ -18,6 +18,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -25,12 +26,17 @@
#include <algorithm>
#include <memory>
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "build/build_config.h"
#include "util/misc/implicit_cast.h"
#include "util/numeric/safe_assignment.h"
+#if defined(OS_MACOSX)
+#include <sys/sysctl.h>
+#endif
+
// Everything in this file is expected to execute between fork() and exec(),
// so everything called here must be acceptable in this context. However,
// logging code that is not expected to execute under normal circumstances is
@@ -82,7 +88,7 @@
bool CloseMultipleNowOrOnExecUsingFDDir(int fd, int preserve_fd) {
#if defined(OS_MACOSX)
const char kFDDir[] = "/dev/fd";
-#elif defined(OS_LINUX)
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
const char kFDDir[] = "/proc/self/fd";
#endif
@@ -100,10 +106,19 @@
return false;
}
- dirent entry;
dirent* result;
- int rv;
- while ((rv = readdir_r(dir, &entry, &result)) == 0 && result != nullptr) {
+#if defined(OS_LINUX)
+ // readdir_r() is deprecated as of glibc 2.24. See
+ // https://sourceware.org/bugzilla/show_bug.cgi?id=19056 and
+ // https://git.kernel.org/cgit/docs/man-pages/man-pages.git/commit?id=0c52f6d623636a61eacd0f7b7a3bb942793a2a05.
+ const char kReaddirName[] = "readdir";
+ while ((errno = 0, result = readdir(dir)) != nullptr)
+#else
+ const char kReaddirName[] = "readdir_r";
+ dirent entry;
+ while ((errno = readdir_r(dir, &entry, &result)) == 0 && result != nullptr)
+#endif
+ {
const char* entry_name = &(*result->d_name);
if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) {
continue;
@@ -127,6 +142,11 @@
}
}
+ if (errno != 0) {
+ PLOG(WARNING) << kReaddirName;
+ return false;
+ }
+
return true;
}
@@ -141,12 +161,68 @@
// system’s file descriptor limit. Check a few values and use the highest as
// the limit, because these may be based on the file descriptor limit set by
// setrlimit(), and higher-numbered file descriptors may have been opened
- // prior to the limit being lowered. For Mac OS X, see 10.9.2
- // Libc-997.90.3/gen/FreeBSD/sysconf.c sysconf() and 10.9.4
- // xnu-2422.110.17/bsd/kern/kern_descrip.c getdtablesize(), which both return
- // the current RLIMIT_NOFILE value, not the maximum possible file descriptor.
- int max_fd = std::max(implicit_cast<int>(sysconf(_SC_OPEN_MAX)), OPEN_MAX);
+ // prior to the limit being lowered. On both macOS and Linux glibc, both
+ // sysconf() and getdtablesize() return the current RLIMIT_NOFILE value, not
+ // the maximum possible file descriptor. For macOS, see 10.11.5
+ // Libc-1082.50.1/gen/FreeBSD/sysconf.c sysconf() and 10.11.6
+ // xnu-3248.60.10/bsd/kern/kern_descrip.c getdtablesize(). For Linux glibc,
+ // see glibc-2.24/sysdeps/posix/sysconf.c __sysconf() and
+ // glibc-2.24/sysdeps/posix/getdtsz.c __getdtablesize(). For Android, see
+ // 7.0.0 bionic/libc/bionic/sysconf.cpp sysconf() and
+ // bionic/libc/bionic/ndk_cruft.cpp getdtablesize().
+ int max_fd = implicit_cast<int>(sysconf(_SC_OPEN_MAX));
+
+#if !defined(OS_ANDROID)
+ // getdtablesize() was removed effective Android 5.0.0 (API 21). Since it
+ // returns the same thing as the sysconf() above, just skip it. See
+ // https://android.googlesource.com/platform/bionic/+/462abab12b074c62c0999859e65d5a32ebb41951.
max_fd = std::max(max_fd, getdtablesize());
+#endif
+
+#if !defined(OS_LINUX) || defined(OPEN_MAX)
+ // Linux does not provide OPEN_MAX. See
+ // https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/include/linux/limits.h?id=77293034696e3e0b6c8b8fc1f96be091104b3d2b.
+ max_fd = std::max(max_fd, OPEN_MAX);
+#endif
+
+ // Consult a sysctl to determine the system-wide limit on the maximum number
+ // of open files per process. Note that it is possible to change this limit
+ // while the system is running, but it’s still a better upper bound than the
+ // current RLIMIT_NOFILE value.
+
+#if defined(OS_MACOSX)
+ // See 10.11.6 xnu-3248.60.10/bsd/kern/kern_resource.c maxfilesperproc,
+ // referenced by dosetrlimit().
+ int oid[] = {CTL_KERN, KERN_MAXFILESPERPROC};
+ int maxfilesperproc;
+ size_t maxfilesperproc_size = sizeof(maxfilesperproc);
+ if (sysctl(oid,
+ arraysize(oid),
+ &maxfilesperproc,
+ &maxfilesperproc_size,
+ nullptr,
+ 0) == 0) {
+ max_fd = std::max(max_fd, maxfilesperproc);
+ } else {
+ PLOG(WARNING) << "sysctl";
+ }
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+ // See linux-4.4.27/fs/file.c sysctl_nr_open, referenced by kernel/sys.c
+ // do_prlimit() and kernel/sysctl.c fs_table. Inability to open this file is
+ // not considered an error, because /proc may not be available or usable.
+ {
+ base::ScopedFILE nr_open_file(fopen("/proc/sys/fs/nr_open", "r"));
+ if (nr_open_file.get() != nullptr) {
+ int nr_open;
+ if (fscanf(nr_open_file.get(), "%d\n", &nr_open) == 1 &&
+ feof(nr_open_file.get())) {
+ max_fd = std::max(max_fd, nr_open);
+ } else {
+ LOG(WARNING) << "/proc/sys/fs/nr_open format error";
+ }
+ }
+ }
+#endif
for (int entry_fd = fd; entry_fd < max_fd; ++entry_fd) {
if (entry_fd != preserve_fd) {
diff --git a/third_party/crashpad/crashpad/util/posix/drop_privileges.cc b/third_party/crashpad/crashpad/util/posix/drop_privileges.cc
index 3fb0ab4..5c80990 100644
--- a/third_party/crashpad/crashpad/util/posix/drop_privileges.cc
+++ b/third_party/crashpad/crashpad/util/posix/drop_privileges.cc
@@ -71,7 +71,7 @@
CHECK_EQ(setegid(egid), -1);
}
}
-#elif defined(OS_LINUX)
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
PCHECK(setresgid(gid, gid, gid) == 0) << "setresgid";
PCHECK(setresuid(uid, uid, uid) == 0) << "setresuid";
diff --git a/third_party/crashpad/crashpad/util/posix/process_info.h b/third_party/crashpad/crashpad/util/posix/process_info.h
index 5aeda25a..1aa23d8c 100644
--- a/third_party/crashpad/crashpad/util/posix/process_info.h
+++ b/third_party/crashpad/crashpad/util/posix/process_info.h
@@ -15,7 +15,6 @@
#ifndef CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_
#define CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_
-#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
@@ -30,6 +29,7 @@
#if defined(OS_MACOSX)
#include <mach/mach.h>
+#include <sys/sysctl.h>
#endif
namespace crashpad {
diff --git a/third_party/crashpad/crashpad/util/posix/process_info_test.cc b/third_party/crashpad/crashpad/util/posix/process_info_test.cc
index dd0844b..7e28e5f4c 100644
--- a/third_party/crashpad/crashpad/util/posix/process_info_test.cc
+++ b/third_party/crashpad/crashpad/util/posix/process_info_test.cc
@@ -15,12 +15,14 @@
#include "util/posix/process_info.h"
#include <time.h>
+#include <stdio.h>
#include <unistd.h>
#include <set>
#include <string>
#include <vector>
+#include "base/files/scoped_file.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/errors.h"
@@ -98,6 +100,34 @@
#if defined(OS_MACOSX)
int expect_argc = *_NSGetArgc();
char** expect_argv = *_NSGetArgv();
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+ std::vector<std::string> expect_arg_vector;
+ {
+ base::ScopedFILE cmdline(fopen("/proc/self/cmdline", "r"));
+ ASSERT_NE(nullptr, cmdline.get()) << ErrnoMessage("fopen");
+
+ int expect_arg_char;
+ std::string expect_arg_string;
+ while ((expect_arg_char = fgetc(cmdline.get())) != EOF) {
+ if (expect_arg_char != '\0') {
+ expect_arg_string.append(1, expect_arg_char);
+ } else {
+ expect_arg_vector.push_back(expect_arg_string);
+ expect_arg_string.clear();
+ }
+ }
+ ASSERT_EQ(0, ferror(cmdline.get())) << ErrnoMessage("fgetc");
+ ASSERT_TRUE(expect_arg_string.empty());
+ }
+
+ std::vector<const char*> expect_argv_storage;
+ for (const std::string& expect_arg_string : expect_arg_vector) {
+ expect_argv_storage.push_back(expect_arg_string.c_str());
+ }
+
+ int expect_argc = expect_argv_storage.size();
+ const char* const* expect_argv =
+ !expect_argv_storage.empty() ? &expect_argv_storage[0] : nullptr;
#else
#error Obtain expect_argc and expect_argv correctly on your system.
#endif
diff --git a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
index c952e82..4ea2300 100644
--- a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
+++ b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
@@ -14,9 +14,9 @@
#include "util/posix/symbolic_constants_posix.h"
+#include <signal.h>
#include <string.h>
#include <sys/types.h>
-#include <sys/signal.h>
#include "base/macros.h"
#include "base/strings/stringprintf.h"
@@ -64,7 +64,7 @@
"INFO",
"USR1",
"USR2",
-#elif defined(OS_LINUX)
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
// sed -Ene 's/^#define[[:space:]]SIG([[:alnum:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p'
// /usr/include/asm-generic/signal.h
// and fix up by removing SIGIOT, SIGLOST, SIGUNUSED, and SIGRTMIN.
@@ -101,7 +101,7 @@
"SYS",
#endif
};
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_ANDROID)
// NSIG is 64 to account for real-time signals.
static_assert(arraysize(kSignalNames) == 32, "kSignalNames length");
#else
diff --git a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc
index 72e37bd..ddbe336 100644
--- a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc
+++ b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc
@@ -14,7 +14,7 @@
#include "util/posix/symbolic_constants_posix.h"
-#include <sys/signal.h>
+#include <signal.h>
#include <sys/types.h>
#include "base/macros.h"
@@ -65,7 +65,7 @@
#if defined(OS_MACOSX)
{SIGEMT, "SIGEMT", "EMT"},
{SIGINFO, "SIGINFO", "INFO"},
-#elif defined(OS_LINUX)
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
{SIGPWR, "SIGPWR", "PWR"},
{SIGSTKFLT, "SIGSTKFLT", "STKFLT"},
#endif
@@ -120,7 +120,7 @@
kSignalTestData[index].short_name);
}
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_ANDROID)
// NSIG is 64 to account for real-time signals.
const int kSignalCount = 32;
#else
diff --git a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc
index 8ab949e..28f6ea11 100644
--- a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc
+++ b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc
@@ -16,6 +16,7 @@
#include <ctype.h>
#include <errno.h>
+#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
@@ -102,12 +103,24 @@
: public StringToUnsignedIntegerTraits<unsigned int, unsigned long> {
static LongType Convert(const char* str, char** end, int base) {
if (str[0] == '-') {
+ *end = const_cast<char*>(str);
return 0;
}
return strtoul(str, end, base);
}
};
+struct StringToUnsignedInt64Traits
+ : public StringToUnsignedIntegerTraits<uint64_t, uint64_t> {
+ static LongType Convert(const char* str, char** end, int base) {
+ if (str[0] == '-') {
+ *end = const_cast<char*>(str);
+ return 0;
+ }
+ return strtoull(str, end, base);
+ }
+};
+
template <typename Traits>
bool StringToIntegerInternal(const base::StringPiece& string,
typename Traits::IntType* number) {
@@ -153,4 +166,8 @@
return StringToIntegerInternal<StringToUnsignedIntTraits>(string, number);
}
+bool StringToNumber(const base::StringPiece& string, uint64_t* number) {
+ return StringToIntegerInternal<StringToUnsignedInt64Traits>(string, number);
+}
+
} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h
index dbc4611e..8641a69 100644
--- a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h
+++ b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h
@@ -56,6 +56,7 @@
//! 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
diff --git a/third_party/crashpad/crashpad/util/string/split_string.cc b/third_party/crashpad/crashpad/util/string/split_string.cc
index 25048b5..9d0d675 100644
--- a/third_party/crashpad/crashpad/util/string/split_string.cc
+++ b/third_party/crashpad/crashpad/util/string/split_string.cc
@@ -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 @@
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
diff --git a/third_party/crashpad/crashpad/util/string/split_string.h b/third_party/crashpad/crashpad/util/string/split_string.h
index bb72904..4ea89d2 100644
--- a/third_party/crashpad/crashpad/util/string/split_string.h
+++ b/third_party/crashpad/crashpad/util/string/split_string.h
@@ -16,6 +16,7 @@
#define CRASHPAD_UTIL_STRING_SPLIT_STRING_H_
#include <string>
+#include <vector>
namespace crashpad {
@@ -31,10 +32,17 @@
//! \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
diff --git a/third_party/crashpad/crashpad/util/string/split_string_test.cc b/third_party/crashpad/crashpad/util/string/split_string_test.cc
index 7d205319..2da5a2a 100644
--- a/third_party/crashpad/crashpad/util/string/split_string_test.cc
+++ b/third_party/crashpad/crashpad/util/string/split_string_test.cc
@@ -20,42 +20,74 @@
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
diff --git a/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc b/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc
index 7f67e45..dec111e7 100644
--- a/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc
+++ b/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc
@@ -46,14 +46,14 @@
}
~ThreadLogMessagesMaster() {
- DCHECK_EQ(logging::GetLogMessageHandler(), LogMessageHandler);
+ DCHECK_EQ(logging::GetLogMessageHandler(), &LogMessageHandler);
logging::SetLogMessageHandler(nullptr);
tls_.Free();
}
void SetThreadMessageList(std::vector<std::string>* message_list) {
- DCHECK_EQ(logging::GetLogMessageHandler(), LogMessageHandler);
+ DCHECK_EQ(logging::GetLogMessageHandler(), &LogMessageHandler);
DCHECK_NE(tls_.Get() != nullptr, message_list != nullptr);
tls_.Set(message_list);
}
diff --git a/third_party/crashpad/crashpad/util/util.gyp b/third_party/crashpad/crashpad/util/util.gyp
index e4bb855..8269bd4 100644
--- a/third_party/crashpad/crashpad/util/util.gyp
+++ b/third_party/crashpad/crashpad/util/util.gyp
@@ -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',
@@ -282,6 +284,10 @@
},
}],
],
+ }, { # else: OS!="win"
+ 'sources!': [
+ 'win/capture_context.asm',
+ ],
}],
],
},
diff --git a/third_party/crashpad/crashpad/util/util_test.gyp b/third_party/crashpad/crashpad/util/util_test.gyp
index 28dab4f7..9efe162a4 100644
--- a/third_party/crashpad/crashpad/util/util_test.gyp
+++ b/third_party/crashpad/crashpad/util/util_test.gyp
@@ -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',
diff --git a/third_party/crashpad/crashpad/util/win/exception_handler_server.cc b/third_party/crashpad/crashpad/util/win/exception_handler_server.cc
index 39575df..70955c8 100644
--- a/third_party/crashpad/crashpad/util/win/exception_handler_server.cc
+++ b/third_party/crashpad/crashpad/util/win/exception_handler_server.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 @@
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 @@
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,47 @@
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(),
+ static_cast<DWORD>(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) {
@@ -457,6 +413,16 @@
return true;
}
+ case ClientToServerMessage::kPing: {
+ // No action required, the fact that the message was processed is
+ // sufficient.
+ ServerToClientMessage shutdown_response = {};
+ LoggingWriteFile(service_context.pipe(),
+ &shutdown_response,
+ sizeof(shutdown_response));
+ return false;
+ }
+
case ClientToServerMessage::kRegister:
// Handled below.
break;
@@ -513,6 +479,12 @@
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,
diff --git a/third_party/crashpad/crashpad/util/win/exception_handler_server.h b/third_party/crashpad/crashpad/util/win/exception_handler_server.h
index 34c52783..69f760d 100644
--- a/third_party/crashpad/crashpad/util/win/exception_handler_server.h
+++ b/third_party/crashpad/crashpad/util/win/exception_handler_server.h
@@ -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 @@
//! \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 @@
//! 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);
diff --git a/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc b/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc
index 54a04870..e94b91d 100644
--- a/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc
+++ b/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc
@@ -81,10 +81,12 @@
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 @@
ADD_FAILURE();
return EXIT_FAILURE;
}
- if (!client.UseHandler()) {
- ADD_FAILURE();
- return EXIT_FAILURE;
- }
WriteWString(WritePipeHandle(), L"OK");
return EXIT_SUCCESS;
}
diff --git a/third_party/crashpad/crashpad/util/win/initial_client_data.cc b/third_party/crashpad/crashpad/util/win/initial_client_data.cc
new file mode 100644
index 0000000..cbeecf1
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/win/initial_client_data.cc
@@ -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
diff --git a/third_party/crashpad/crashpad/util/win/initial_client_data.h b/third_party/crashpad/crashpad/util/win/initial_client_data.h
new file mode 100644
index 0000000..37f0964
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/win/initial_client_data.h
@@ -0,0 +1,122 @@
+// 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_;
+
+#if _MSC_VER < 1900
+ // The default copy and assignment constructors are fine, as we don't take
+ // ownership of the handles. They also shouldn't be required for how we use
+ // this class, however, VS2013 seems to think they are.
+#else
+ DISALLOW_COPY_AND_ASSIGN(InitialClientData);
+#endif // _MSC_VER < 1900
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_INITIAL_CLIENT_DATA_H_
diff --git a/third_party/crashpad/crashpad/util/win/initial_client_data_test.cc b/third_party/crashpad/crashpad/util/win/initial_client_data_test.cc
new file mode 100644
index 0000000..5ac7be9
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/win/initial_client_data_test.cc
@@ -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
diff --git a/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc b/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc
index eff27e28..1763841 100644
--- a/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc
+++ b/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc
@@ -15,15 +15,18 @@
#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 {
bool SendToCrashHandlerServer(const base::string16& pipe_name,
- const crashpad::ClientToServerMessage& message,
- crashpad::ServerToClientMessage* response) {
+ const ClientToServerMessage& message,
+ ServerToClientMessage* response) {
// Retry CreateFile() in a loop. If the handler isn’t actively waiting in
// ConnectNamedPipe() on a pipe instance because it’s busy doing something
// else, CreateFile() will fail with ERROR_PIPE_BUSY. WaitNamedPipe() waits
@@ -39,7 +42,7 @@
// around the same time as its client, something external to this code must be
// done to guarantee correct ordering. When the client starts the handler
// itself, CrashpadClient::StartHandler() provides this synchronization.
- for (int tries = 0;;) {
+ for (;;) {
ScopedFileHANDLE pipe(
CreateFile(pipe_name.c_str(),
GENERIC_READ | GENERIC_WRITE,
@@ -49,13 +52,12 @@
SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION,
nullptr));
if (!pipe.is_valid()) {
- if (++tries == 5 || GetLastError() != ERROR_PIPE_BUSY) {
+ if (GetLastError() != ERROR_PIPE_BUSY) {
PLOG(ERROR) << "CreateFile";
return false;
}
- if (!WaitNamedPipe(pipe_name.c_str(), 1000) &&
- GetLastError() != ERROR_SEM_TIMEOUT) {
+ if (!WaitNamedPipe(pipe_name.c_str(), NMPWAIT_WAIT_FOREVER)) {
PLOG(ERROR) << "WaitNamedPipe";
return false;
}
@@ -72,7 +74,7 @@
BOOL result = TransactNamedPipe(
pipe.get(),
// This is [in], but is incorrectly declared non-const.
- const_cast<crashpad::ClientToServerMessage*>(&message),
+ const_cast<ClientToServerMessage*>(&message),
sizeof(message),
response,
sizeof(*response),
@@ -91,4 +93,47 @@
}
}
+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
diff --git a/third_party/crashpad/crashpad/util/win/registration_protocol_win.h b/third_party/crashpad/crashpad/util/win/registration_protocol_win.h
index c57c6e20..122098358 100644
--- a/third_party/crashpad/crashpad/util/win/registration_protocol_win.h
+++ b/third_party/crashpad/crashpad/util/win/registration_protocol_win.h
@@ -82,8 +82,14 @@
enum Type : uint32_t {
//! \brief For RegistrationRequest.
kRegister,
+
//! \brief For ShutdownRequest.
kShutdown,
+
+ //! \brief An empty message sent by the initial client in asynchronous mode.
+ //! No data is required, this just confirms that the server is ready to
+ //! accept client registrations.
+ kPing,
} type;
union {
@@ -128,6 +134,17 @@
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_
diff --git a/third_party/crashpad/crashpad/util/win/termination_codes.h b/third_party/crashpad/crashpad/util/win/termination_codes.h
index 1cb69b1..1bcbf5dd 100644
--- a/third_party/crashpad/crashpad/util/win/termination_codes.h
+++ b/third_party/crashpad/crashpad/util/win/termination_codes.h
@@ -29,7 +29,7 @@
//! \brief A dump was requested for a client that was never registered with
//! the crash handler.
- kTerminationCodeUseHandlerNotCalled = 0xffff7003,
+ kTerminationCodeNotConnectedToHandler = 0xffff7003,
};
} // namespace crashpad