mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-20 18:53:47 +00:00
Merge master 6128f38e28eb into doc
This commit is contained in:
commit
5fcdc7528b
28
DEPS
28
DEPS
@ -38,7 +38,7 @@ deps = {
|
||||
|
||||
'crashpad/third_party/mini_chromium/mini_chromium':
|
||||
Var('chromium_git') + '/chromium/mini_chromium@' +
|
||||
'f65519e442d23498937251e680a3b113927613b0',
|
||||
'3a2d52d74c9af5277bf6456cc00ae728f89c4898',
|
||||
'crashpad/third_party/zlib/zlib':
|
||||
Var('chromium_git') + '/chromium/src/third_party/zlib@' +
|
||||
'13dc246a58e4b72104d35f9b1809af95221ebda7',
|
||||
@ -71,6 +71,19 @@ hooks = [
|
||||
'buildtools/win/clang-format.exe.sha1',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'clang_format_linux',
|
||||
'pattern': '.',
|
||||
'action': [
|
||||
'download_from_google_storage',
|
||||
'--platform=^linux2?$',
|
||||
'--no_resume',
|
||||
'--no_auth',
|
||||
'--bucket=chromium-clang-format',
|
||||
'--sha1_file',
|
||||
'buildtools/linux64/clang-format.sha1',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'gn_mac',
|
||||
'pattern': '.',
|
||||
@ -97,6 +110,19 @@ hooks = [
|
||||
'buildtools/win/gn.exe.sha1',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'gn_linux',
|
||||
'pattern': '.',
|
||||
'action': [
|
||||
'download_from_google_storage',
|
||||
'--platform=^linux2?$',
|
||||
'--no_resume',
|
||||
'--no_auth',
|
||||
'--bucket=chromium-gn',
|
||||
'--sha1_file',
|
||||
'buildtools/linux64/gn.sha1',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'gyp',
|
||||
'pattern': '\.gypi?$',
|
||||
|
@ -299,9 +299,10 @@ CrashReportDatabaseMac::PrepareNewCrashReport(NewReport** out_report) {
|
||||
base_dir_.Append(kWriteDirectory)
|
||||
.Append(report->uuid.ToString() + "." + kCrashReportFileExtension);
|
||||
|
||||
report->handle = HANDLE_EINTR(open(report->path.value().c_str(),
|
||||
O_CREAT | O_WRONLY | O_EXCL | O_EXLOCK,
|
||||
0600));
|
||||
report->handle = HANDLE_EINTR(
|
||||
open(report->path.value().c_str(),
|
||||
O_WRONLY | O_EXLOCK | O_CREAT | O_EXCL | O_NOCTTY | O_CLOEXEC,
|
||||
0600));
|
||||
if (report->handle < 0) {
|
||||
PLOG(ERROR) << "open " << report->path.value();
|
||||
return kFileSystemError;
|
||||
@ -612,8 +613,9 @@ CrashReportDatabase::OperationStatus CrashReportDatabaseMac::RequestUpload(
|
||||
// static
|
||||
base::ScopedFD CrashReportDatabaseMac::ObtainReportLock(
|
||||
const base::FilePath& path) {
|
||||
int fd = HANDLE_EINTR(open(path.value().c_str(),
|
||||
O_RDONLY | O_EXLOCK | O_NONBLOCK));
|
||||
int fd = HANDLE_EINTR(
|
||||
open(path.value().c_str(),
|
||||
O_RDONLY | O_NONBLOCK | O_EXLOCK | O_NOCTTY | O_CLOEXEC));
|
||||
PLOG_IF(ERROR, fd < 0) << "open lock " << path.value();
|
||||
return base::ScopedFD(fd);
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ enum MINIDUMP_STREAM_TYPE {
|
||||
UnloadedModuleListStream = 14,
|
||||
|
||||
//! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
|
||||
//! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
|
||||
//! MINIDUMP_MISC_INFO_3, MINIDUMP_MISC_INFO_4, and MINIDUMP_MISC_INFO_5.
|
||||
//!
|
||||
//! More recent versions of this stream are supersets of earlier versions.
|
||||
//!
|
||||
@ -753,6 +753,33 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST {
|
||||
uint32_t NumberOfEntries;
|
||||
};
|
||||
|
||||
//! \brief Information about XSAVE-managed state stored within CPU-specific
|
||||
//! context structures.
|
||||
struct __attribute__((packed, aligned(4))) XSTATE_CONFIG_FEATURE_MSC_INFO {
|
||||
//! \brief The size of this structure, in bytes. This value is
|
||||
//! `sizeof(XSTATE_CONFIG_FEATURE_MSC_INFO)`.
|
||||
uint32_t SizeOfInfo;
|
||||
|
||||
//! \brief The size of a CPU-specific context structure carrying all XSAVE
|
||||
//! state components described by this structure.
|
||||
//!
|
||||
//! Equivalent to the value returned by `InitializeContext()` in \a
|
||||
//! ContextLength.
|
||||
uint32_t ContextSize;
|
||||
|
||||
//! \brief The XSAVE state-component bitmap, XSAVE_BV.
|
||||
//!
|
||||
//! See Intel Software Developer’s Manual, Volume 1: Basic Architecture
|
||||
//! (253665-060), 13.4.2 “XSAVE Header”.
|
||||
uint64_t EnabledFeatures;
|
||||
|
||||
//! \brief The location of each state component within a CPU-specific context
|
||||
//! structure.
|
||||
//!
|
||||
//! This array is indexed by bit position numbers used in #EnabledFeatures.
|
||||
XSTATE_FEATURE Features[MAXIMUM_XSTATE_FEATURES];
|
||||
};
|
||||
|
||||
//! \anchor MINIDUMP_MISCx
|
||||
//! \name MINIDUMP_MISC*
|
||||
//!
|
||||
@ -805,6 +832,10 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST {
|
||||
//! - MINIDUMP_MISC_INFO_4::BuildString
|
||||
//! - MINIDUMP_MISC_INFO_4::DbgBldStr
|
||||
#define MINIDUMP_MISC4_BUILDSTRING 0x00000100
|
||||
|
||||
//! \brief MINIDUMP_MISC_INFO_5::ProcessCookie is valid.
|
||||
#define MINIDUMP_MISC5_PROCESS_COOKIE 0x00000200
|
||||
|
||||
//! \}
|
||||
|
||||
//! \brief Information about the process that the minidump file contains a
|
||||
@ -814,6 +845,7 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST {
|
||||
//! \sa MINIDUMP_MISC_INFO_2
|
||||
//! \sa MINIDUMP_MISC_INFO_3
|
||||
//! \sa MINIDUMP_MISC_INFO_4
|
||||
//! \sa MINIDUMP_MISC_INFO_5
|
||||
//! \sa MINIDUMP_MISC_INFO_N
|
||||
struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO {
|
||||
//! \brief The size of the structure.
|
||||
@ -854,6 +886,7 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO {
|
||||
//! \sa MINIDUMP_MISC_INFO
|
||||
//! \sa MINIDUMP_MISC_INFO_3
|
||||
//! \sa MINIDUMP_MISC_INFO_4
|
||||
//! \sa MINIDUMP_MISC_INFO_5
|
||||
//! \sa MINIDUMP_MISC_INFO_N
|
||||
struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_2
|
||||
: public MINIDUMP_MISC_INFO {
|
||||
@ -885,6 +918,7 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_2
|
||||
//! \sa MINIDUMP_MISC_INFO
|
||||
//! \sa MINIDUMP_MISC_INFO_2
|
||||
//! \sa MINIDUMP_MISC_INFO_4
|
||||
//! \sa MINIDUMP_MISC_INFO_5
|
||||
//! \sa MINIDUMP_MISC_INFO_N
|
||||
struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_3
|
||||
: public MINIDUMP_MISC_INFO_2 {
|
||||
@ -946,6 +980,7 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_3
|
||||
//! \sa MINIDUMP_MISC_INFO
|
||||
//! \sa MINIDUMP_MISC_INFO_2
|
||||
//! \sa MINIDUMP_MISC_INFO_3
|
||||
//! \sa MINIDUMP_MISC_INFO_5
|
||||
//! \sa MINIDUMP_MISC_INFO_N
|
||||
struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4
|
||||
: public MINIDUMP_MISC_INFO_3 {
|
||||
@ -968,8 +1003,31 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4
|
||||
base::char16 DbgBldStr[40];
|
||||
};
|
||||
|
||||
//! \brief Information about the process that the minidump file contains a
|
||||
//! snapshot of, as well as the system that hosted that process.
|
||||
//!
|
||||
//! This structure variant is used on Windows 10 and later.
|
||||
//!
|
||||
//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*"
|
||||
//! \sa MINIDUMP_MISC_INFO
|
||||
//! \sa MINIDUMP_MISC_INFO_2
|
||||
//! \sa MINIDUMP_MISC_INFO_3
|
||||
//! \sa MINIDUMP_MISC_INFO_4
|
||||
//! \sa MINIDUMP_MISC_INFO_N
|
||||
struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_5
|
||||
: public MINIDUMP_MISC_INFO_4 {
|
||||
//! \brief Information about XSAVE-managed state stored within CPU-specific
|
||||
//! context structures.
|
||||
//!
|
||||
//! This information can be used to locate state components within
|
||||
//! CPU-specific context structures.
|
||||
XSTATE_CONFIG_FEATURE_MSC_INFO XStateData;
|
||||
|
||||
uint32_t ProcessCookie;
|
||||
};
|
||||
|
||||
//! \brief The latest known version of the MINIDUMP_MISC_INFO structure.
|
||||
typedef MINIDUMP_MISC_INFO_4 MINIDUMP_MISC_INFO_N;
|
||||
typedef MINIDUMP_MISC_INFO_5 MINIDUMP_MISC_INFO_N;
|
||||
|
||||
//! \brief Describes a region of memory.
|
||||
struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_INFO {
|
||||
|
@ -112,6 +112,66 @@
|
||||
#define PF_RDTSCP_INSTRUCTION_AVAILABLE 32
|
||||
//! \}
|
||||
|
||||
//! \anchor PAGE_x
|
||||
//! \name PAGE_*
|
||||
//!
|
||||
//! \brief Memory protection constants for MINIDUMP_MEMORY_INFO::Protect and
|
||||
//! MINIDUMP_MEMORY_INFO::AllocationProtect.
|
||||
//! \{
|
||||
#define PAGE_NOACCESS 0x1
|
||||
#define PAGE_READONLY 0x2
|
||||
#define PAGE_READWRITE 0x4
|
||||
#define PAGE_WRITECOPY 0x8
|
||||
#define PAGE_EXECUTE 0x10
|
||||
#define PAGE_EXECUTE_READ 0x20
|
||||
#define PAGE_EXECUTE_READWRITE 0x40
|
||||
#define PAGE_EXECUTE_WRITECOPY 0x80
|
||||
#define PAGE_GUARD 0x100
|
||||
#define PAGE_NOCACHE 0x200
|
||||
#define PAGE_WRITECOMBINE 0x400
|
||||
//! \}
|
||||
|
||||
//! \anchor MEM_x
|
||||
//! \name MEM_*
|
||||
//!
|
||||
//! \brief Memory state and type constants for MINIDUMP_MEMORY_INFO::State and
|
||||
//! MINIDUMP_MEMORY_INFO::Type.
|
||||
//! \{
|
||||
#define MEM_COMMIT 0x1000
|
||||
#define MEM_RESERVE 0x2000
|
||||
#define MEM_DECOMMIT 0x4000
|
||||
#define MEM_RELEASE 0x8000
|
||||
#define MEM_FREE 0x10000
|
||||
#define MEM_PRIVATE 0x20000
|
||||
#define MEM_MAPPED 0x40000
|
||||
#define MEM_RESET 0x80000
|
||||
//! \}
|
||||
|
||||
//! \brief The maximum number of distinct identifiable features that could
|
||||
//! possibly be carried in an XSAVE area.
|
||||
//!
|
||||
//! This corresponds to the number of bits in the XSAVE state-component bitmap,
|
||||
//! XSAVE_BV. See Intel Software Developer’s Manual, Volume 1: Basic
|
||||
//! Architecture (253665-060), 13.4.2 “XSAVE Header”.
|
||||
#define MAXIMUM_XSTATE_FEATURES (64)
|
||||
|
||||
//! \brief The location of a single state component within an XSAVE area.
|
||||
struct XSTATE_FEATURE {
|
||||
//! \brief The location of a state component within a CPU-specific context
|
||||
//! structure.
|
||||
//!
|
||||
//! This is equivalent to the difference (`ptrdiff_t`) between the return
|
||||
//! value of `LocateXStateFeature()` and its \a Context argument.
|
||||
uint32_t Offset;
|
||||
|
||||
//! \brief The size of a state component with a CPU-specific context
|
||||
//! structure.
|
||||
//!
|
||||
//! This is equivalent to the size returned by `LocateXStateFeature()` in \a
|
||||
//! Length.
|
||||
uint32_t Size;
|
||||
};
|
||||
|
||||
//! \anchor IMAGE_DEBUG_MISC_x
|
||||
//! \name IMAGE_DEBUG_MISC_*
|
||||
//!
|
||||
@ -187,39 +247,4 @@ struct IMAGE_DEBUG_MISC {
|
||||
#define VER_PLATFORM_WIN32_NT 2
|
||||
//! \}
|
||||
|
||||
//! \anchor PAGE_x
|
||||
//! \name PAGE_*
|
||||
//!
|
||||
//! \brief Memory protection constants for MINIDUMP_MEMORY_INFO::Protect and
|
||||
//! MINIDUMP_MEMORY_INFO::AllocationProtect.
|
||||
//! \{
|
||||
#define PAGE_NOACCESS 0x1
|
||||
#define PAGE_READONLY 0x2
|
||||
#define PAGE_READWRITE 0x4
|
||||
#define PAGE_WRITECOPY 0x8
|
||||
#define PAGE_EXECUTE 0x10
|
||||
#define PAGE_EXECUTE_READ 0x20
|
||||
#define PAGE_EXECUTE_READWRITE 0x40
|
||||
#define PAGE_EXECUTE_WRITECOPY 0x80
|
||||
#define PAGE_GUARD 0x100
|
||||
#define PAGE_NOCACHE 0x200
|
||||
#define PAGE_WRITECOMBINE 0x400
|
||||
//! \}
|
||||
|
||||
//! \anchor MEM_x
|
||||
//! \name MEM_*
|
||||
//!
|
||||
//! \brief Memory state and type constants for MINIDUMP_MEMORY_INFO::State and
|
||||
//! MINIDUMP_MEMORY_INFO::Type.
|
||||
//! \{
|
||||
#define MEM_COMMIT 0x1000
|
||||
#define MEM_RESERVE 0x2000
|
||||
#define MEM_DECOMMIT 0x4000
|
||||
#define MEM_RELEASE 0x8000
|
||||
#define MEM_FREE 0x10000
|
||||
#define MEM_PRIVATE 0x20000
|
||||
#define MEM_MAPPED 0x40000
|
||||
#define MEM_RESET 0x80000
|
||||
//! \}
|
||||
|
||||
#endif // CRASHPAD_COMPAT_NON_WIN_WINNT_H_
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.12
|
||||
# Doxyfile 1.8.13
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@ -2362,6 +2362,11 @@ DIAFILE_DIRS =
|
||||
|
||||
PLANTUML_JAR_PATH =
|
||||
|
||||
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
|
||||
# configuration file for plantuml.
|
||||
|
||||
PLANTUML_CFG_FILE =
|
||||
|
||||
# When using plantuml, the specified paths are searched for files specified by
|
||||
# the !include statement in a plantuml block.
|
||||
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "util/mach/child_port_handshake.h"
|
||||
#include "util/mach/mach_extensions.h"
|
||||
#include "util/posix/close_stdio.h"
|
||||
#include "util/posix/signals.h"
|
||||
#elif defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
|
||||
@ -145,28 +146,6 @@ class CallMetricsRecordNormalExit {
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
|
||||
void InstallSignalHandler(const std::vector<int>& signals,
|
||||
void (*handler)(int, siginfo_t*, void*)) {
|
||||
struct sigaction sa = {};
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sa.sa_sigaction = handler;
|
||||
|
||||
for (int sig : signals) {
|
||||
int rv = sigaction(sig, &sa, nullptr);
|
||||
PCHECK(rv == 0) << "sigaction " << sig;
|
||||
}
|
||||
}
|
||||
|
||||
void RestoreDefaultSignalHandler(int sig) {
|
||||
struct sigaction sa = {};
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = SIG_DFL;
|
||||
int rv = sigaction(sig, &sa, nullptr);
|
||||
DPLOG_IF(ERROR, rv != 0) << "sigaction " << sig;
|
||||
}
|
||||
|
||||
void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
||||
MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);
|
||||
|
||||
@ -201,97 +180,19 @@ void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
||||
}
|
||||
Metrics::HandlerCrashed(metrics_code);
|
||||
|
||||
RestoreDefaultSignalHandler(sig);
|
||||
|
||||
// If the signal was received synchronously resulting from a hardware fault,
|
||||
// returning from the signal handler will cause the kernel to re-raise it,
|
||||
// because this handler hasn’t done anything to alleviate the condition that
|
||||
// caused the signal to be raised in the first place. With the default signal
|
||||
// handler in place, it will cause the same behavior to be taken as though
|
||||
// this signal handler had never been installed at all (expected to be a
|
||||
// crash). This is ideal, because the signal is re-raised with the same
|
||||
// properties and from the same context that initially triggered it, providing
|
||||
// the best debugging experience.
|
||||
|
||||
if ((sig != SIGILL && sig != SIGFPE && sig != SIGBUS && sig != SIGSEGV) ||
|
||||
!si_code_valid) {
|
||||
// Signals received other than via hardware faults, such as those raised
|
||||
// asynchronously via kill() and raise(), and those arising via hardware
|
||||
// traps such as int3 (resulting in SIGTRAP but advancing the instruction
|
||||
// pointer), will not reoccur on their own when returning from the signal
|
||||
// handler. Re-raise them.
|
||||
//
|
||||
// Unfortunately, when SIGBUS is received asynchronously via kill(),
|
||||
// siginfo->si_code makes it appear as though it was actually received via a
|
||||
// hardware fault. See 10.12.3 xnu-3789.41.3/bsd/dev/i386/unix_signal.c
|
||||
// sendsig(). An asynchronous SIGBUS will thus cause the handler-crashed
|
||||
// metric to be logged but will not cause the process to terminate. This
|
||||
// isn’t ideal, but asynchronous SIGBUS is an unexpected condition. The
|
||||
// alternative, to re-raise here on any SIGBUS, is a bad idea because it
|
||||
// would lose properties associated with the the original signal, which are
|
||||
// very valuable for debugging and are visible to a Mach exception handler.
|
||||
// Since SIGBUS is normally received synchronously in response to a hardware
|
||||
// fault, don’t sweat the unexpected asynchronous case.
|
||||
//
|
||||
// Because this signal handler executes with the signal blocked, this
|
||||
// raise() cannot immediately deliver the signal. Delivery is deferred until
|
||||
// this signal handler returns and the signal becomes unblocked. The
|
||||
// re-raised signal will appear with the same context as where it was
|
||||
// initially triggered.
|
||||
int rv = raise(sig);
|
||||
DPLOG_IF(ERROR, rv != 0) << "raise";
|
||||
}
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
||||
}
|
||||
|
||||
void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) {
|
||||
MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);
|
||||
|
||||
RestoreDefaultSignalHandler(sig);
|
||||
|
||||
// Re-raise the signal. See the explanation in HandleCrashSignal(). Note that
|
||||
// no checks for signals arising from synchronous hardware faults are made
|
||||
// because termination signals never originate in that way.
|
||||
int rv = raise(sig);
|
||||
DPLOG_IF(ERROR, rv != 0) << "raise";
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
||||
}
|
||||
|
||||
void InstallCrashHandler() {
|
||||
// These are the core-generating signals from 10.12.3
|
||||
// xnu-3789.41.3/bsd/sys/signalvar.h sigprop: entries with SA_CORE are in the
|
||||
// set.
|
||||
const int kCrashSignals[] = {SIGQUIT,
|
||||
SIGILL,
|
||||
SIGTRAP,
|
||||
SIGABRT,
|
||||
SIGEMT,
|
||||
SIGFPE,
|
||||
SIGBUS,
|
||||
SIGSEGV,
|
||||
SIGSYS};
|
||||
InstallSignalHandler(
|
||||
std::vector<int>(&kCrashSignals[0],
|
||||
&kCrashSignals[arraysize(kCrashSignals)]),
|
||||
HandleCrashSignal);
|
||||
Signals::InstallCrashHandlers(HandleCrashSignal, 0, nullptr);
|
||||
|
||||
// Not a crash handler, but close enough. These are non-core-generating but
|
||||
// terminating signals from 10.12.3 xnu-3789.41.3/bsd/sys/signalvar.h sigprop:
|
||||
// entries with SA_KILL but not SA_CORE are in the set. SIGKILL is excluded
|
||||
// because it is uncatchable.
|
||||
const int kTerminateSignals[] = {SIGHUP,
|
||||
SIGINT,
|
||||
SIGPIPE,
|
||||
SIGALRM,
|
||||
SIGTERM,
|
||||
SIGXCPU,
|
||||
SIGXFSZ,
|
||||
SIGVTALRM,
|
||||
SIGPROF,
|
||||
SIGUSR1,
|
||||
SIGUSR2};
|
||||
InstallSignalHandler(
|
||||
std::vector<int>(&kTerminateSignals[0],
|
||||
&kTerminateSignals[arraysize(kTerminateSignals)]),
|
||||
HandleTerminateSignal);
|
||||
// Not a crash handler, but close enough.
|
||||
Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr);
|
||||
}
|
||||
|
||||
struct ResetSIGTERMTraits {
|
||||
@ -614,7 +515,7 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
base::AutoReset<ExceptionHandlerServer*> reset_g_exception_handler_server(
|
||||
&g_exception_handler_server, &exception_handler_server);
|
||||
|
||||
struct sigaction old_sa;
|
||||
struct sigaction old_sigterm_action;
|
||||
ScopedResetSIGTERM reset_sigterm;
|
||||
if (!options.mach_service.empty()) {
|
||||
// When running from launchd, no no-senders notification could ever be
|
||||
@ -624,13 +525,10 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
//
|
||||
// Set up a SIGTERM handler that will call exception_handler_server.Stop().
|
||||
// This replaces the HandleTerminateSignal handler for SIGTERM.
|
||||
struct sigaction sa = {};
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sa.sa_sigaction = HandleSIGTERM;
|
||||
int rv = sigaction(SIGTERM, &sa, &old_sa);
|
||||
PCHECK(rv == 0) << "sigaction";
|
||||
reset_sigterm.reset(&old_sa);
|
||||
if (Signals::InstallHandler(
|
||||
SIGTERM, HandleSIGTERM, 0, &old_sigterm_action)) {
|
||||
reset_sigterm.reset(&old_sigterm_action);
|
||||
}
|
||||
}
|
||||
#elif defined(OS_WIN)
|
||||
// Shut down as late as possible relative to programs we're watching.
|
||||
@ -692,9 +590,6 @@ int HandlerMain(int argc, char* argv[]) {
|
||||
upload_thread.Stop();
|
||||
prune_thread.Stop();
|
||||
|
||||
if (histogram_allocator)
|
||||
histogram_allocator->DeletePersistentLocation();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
46
infra/config/cq.cfg
Normal file
46
infra/config/cq.cfg
Normal file
@ -0,0 +1,46 @@
|
||||
# Copyright 2017 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.
|
||||
|
||||
# See https://luci-config.appspot.com/schemas/projects/refs:cq.cfg for the
|
||||
# documentation of this file format.
|
||||
|
||||
version: 1
|
||||
cq_name: "crashpad"
|
||||
cq_status_url: "https://chromium-cq-status.appspot.com"
|
||||
|
||||
# This is required for gerrit projects.
|
||||
git_repo_url: "https://chromium.googlesource.com/crashpad/crashpad"
|
||||
|
||||
# Crashpad uses gerrit with no special options.
|
||||
gerrit {}
|
||||
|
||||
verifiers {
|
||||
gerrit_cq_ability {
|
||||
committer_list: "project-crashpad-committers"
|
||||
dry_run_access_list: "project-crashpad-committers"
|
||||
}
|
||||
try_job {
|
||||
buckets {
|
||||
name: "master.client.crashpad"
|
||||
builders { name: "crashpad_try_mac_dbg" }
|
||||
builders { name: "crashpad_try_mac_rel" }
|
||||
builders { name: "crashpad_try_win_x64_dbg" }
|
||||
builders { name: "crashpad_try_win_x64_rel" }
|
||||
builders { name: "crashpad_try_win_x86_dbg" }
|
||||
builders { name: "crashpad_try_win_x86_rel" }
|
||||
builders { name: "crashpad_try_win_x86_wow64_dbg" }
|
||||
builders { name: "crashpad_try_win_x86_wow64_rel" }
|
||||
}
|
||||
}
|
||||
}
|
@ -145,7 +145,7 @@ std::string MinidumpMiscInfoDebugBuildString() {
|
||||
} // namespace internal
|
||||
|
||||
MinidumpMiscInfoWriter::MinidumpMiscInfoWriter()
|
||||
: MinidumpStreamWriter(), misc_info_() {
|
||||
: MinidumpStreamWriter(), misc_info_(), has_xstate_data_(false) {
|
||||
}
|
||||
|
||||
MinidumpMiscInfoWriter::~MinidumpMiscInfoWriter() {
|
||||
@ -330,6 +330,21 @@ void MinidumpMiscInfoWriter::SetBuildString(
|
||||
debug_build_string);
|
||||
}
|
||||
|
||||
void MinidumpMiscInfoWriter::SetXStateData(
|
||||
const XSTATE_CONFIG_FEATURE_MSC_INFO& xstate_data) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
misc_info_.XStateData = xstate_data;
|
||||
has_xstate_data_ = true;
|
||||
}
|
||||
|
||||
void MinidumpMiscInfoWriter::SetProcessCookie(uint32_t process_cookie) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
misc_info_.ProcessCookie = process_cookie;
|
||||
misc_info_.Flags1 |= MINIDUMP_MISC5_PROCESS_COOKIE;
|
||||
}
|
||||
|
||||
bool MinidumpMiscInfoWriter::Freeze() {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
@ -365,6 +380,9 @@ MinidumpStreamType MinidumpMiscInfoWriter::StreamType() const {
|
||||
size_t MinidumpMiscInfoWriter::CalculateSizeOfObjectFromFlags() const {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
if (has_xstate_data_ || (misc_info_.Flags1 & MINIDUMP_MISC5_PROCESS_COOKIE)) {
|
||||
return sizeof(MINIDUMP_MISC_INFO_5);
|
||||
}
|
||||
if (misc_info_.Flags1 & MINIDUMP_MISC4_BUILDSTRING) {
|
||||
return sizeof(MINIDUMP_MISC_INFO_4);
|
||||
}
|
||||
|
@ -51,10 +51,10 @@ std::string MinidumpMiscInfoDebugBuildString();
|
||||
//! minidump file.
|
||||
//!
|
||||
//! The actual stream written will be a MINIDUMP_MISC_INFO,
|
||||
//! MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, or MINIDUMP_MISC_INFO_4 stream.
|
||||
//! Later versions of MINIDUMP_MISC_INFO are supersets of earlier versions. The
|
||||
//! earliest version that supports all of the information that an object of this
|
||||
//! class contains will be used.
|
||||
//! MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, MINIDUMP_MISC_INFO_4, or
|
||||
//! MINIDUMP_MISC_INFO_5 stream. Later versions of MINIDUMP_MISC_INFO are
|
||||
//! supersets of earlier versions. The earliest version that supports all of the
|
||||
//! information that an object of this class contains will be used.
|
||||
class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter {
|
||||
public:
|
||||
MinidumpMiscInfoWriter();
|
||||
@ -107,6 +107,15 @@ class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter {
|
||||
void SetBuildString(const std::string& build_string,
|
||||
const std::string& debug_build_string);
|
||||
|
||||
// TODO(mark): Provide a better interface than this. Don’t force callers to
|
||||
// build their own XSTATE_CONFIG_FEATURE_MSC_INFO structure.
|
||||
//
|
||||
//! \brief Sets MINIDUMP_MISC_INFO_5::XStateData.
|
||||
void SetXStateData(const XSTATE_CONFIG_FEATURE_MSC_INFO& xstate_data);
|
||||
|
||||
//! \brief Sets the field referenced by #MINIDUMP_MISC5_PROCESS_COOKIE.
|
||||
void SetProcessCookie(uint32_t process_cookie);
|
||||
|
||||
protected:
|
||||
// MinidumpWritable:
|
||||
bool Freeze() override;
|
||||
@ -123,6 +132,7 @@ class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter {
|
||||
size_t CalculateSizeOfObjectFromFlags() const;
|
||||
|
||||
MINIDUMP_MISC_INFO_N misc_info_;
|
||||
bool has_xstate_data_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpMiscInfoWriter);
|
||||
};
|
||||
|
@ -21,9 +21,11 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/format_macros.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "minidump/minidump_file_writer.h"
|
||||
@ -164,6 +166,29 @@ void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_4>(
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_5>(
|
||||
const MINIDUMP_MISC_INFO_5* expected,
|
||||
const MINIDUMP_MISC_INFO_5* observed) {
|
||||
ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_4>(
|
||||
reinterpret_cast<const MINIDUMP_MISC_INFO_4*>(expected),
|
||||
reinterpret_cast<const MINIDUMP_MISC_INFO_4*>(observed));
|
||||
EXPECT_EQ(expected->XStateData.SizeOfInfo, observed->XStateData.SizeOfInfo);
|
||||
EXPECT_EQ(expected->XStateData.ContextSize, observed->XStateData.ContextSize);
|
||||
EXPECT_EQ(expected->XStateData.EnabledFeatures,
|
||||
observed->XStateData.EnabledFeatures);
|
||||
for (size_t feature_index = 0;
|
||||
feature_index < arraysize(observed->XStateData.Features);
|
||||
++feature_index) {
|
||||
SCOPED_TRACE(base::StringPrintf("feature_index %" PRIuS, feature_index));
|
||||
EXPECT_EQ(expected->XStateData.Features[feature_index].Offset,
|
||||
observed->XStateData.Features[feature_index].Offset);
|
||||
EXPECT_EQ(expected->XStateData.Features[feature_index].Size,
|
||||
observed->XStateData.Features[feature_index].Size);
|
||||
}
|
||||
EXPECT_EQ(expected->ProcessCookie, observed->ProcessCookie);
|
||||
}
|
||||
|
||||
TEST(MinidumpMiscInfoWriter, Empty) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter());
|
||||
@ -490,7 +515,7 @@ TEST(MinidumpMiscInfoWriter, BuildStringsOverflow) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter());
|
||||
|
||||
MINIDUMP_MISC_INFO_4 tmp;
|
||||
MINIDUMP_MISC_INFO_N tmp;
|
||||
ALLOW_UNUSED_LOCAL(tmp);
|
||||
std::string build_string(arraysize(tmp.BuildString) + 1, 'B');
|
||||
std::string debug_build_string(arraysize(tmp.DbgBldStr), 'D');
|
||||
@ -520,6 +545,63 @@ TEST(MinidumpMiscInfoWriter, BuildStringsOverflow) {
|
||||
ExpectMiscInfoEqual(&expected, observed);
|
||||
}
|
||||
|
||||
TEST(MinidumpMiscInfoWriter, XStateData) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter());
|
||||
|
||||
const XSTATE_CONFIG_FEATURE_MSC_INFO kXStateData = {
|
||||
sizeof(XSTATE_CONFIG_FEATURE_MSC_INFO),
|
||||
1024,
|
||||
0x000000000000005f,
|
||||
{
|
||||
{0, 512},
|
||||
{512, 256},
|
||||
{768, 128},
|
||||
{896, 64},
|
||||
{960, 32},
|
||||
{0, 0},
|
||||
{992, 32},
|
||||
}};
|
||||
|
||||
misc_info_writer->SetXStateData(kXStateData);
|
||||
|
||||
minidump_file_writer.AddStream(std::move(misc_info_writer));
|
||||
|
||||
StringFile string_file;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
||||
|
||||
const MINIDUMP_MISC_INFO_5* observed = nullptr;
|
||||
ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
|
||||
|
||||
MINIDUMP_MISC_INFO_5 expected = {};
|
||||
expected.XStateData = kXStateData;
|
||||
|
||||
ExpectMiscInfoEqual(&expected, observed);
|
||||
}
|
||||
|
||||
TEST(MinidumpMiscInfoWriter, ProcessCookie) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter());
|
||||
|
||||
const uint32_t kProcessCookie = 0x12345678;
|
||||
|
||||
misc_info_writer->SetProcessCookie(kProcessCookie);
|
||||
|
||||
minidump_file_writer.AddStream(std::move(misc_info_writer));
|
||||
|
||||
StringFile string_file;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
||||
|
||||
const MINIDUMP_MISC_INFO_5* observed = nullptr;
|
||||
ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
|
||||
|
||||
MINIDUMP_MISC_INFO_5 expected = {};
|
||||
expected.Flags1 = MINIDUMP_MISC5_PROCESS_COOKIE;
|
||||
expected.ProcessCookie = kProcessCookie;
|
||||
|
||||
ExpectMiscInfoEqual(&expected, observed);
|
||||
}
|
||||
|
||||
TEST(MinidumpMiscInfoWriter, Everything) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter());
|
||||
|
@ -19,7 +19,7 @@
|
||||
#define PACKAGE_COPYRIGHT \
|
||||
"Copyright " PACKAGE_COPYRIGHT_YEAR " " PACKAGE_COPYRIGHT_OWNER
|
||||
#define PACKAGE_COPYRIGHT_OWNER "The Crashpad Authors"
|
||||
#define PACKAGE_COPYRIGHT_YEAR "2016"
|
||||
#define PACKAGE_COPYRIGHT_YEAR "2017"
|
||||
#define PACKAGE_NAME "Crashpad"
|
||||
#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION
|
||||
#define PACKAGE_TARNAME "crashpad"
|
||||
|
@ -127,7 +127,6 @@
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
'-lpowrprof.lib',
|
||||
'-lversion.lib',
|
||||
],
|
||||
},
|
||||
}],
|
||||
|
@ -106,17 +106,17 @@ void SystemSnapshotWin::Initialize(ProcessReaderWin* process_reader) {
|
||||
os_version_major_ = ffi.dwFileVersionMS >> 16;
|
||||
os_version_minor_ = ffi.dwFileVersionMS & 0xffff;
|
||||
os_version_bugfix_ = ffi.dwFileVersionLS >> 16;
|
||||
os_version_build_ =
|
||||
base::StringPrintf("%d", ffi.dwFileVersionLS & 0xffff);
|
||||
os_version_build_ = base::StringPrintf("%u", ffi.dwFileVersionLS & 0xffff);
|
||||
os_version_full_ = base::StringPrintf(
|
||||
"%s %d.%d.%d.%s%s",
|
||||
"%s %u.%u.%u.%s%s",
|
||||
os_name.c_str(),
|
||||
os_version_major_,
|
||||
os_version_minor_,
|
||||
os_version_bugfix_,
|
||||
os_version_build_.c_str(),
|
||||
flags_string.empty() ? "" : (std::string(" (") + flags_string + ")")
|
||||
.c_str());
|
||||
flags_string.empty()
|
||||
? ""
|
||||
: (std::string(" (") + flags_string + ")").c_str());
|
||||
}
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
|
@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
@ -279,6 +280,10 @@ int CatchExceptionToolMain(int argc, char* argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
options.file = file_owner.get();
|
||||
if (fcntl(fileno(options.file), F_SETFD, FD_CLOEXEC) == -1) {
|
||||
PLOG(ERROR) << "fcntl " << options.file_path;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
int exceptions_handled = 0;
|
||||
|
@ -80,10 +80,11 @@ FileHandle OpenFileForOutput(int rdwr_or_wronly,
|
||||
const base::FilePath& path,
|
||||
FileWriteMode mode,
|
||||
FilePermissions permissions) {
|
||||
int flags = O_NOCTTY | O_CLOEXEC;
|
||||
|
||||
DCHECK(rdwr_or_wronly & (O_RDWR | O_WRONLY));
|
||||
DCHECK_EQ(rdwr_or_wronly & ~(O_RDWR | O_WRONLY), 0);
|
||||
|
||||
int flags = rdwr_or_wronly;
|
||||
flags |= rdwr_or_wronly;
|
||||
|
||||
switch (mode) {
|
||||
case FileWriteMode::kReuseOrFail:
|
||||
@ -118,7 +119,8 @@ FileOperationResult WriteFile(FileHandle file,
|
||||
}
|
||||
|
||||
FileHandle OpenFileForRead(const base::FilePath& path) {
|
||||
return HANDLE_EINTR(open(path.value().c_str(), O_RDONLY));
|
||||
return HANDLE_EINTR(
|
||||
open(path.value().c_str(), O_RDONLY | O_NOCTTY | O_CLOEXEC));
|
||||
}
|
||||
|
||||
FileHandle OpenFileForWrite(const base::FilePath& path,
|
||||
|
@ -37,8 +37,10 @@ class Xattr : public testing::Test {
|
||||
|
||||
void SetUp() override {
|
||||
path_ = temp_dir_.path().Append("xattr_file");
|
||||
base::ScopedFD tmp(HANDLE_EINTR(
|
||||
open(path_.value().c_str(), O_CREAT | O_TRUNC, 0644)));
|
||||
base::ScopedFD tmp(
|
||||
HANDLE_EINTR(open(path_.value().c_str(),
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY | O_CLOEXEC,
|
||||
0644)));
|
||||
EXPECT_GE(tmp.get(), 0) << ErrnoMessage("open");
|
||||
}
|
||||
|
||||
|
@ -16,11 +16,14 @@
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "base/mac/foundation_util.h"
|
||||
#import "base/mac/scoped_nsobject.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
#include "package.h"
|
||||
#include "third_party/apple_cf/CFStreamAbstract.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/misc/implicit_cast.h"
|
||||
@ -30,6 +33,80 @@ namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
NSString* AppendEscapedFormat(NSString* base,
|
||||
NSString* format,
|
||||
NSString* data) {
|
||||
return [base stringByAppendingFormat:
|
||||
format,
|
||||
[data stringByAddingPercentEncodingWithAllowedCharacters:
|
||||
[[NSCharacterSet
|
||||
characterSetWithCharactersInString:
|
||||
@"()<>@,;:\\\"/[]?={} \t"] invertedSet]]];
|
||||
}
|
||||
|
||||
// This builds the same User-Agent string that CFNetwork would build internally,
|
||||
// but it uses PACKAGE_NAME and PACKAGE_VERSION in place of values obtained from
|
||||
// the main bundle’s Info.plist.
|
||||
NSString* UserAgentString() {
|
||||
NSString* user_agent = [NSString string];
|
||||
|
||||
// CFNetwork would use the main bundle’s CFBundleName, or the main
|
||||
// executable’s filename if none.
|
||||
user_agent = AppendEscapedFormat(
|
||||
user_agent, @"%@", [NSString stringWithUTF8String:PACKAGE_NAME]);
|
||||
|
||||
// CFNetwork would use the main bundle’s CFBundleVersion, or the string
|
||||
// “(unknown version)” if none.
|
||||
user_agent = AppendEscapedFormat(
|
||||
user_agent, @"/%@", [NSString stringWithUTF8String:PACKAGE_VERSION]);
|
||||
|
||||
// Expected to be CFNetwork.
|
||||
NSBundle* nsurl_bundle = [NSBundle bundleForClass:[NSURLRequest class]];
|
||||
NSString* bundle_name = base::mac::ObjCCast<NSString>([nsurl_bundle
|
||||
objectForInfoDictionaryKey:base::mac::CFToNSCast(kCFBundleNameKey)]);
|
||||
if (bundle_name) {
|
||||
user_agent = AppendEscapedFormat(user_agent, @" %@", bundle_name);
|
||||
|
||||
NSString* bundle_version = base::mac::ObjCCast<NSString>([nsurl_bundle
|
||||
objectForInfoDictionaryKey:base::mac::CFToNSCast(kCFBundleVersionKey)]);
|
||||
if (bundle_version) {
|
||||
user_agent = AppendEscapedFormat(user_agent, @"/%@", bundle_version);
|
||||
}
|
||||
}
|
||||
|
||||
utsname os;
|
||||
if (uname(&os) != 0) {
|
||||
PLOG(WARNING) << "uname";
|
||||
} else {
|
||||
user_agent = AppendEscapedFormat(
|
||||
user_agent, @" %@", [NSString stringWithUTF8String:os.sysname]);
|
||||
user_agent = AppendEscapedFormat(
|
||||
user_agent, @"/%@", [NSString stringWithUTF8String:os.release]);
|
||||
|
||||
// CFNetwork just uses the equivalent of os.machine to obtain the native
|
||||
// (kernel) architecture. Here, give the process’ architecture as well as
|
||||
// the native architecture. Use the same strings that the kernel would, so
|
||||
// that they can be de-duplicated.
|
||||
#if defined(ARCH_CPU_X86)
|
||||
NSString* arch = @"i386";
|
||||
#elif defined(ARCH_CPU_X86_64)
|
||||
NSString* arch = @"x86_64";
|
||||
#else
|
||||
#error Port
|
||||
#endif
|
||||
user_agent = AppendEscapedFormat(user_agent, @" (%@", arch);
|
||||
|
||||
NSString* machine = [NSString stringWithUTF8String:os.machine];
|
||||
if (![machine isEqualToString:arch]) {
|
||||
user_agent = AppendEscapedFormat(user_agent, @"; %@", machine);
|
||||
}
|
||||
|
||||
user_agent = [user_agent stringByAppendingString:@")"];
|
||||
}
|
||||
|
||||
return user_agent;
|
||||
}
|
||||
|
||||
// An implementation of CFReadStream. This implements the V0 callback
|
||||
// scheme.
|
||||
class HTTPBodyStreamCFReadStream {
|
||||
@ -171,6 +248,13 @@ bool HTTPTransportMac::ExecuteSynchronously(std::string* response_body) {
|
||||
timeoutInterval:timeout()];
|
||||
[request setHTTPMethod:base::SysUTF8ToNSString(method())];
|
||||
|
||||
// If left to its own devices, CFNetwork would build a user-agent string
|
||||
// based on keys in the main bundle’s Info.plist, giving ugly results if
|
||||
// there is no Info.plist. Provide a User-Agent string similar to the one
|
||||
// that CFNetwork would use, but with appropriate values in place of the
|
||||
// Info.plist-derived strings.
|
||||
[request setValue:UserAgentString() forHTTPHeaderField:@"User-Agent"];
|
||||
|
||||
for (const auto& pair : headers()) {
|
||||
[request setValue:base::SysUTF8ToNSString(pair.second)
|
||||
forHTTPHeaderField:base::SysUTF8ToNSString(pair.first)];
|
||||
|
@ -28,15 +28,59 @@
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
#include "package.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
#include "util/net/http_body.h"
|
||||
#include "util/win/module_version.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
const wchar_t kWinHttpDll[] = L"winhttp.dll";
|
||||
|
||||
std::string UserAgent() {
|
||||
std::string user_agent =
|
||||
base::StringPrintf("%s/%s WinHTTP", PACKAGE_NAME, PACKAGE_VERSION);
|
||||
|
||||
VS_FIXEDFILEINFO version;
|
||||
if (GetModuleVersionAndType(base::FilePath(kWinHttpDll), &version)) {
|
||||
user_agent.append(base::StringPrintf("/%u.%u.%u.%u",
|
||||
version.dwFileVersionMS >> 16,
|
||||
version.dwFileVersionMS & 0xffff,
|
||||
version.dwFileVersionLS >> 16,
|
||||
version.dwFileVersionLS & 0xffff));
|
||||
}
|
||||
|
||||
if (GetModuleVersionAndType(base::FilePath(L"kernel32.dll"), &version) &&
|
||||
(version.dwFileOS & VOS_NT_WINDOWS32) == VOS_NT_WINDOWS32) {
|
||||
user_agent.append(base::StringPrintf(" Windows_NT/%u.%u.%u.%u (",
|
||||
version.dwFileVersionMS >> 16,
|
||||
version.dwFileVersionMS & 0xffff,
|
||||
version.dwFileVersionLS >> 16,
|
||||
version.dwFileVersionLS & 0xffff));
|
||||
#if defined(ARCH_CPU_X86)
|
||||
user_agent.append("x86");
|
||||
#elif defined(ARCH_CPU_X86_64)
|
||||
user_agent.append("x64");
|
||||
#else
|
||||
#error Port
|
||||
#endif
|
||||
|
||||
BOOL is_wow64;
|
||||
if (!IsWow64Process(GetCurrentProcess(), &is_wow64)) {
|
||||
PLOG(WARNING) << "IsWow64Process";
|
||||
} else if (is_wow64) {
|
||||
user_agent.append("; WoW64");
|
||||
}
|
||||
user_agent.append(1, ')');
|
||||
}
|
||||
|
||||
return user_agent;
|
||||
}
|
||||
|
||||
// PLOG doesn't work for messages from WinHTTP, so we need to use
|
||||
// FORMAT_MESSAGE_FROM_HMODULE + the dll name manually here.
|
||||
std::string WinHttpMessage(const char* extra) {
|
||||
@ -45,7 +89,7 @@ std::string WinHttpMessage(const char* extra) {
|
||||
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
|
||||
FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE;
|
||||
DWORD len = FormatMessageA(flags,
|
||||
GetModuleHandle(L"winhttp.dll"),
|
||||
GetModuleHandle(kWinHttpDll),
|
||||
error_code,
|
||||
0,
|
||||
msgbuf,
|
||||
@ -93,12 +137,11 @@ HTTPTransportWin::~HTTPTransportWin() {
|
||||
}
|
||||
|
||||
bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) {
|
||||
ScopedHINTERNET session(
|
||||
WinHttpOpen(base::UTF8ToUTF16(PACKAGE_NAME "/" PACKAGE_VERSION).c_str(),
|
||||
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
|
||||
WINHTTP_NO_PROXY_NAME,
|
||||
WINHTTP_NO_PROXY_BYPASS,
|
||||
0));
|
||||
ScopedHINTERNET session(WinHttpOpen(base::UTF8ToUTF16(UserAgent()).c_str(),
|
||||
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
|
||||
WINHTTP_NO_PROXY_NAME,
|
||||
WINHTTP_NO_PROXY_BYPASS,
|
||||
0));
|
||||
if (!session.get()) {
|
||||
LOG(ERROR) << WinHttpMessage("WinHttpOpen");
|
||||
return false;
|
||||
@ -136,6 +179,16 @@ bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) {
|
||||
std::wstring extra_info(url_components.lpszExtraInfo,
|
||||
url_components.dwExtraInfoLength);
|
||||
|
||||
// Use url_path, and get the query parameter from extra_info, up to the first
|
||||
// #, if any. See RFC 7230 §5.3.1 and RFC 3986 §3.4. Beware that when this is
|
||||
// used to POST data, the query parameters generally belong in the request
|
||||
// body and not in the URL request target. It’s legal for them to be in both
|
||||
// places, but the interpretation is subject to whatever the client and server
|
||||
// agree on. This honors whatever was passed in, matching other platforms, but
|
||||
// you’ve been warned!
|
||||
std::wstring request_target(
|
||||
url_path.append(extra_info.substr(0, extra_info.find(L'#'))));
|
||||
|
||||
ScopedHINTERNET connect(WinHttpConnect(
|
||||
session.get(), host_name.c_str(), url_components.nPort, 0));
|
||||
if (!connect.get()) {
|
||||
@ -146,7 +199,7 @@ bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) {
|
||||
ScopedHINTERNET request(WinHttpOpenRequest(
|
||||
connect.get(),
|
||||
base::UTF8ToUTF16(method()).c_str(),
|
||||
url_path.c_str(),
|
||||
request_target.c_str(),
|
||||
nullptr,
|
||||
WINHTTP_NO_REFERER,
|
||||
WINHTTP_DEFAULT_ACCEPT_TYPES,
|
||||
|
@ -211,7 +211,7 @@ void CloseMultipleNowOrOnExec(int fd, int preserve_fd) {
|
||||
// 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"));
|
||||
base::ScopedFILE nr_open_file(fopen("/proc/sys/fs/nr_open", "re"));
|
||||
if (nr_open_file.get() != nullptr) {
|
||||
int nr_open;
|
||||
if (fscanf(nr_open_file.get(), "%d\n", &nr_open) == 1 &&
|
||||
|
@ -27,7 +27,8 @@ namespace crashpad {
|
||||
namespace {
|
||||
|
||||
void CloseStdioStream(int desired_fd, int oflag) {
|
||||
base::ScopedFD fd(HANDLE_EINTR(open(_PATH_DEVNULL, oflag)));
|
||||
base::ScopedFD fd(
|
||||
HANDLE_EINTR(open(_PATH_DEVNULL, oflag | O_NOCTTY | O_CLOEXEC)));
|
||||
if (fd == desired_fd) {
|
||||
// Weird, but play along.
|
||||
ignore_result(fd.release());
|
||||
|
@ -103,7 +103,7 @@ void TestSelfProcess(const ProcessInfo& process_info) {
|
||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
std::vector<std::string> expect_arg_vector;
|
||||
{
|
||||
base::ScopedFILE cmdline(fopen("/proc/self/cmdline", "r"));
|
||||
base::ScopedFILE cmdline(fopen("/proc/self/cmdline", "re"));
|
||||
ASSERT_NE(nullptr, cmdline.get()) << ErrnoMessage("fopen");
|
||||
|
||||
int expect_arg_char;
|
||||
|
281
util/posix/signals.cc
Normal file
281
util/posix/signals.cc
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright 2017 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/posix/signals.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
// These are the core-generating signals.
|
||||
//
|
||||
// On macOS, these come from 10.12.3 xnu-3789.41.3/bsd/sys/signalvar.h sigprop:
|
||||
// entries with SA_CORE are in the set.
|
||||
//
|
||||
// For Linux, see linux-4.4.52/kernel/signal.c get_signal() and
|
||||
// linux-4.4.52/include/linux/signal.h sig_kernel_coredump(): signals in
|
||||
// SIG_KERNEL_COREDUMP_MASK are in the set.
|
||||
constexpr int kCrashSignals[] = {
|
||||
SIGABRT,
|
||||
SIGBUS,
|
||||
SIGFPE,
|
||||
SIGILL,
|
||||
SIGQUIT,
|
||||
SIGSEGV,
|
||||
SIGSYS,
|
||||
SIGTRAP,
|
||||
#if defined(SIGEMT)
|
||||
SIGEMT,
|
||||
#endif // defined(SIGEMT)
|
||||
#if defined(OS_LINUX)
|
||||
SIGXCPU,
|
||||
SIGXFSZ,
|
||||
#endif // defined(OS_LINUX)
|
||||
};
|
||||
|
||||
// These are the non-core-generating but terminating signals.
|
||||
//
|
||||
// On macOS, these come from 10.12.3 xnu-3789.41.3/bsd/sys/signalvar.h sigprop:
|
||||
// entries with SA_KILL but not SA_CORE are in the set. SIGKILL is excluded
|
||||
// because it is uncatchable.
|
||||
//
|
||||
// For Linux, see linux-4.4.52/kernel/signal.c get_signal() and
|
||||
// linux-4.4.52/include/linux/signal.h sig_kernel_coredump(),
|
||||
// sig_kernel_ignore(), and sig_kernel_stop(): signals not in
|
||||
// SIG_KERNEL_COREDUMP_MASK, SIG_KERNEL_IGNORE_MASK, or SIG_KERNEL_STOP_MASK are
|
||||
// in the set. SIGKILL is excluded because it is uncatchable (it’s in
|
||||
// SIG_KERNEL_ONLY_MASK and qualifies for sig_kernel_only()). Real-time signals
|
||||
// in the range [SIGRTMIN, SIGRTMAX) also have termination as the default
|
||||
// action, although they are not listed here.
|
||||
constexpr int kTerminateSignals[] = {
|
||||
SIGALRM,
|
||||
SIGHUP,
|
||||
SIGINT,
|
||||
SIGPIPE,
|
||||
SIGPROF,
|
||||
SIGTERM,
|
||||
SIGUSR1,
|
||||
SIGUSR2,
|
||||
SIGVTALRM,
|
||||
#if defined(SIGPWR)
|
||||
SIGPWR,
|
||||
#endif // defined(SIGPWR)
|
||||
#if defined(SIGSTKFLT)
|
||||
SIGSTKFLT,
|
||||
#endif // defined(SIGSTKFLT)
|
||||
#if defined(OS_MACOSX)
|
||||
SIGXCPU,
|
||||
SIGXFSZ,
|
||||
#endif // defined(OS_MACOSX)
|
||||
#if defined(OS_LINUX)
|
||||
SIGIO,
|
||||
#endif // defined(OS_LINUX)
|
||||
};
|
||||
|
||||
bool InstallHandlers(const std::vector<int>& signals,
|
||||
Signals::Handler handler,
|
||||
int flags,
|
||||
Signals::OldActions* old_actions) {
|
||||
bool success = true;
|
||||
for (int sig : signals) {
|
||||
success &= Signals::InstallHandler(
|
||||
sig,
|
||||
handler,
|
||||
flags,
|
||||
old_actions ? old_actions->ActionForSignal(sig) : nullptr);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool IsSignalInSet(int sig, const int* set, size_t set_size) {
|
||||
for (size_t index = 0; index < set_size; ++index) {
|
||||
if (sig == set[index]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct sigaction* Signals::OldActions::ActionForSignal(int sig) {
|
||||
DCHECK_GT(sig, 0);
|
||||
const size_t slot = sig - 1;
|
||||
DCHECK_LT(slot, arraysize(actions_));
|
||||
return &actions_[slot];
|
||||
}
|
||||
|
||||
// static
|
||||
bool Signals::InstallHandler(int sig,
|
||||
Handler handler,
|
||||
int flags,
|
||||
struct sigaction* old_action) {
|
||||
struct sigaction action;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = flags | SA_SIGINFO;
|
||||
action.sa_sigaction = handler;
|
||||
if (sigaction(sig, &action, old_action) != 0) {
|
||||
PLOG(ERROR) << "sigaction " << sig;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Signals::InstallCrashHandlers(Handler handler,
|
||||
int flags,
|
||||
OldActions* old_actions) {
|
||||
return InstallHandlers(
|
||||
std::vector<int>(kCrashSignals, kCrashSignals + arraysize(kCrashSignals)),
|
||||
handler,
|
||||
flags,
|
||||
old_actions);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Signals::InstallTerminateHandlers(Handler handler,
|
||||
int flags,
|
||||
OldActions* old_actions) {
|
||||
return InstallHandlers(
|
||||
std::vector<int>(kTerminateSignals,
|
||||
kTerminateSignals + arraysize(kTerminateSignals)),
|
||||
handler,
|
||||
flags,
|
||||
old_actions);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Signals::WillSignalReraiseAutonomously(const siginfo_t* siginfo) {
|
||||
// Signals received other than via hardware faults, such as those raised
|
||||
// asynchronously via kill() and raise(), and those arising via hardware traps
|
||||
// such as int3 on x86 (resulting in SIGTRAP but advancing the instruction
|
||||
// pointer), will not reoccur on their own when returning from the signal
|
||||
// handler.
|
||||
//
|
||||
// Unfortunately, on macOS, when SIGBUS is received asynchronously via kill(),
|
||||
// siginfo->si_code makes it appear as though it was actually received via a
|
||||
// hardware fault. See 10.12.3 xnu-3789.41.3/bsd/dev/i386/unix_signal.c
|
||||
// sendsig(). Asynchronous SIGBUS will not re-raise itself autonomously, but
|
||||
// this function (acting on information from the kernel) behaves as though it
|
||||
// will. This isn’t ideal, but asynchronous SIGBUS is an unexpected condition.
|
||||
// The alternative, to never treat SIGBUS as autonomously re-raising, is a bad
|
||||
// idea because the explicit re-raise would lose properties associated with
|
||||
// the the original signal, which are valuable for debugging and are visible
|
||||
// to a Mach exception handler. Since SIGBUS is normally received
|
||||
// synchronously in response to a hardware fault, don’t sweat the unexpected
|
||||
// asynchronous case.
|
||||
//
|
||||
// SIGSEGV on macOS originating from a general protection fault is a more
|
||||
// difficult case: si_code is cleared, making the signal appear asynchronous.
|
||||
// See 10.12.3 xnu-3789.41.3/bsd/dev/i386/unix_signal.c sendsig().
|
||||
const int sig = siginfo->si_signo;
|
||||
const int code = siginfo->si_code;
|
||||
|
||||
// Only these signals can be generated from hardware faults and can re-raise
|
||||
// autonomously.
|
||||
return (sig == SIGBUS ||
|
||||
sig == SIGFPE ||
|
||||
sig == SIGILL ||
|
||||
sig == SIGSEGV) &&
|
||||
|
||||
// The signal was only generated from a hardware fault if the code is a
|
||||
// positive number not matching one of these SI_* constants. See
|
||||
// “Signal Actions” under XRAT “Rationale”/B.2.4 “Signal Concepts” in
|
||||
// POSIX.1-2008, 2016 Edition, regarding si_code. The historical
|
||||
// behavior does not use these SI_* constants and signals generated
|
||||
// asynchronously show up with a code of 0. On macOS, the SI_*
|
||||
// constants are defined but never used, and the historical value of 0
|
||||
// remains. See 10.12.3 xnu-3789.41.3/bsd/kern/kern_sig.c
|
||||
// psignal_internal().
|
||||
(code > 0 &&
|
||||
code != SI_ASYNCIO &&
|
||||
code != SI_MESGQ &&
|
||||
code != SI_QUEUE &&
|
||||
code != SI_TIMER &&
|
||||
code != SI_USER &&
|
||||
#if defined(SI_DETHREAD)
|
||||
code != SI_DETHREAD &&
|
||||
#endif // defiend(SI_DETHREAD)
|
||||
#if defined(SI_KERNEL)
|
||||
// In Linux, SI_KERNEL is used for signals that are raised by the
|
||||
// kernel in software, opposing SI_USER. See
|
||||
// linux-4.4.52/kernel/signal.c __send_signal(). Signals originating
|
||||
// from hardware faults do not use this SI_KERNEL, but a proper signal
|
||||
// code translated in architecture-specific code from the
|
||||
// characteristics of the hardware fault.
|
||||
code != SI_KERNEL &&
|
||||
#endif // defined(SI_KERNEL)
|
||||
#if defined(SI_SIGIO)
|
||||
code != SI_SIGIO &&
|
||||
#endif // defined(SI_SIGIO)
|
||||
#if defined(SI_TKILL)
|
||||
code != SI_TKILL &&
|
||||
#endif // defined(SI_TKILL)
|
||||
true);
|
||||
}
|
||||
|
||||
// static
|
||||
void Signals::RestoreHandlerAndReraiseSignalOnReturn(
|
||||
const siginfo_t* siginfo,
|
||||
const struct sigaction* old_action) {
|
||||
// Failures in this function should _exit(kFailureExitCode). This is a quick
|
||||
// and quiet failure. This function runs in signal handler context, and it’s
|
||||
// difficult to safely be loud from a signal handler.
|
||||
const int kFailureExitCode = 191;
|
||||
|
||||
struct sigaction default_action;
|
||||
sigemptyset(&default_action.sa_mask);
|
||||
default_action.sa_flags = 0;
|
||||
default_action.sa_handler = SIG_DFL;
|
||||
|
||||
const struct sigaction* restore_action =
|
||||
old_action ? old_action : &default_action;
|
||||
|
||||
// Try to restore restore_action. If that fails and restore_action was
|
||||
// old_action, the problem may have been that old_action was bogus, so try to
|
||||
// set the default action.
|
||||
const int sig = siginfo->si_signo;
|
||||
if (sigaction(sig, restore_action, nullptr) != 0 && old_action &&
|
||||
sigaction(sig, &default_action, nullptr) != 0) {
|
||||
_exit(kFailureExitCode);
|
||||
}
|
||||
|
||||
// Explicitly re-raise the signal if it will not re-raise itself. Because
|
||||
// signal handlers normally execute with their signal blocked, this raise()
|
||||
// cannot immediately deliver the signal. Delivery is deferred until the
|
||||
// signal handler returns and the signal becomes unblocked. The re-raised
|
||||
// signal will appear with the same context as where it was initially
|
||||
// triggered.
|
||||
if (!WillSignalReraiseAutonomously(siginfo) && raise(sig) != 0) {
|
||||
_exit(kFailureExitCode);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool Signals::IsCrashSignal(int sig) {
|
||||
return IsSignalInSet(sig, kCrashSignals, arraysize(kCrashSignals));
|
||||
}
|
||||
|
||||
// static
|
||||
bool Signals::IsTerminateSignal(int sig) {
|
||||
return IsSignalInSet(sig, kTerminateSignals, arraysize(kTerminateSignals));
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
228
util/posix/signals.h
Normal file
228
util/posix/signals.h
Normal file
@ -0,0 +1,228 @@
|
||||
// Copyright 2017 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_POSIX_SIGNALS_H_
|
||||
#define CRASHPAD_UTIL_POSIX_SIGNALS_H_
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief Utilities for handling POSIX signals.
|
||||
class Signals {
|
||||
public:
|
||||
//! \brief The type used for `struct sigaction::sa_sigaction`.
|
||||
using Handler = void (*)(int, siginfo_t*, void*);
|
||||
|
||||
//! \brief A group of `struct sigaction` structures corresponding to a set
|
||||
//! of signals’ previous actions, addressable by signal number.
|
||||
//!
|
||||
//! This type is used to store previous signal actions when new actions are
|
||||
//! installed in batch by InstallCrashHandlers() or
|
||||
//! InstallTerminateHandlers().
|
||||
//!
|
||||
//! This object is not initialized by any constructor. Its expected initial
|
||||
//! state is to have its contents filled with zeroes. Because signal handlers
|
||||
//! are stateless (there is no “context” parameter), any state must be
|
||||
//! accessed via objects of static storage duration, and it is expected that
|
||||
//! objects of this class will only ever exist with static storage duration,
|
||||
//! which in the absence of a constructor will be zero-initialized as
|
||||
//! expected. In the event that an object of this class must exist with a
|
||||
//! different storage duration, such as automatic or dynamic storage duration,
|
||||
//! it must be explicitly initialized. For example: `OldActions old_actions =
|
||||
//! {};`.
|
||||
class OldActions {
|
||||
public:
|
||||
// DISALLOW_COPY_AND_ASSIGN removes the default constructor, so explicitly
|
||||
// opt for it. This should not result in any static initialization code even
|
||||
// when an object of this class is given static storage duration.
|
||||
OldActions() = default;
|
||||
|
||||
//! \brief Returns a `struct sigaction` structure corresponding to the
|
||||
//! given signal.
|
||||
//!
|
||||
//! \note This method is safe to call from a signal handler.
|
||||
struct sigaction* ActionForSignal(int sig);
|
||||
|
||||
private:
|
||||
// As a small storage optimization, don’t waste any space on a slot for
|
||||
// signal 0, because there is no signal 0.
|
||||
struct sigaction actions_[NSIG - 1];
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(OldActions);
|
||||
};
|
||||
|
||||
//! \brief Installs a new signal handler.
|
||||
//!
|
||||
//! \param[in] sig The signal number to handle.
|
||||
//! \param[in] handler A signal-handling function to execute, used as the
|
||||
//! `struct sigaction::sa_sigaction` field when calling `sigaction()`.
|
||||
//! \param[in] flags Flags to pass to `sigaction()` in the `struct
|
||||
//! sigaction::sa_flags` field. `SA_SIGINFO` will be specified implicitly.
|
||||
//! \param[out] old_action The previous action for the signal, replaced by the
|
||||
//! new action. May be `nullptr` if not needed.
|
||||
//!
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
//!
|
||||
//! \warning This function may not be called from a signal handler because of
|
||||
//! its use of logging. See RestoreHandlerAndReraiseSignalOnReturn()
|
||||
//! instead.
|
||||
static bool InstallHandler(int sig,
|
||||
Handler handler,
|
||||
int flags,
|
||||
struct sigaction* old_action);
|
||||
|
||||
//! \brief Installs a new signal handler for all signals associated with
|
||||
//! crashes.
|
||||
//!
|
||||
//! Signals associated with crashes are those whose default dispositions
|
||||
//! involve creating a core dump. The precise set of signals involved varies
|
||||
//! between operating systems.
|
||||
//!
|
||||
//! A single signal may either be associated with a crash or with termination
|
||||
//! (see InstallTerminateHandlers()), and perhaps neither, but never both.
|
||||
//!
|
||||
//! \param[in] handler A signal-handling function to execute, used as the
|
||||
//! `struct sigaction::sa_sigaction` field when calling `sigaction()`.
|
||||
//! \param[in] flags Flags to pass to `sigaction()` in the `struct
|
||||
//! sigaction::sa_flags` field. `SA_SIGINFO` will be specified implicitly.
|
||||
//! \param[out] old_actions The previous actions for the signals, replaced by
|
||||
//! the new action. May be `nullptr` if not needed. The same \a
|
||||
//! old_actions object may be used for calls to both this function and
|
||||
//! InstallTerminateHandlers().
|
||||
//!
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
//!
|
||||
//! \warning This function may not be called from a signal handler because of
|
||||
//! its use of logging. See RestoreHandlerAndReraiseSignalOnReturn()
|
||||
//! instead.
|
||||
static bool InstallCrashHandlers(Handler handler,
|
||||
int flags,
|
||||
OldActions* old_actions);
|
||||
|
||||
//! \brief Installs a new signal handler for all signals associated with
|
||||
//! termination.
|
||||
//!
|
||||
//! Signals associated with termination are those whose default dispositions
|
||||
//! involve terminating the process without creating a core dump. The precise
|
||||
//! set of signals involved varies between operating systems.
|
||||
//!
|
||||
//! A single signal may either be associated with termination or with a
|
||||
//! crash (see InstalCrashHandlers()), and perhaps neither, but never both.
|
||||
//!
|
||||
//! \param[in] handler A signal-handling function to execute, used as the
|
||||
//! `struct sigaction::sa_sigaction` field when calling `sigaction()`.
|
||||
//! \param[in] flags Flags to pass to `sigaction()` in the `struct
|
||||
//! sigaction::sa_flags` field. `SA_SIGINFO` will be specified implicitly.
|
||||
//! \param[out] old_actions The previous actions for the signals, replaced by
|
||||
//! the new action. May be `nullptr` if not needed. The same \a
|
||||
//! old_actions object may be used for calls to both this function and
|
||||
//! InstallCrashHandlers().
|
||||
//!
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
//!
|
||||
//! \warning This function may not be called from a signal handler because of
|
||||
//! its use of logging. See RestoreHandlerAndReraiseSignalOnReturn()
|
||||
//! instead.
|
||||
static bool InstallTerminateHandlers(Handler handler,
|
||||
int flags,
|
||||
OldActions* old_actions);
|
||||
|
||||
//! \brief Determines whether a signal will be re-raised autonomously upon
|
||||
//! return from a signal handler.
|
||||
//!
|
||||
//! Certain signals, when generated synchronously in response to a hardware
|
||||
//! fault, are unrecoverable. Upon return from the signal handler, the same
|
||||
//! action that triggered the signal to be raised initially will be retried,
|
||||
//! and unless the signal handler took action to mitigate this error, the same
|
||||
//! signal will be re-raised. As an example, a CPU will not be able to read
|
||||
//! unmapped memory (causing `SIGSEGV`), thus the signal will be re-raised
|
||||
//! upon return from the signal handler unless the signal handler established
|
||||
//! a memory mapping where required.
|
||||
//!
|
||||
//! It is important to distinguish between these synchronous signals generated
|
||||
//! in response to a hardware fault and signals generated asynchronously or in
|
||||
//! software. As an example, `SIGSEGV` will not re-raise autonomously if sent
|
||||
//! by `kill()`.
|
||||
//!
|
||||
//! This function distinguishes between signals that can re-raise
|
||||
//! autonomously, and for those that can, between instances of the signal that
|
||||
//! were generated synchronously in response to a hardware fault and instances
|
||||
//! that were generated by other means.
|
||||
//!
|
||||
//! \param[in] siginfo A pointer to a `siginfo_t` object received by a signal
|
||||
//! handler.
|
||||
//!
|
||||
//! \return `true` if the signal being handled will re-raise itself
|
||||
//! autonomously upon return from a signal handler. `false` if it will
|
||||
//! not. When this function returns `false`, a signal can still be
|
||||
//! re-raised upon return from a signal handler by calling `raise()` from
|
||||
//! within the signal handler.
|
||||
//!
|
||||
//! \note This function is safe to call from a signal handler.
|
||||
static bool WillSignalReraiseAutonomously(const siginfo_t* siginfo);
|
||||
|
||||
//! \brief Restores a previous signal action and arranges to re-raise a signal
|
||||
//! on return from a signal handler.
|
||||
//!
|
||||
//! \param[in] siginfo A pointer to a `siginfo_t` object received by a signal
|
||||
//! handler.
|
||||
//! \param[in] old_action The previous action for the signal, which will be
|
||||
//! re-established as the signal’s action. May be `nullptr`, which directs
|
||||
//! the default action for the signal to be used.
|
||||
//!
|
||||
//! If this function fails, it will immediately call `_exit()` and set an exit
|
||||
//! status of `191`.
|
||||
//!
|
||||
//! \note This function may only be called from a signal handler.
|
||||
static void RestoreHandlerAndReraiseSignalOnReturn(
|
||||
const siginfo_t* siginfo,
|
||||
const struct sigaction* old_action);
|
||||
|
||||
//! \brief Determines whether a signal is associated with a crash.
|
||||
//!
|
||||
//! Signals associated with crashes are those whose default dispositions
|
||||
//! involve creating a core dump. The precise set of signals involved varies
|
||||
//! between operating systems.
|
||||
//!
|
||||
//! \param[in] sig The signal to test.
|
||||
//!
|
||||
//! \return `true` if \a sig is associated with a crash. `false` otherwise.
|
||||
//!
|
||||
//! \note This function is safe to call from a signal handler.
|
||||
static bool IsCrashSignal(int sig);
|
||||
|
||||
//! \brief Determines whether a signal is associated with termination.
|
||||
//!
|
||||
//! Signals associated with termination are those whose default dispositions
|
||||
//! involve terminating the process without creating a core dump. The precise
|
||||
//! set of signals involved varies between operating systems.
|
||||
//!
|
||||
//! \param[in] sig The signal to test.
|
||||
//!
|
||||
//! \return `true` if \a sig is associated with termination. `false`
|
||||
//! otherwise.
|
||||
//!
|
||||
//! \note This function is safe to call from a signal handler.
|
||||
static bool IsTerminateSignal(int sig);
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(Signals);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_POSIX_SIGNALS_H_
|
579
util/posix/signals_test.cc
Normal file
579
util/posix/signals_test.cc
Normal file
@ -0,0 +1,579 @@
|
||||
// Copyright 2017 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/posix/signals.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/files/scoped_file.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "build/build_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/errors.h"
|
||||
#include "test/multiprocess.h"
|
||||
#include "test/scoped_temp_dir.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
constexpr int kUnexpectedExitStatus = 3;
|
||||
|
||||
// Keep synchronized with CauseSignal().
|
||||
bool CanCauseSignal(int sig) {
|
||||
return sig == SIGABRT ||
|
||||
sig == SIGALRM ||
|
||||
sig == SIGBUS ||
|
||||
#if !defined(ARCH_CPU_ARM64)
|
||||
sig == SIGFPE ||
|
||||
#endif // !defined(ARCH_CPU_ARM64)
|
||||
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
|
||||
sig == SIGILL ||
|
||||
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL
|
||||
sig == SIGPIPE ||
|
||||
sig == SIGSEGV ||
|
||||
#if defined(OS_MACOSX)
|
||||
sig == SIGSYS ||
|
||||
#endif // OS_MACOSX
|
||||
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
|
||||
sig == SIGTRAP ||
|
||||
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
|
||||
false;
|
||||
}
|
||||
|
||||
// Keep synchronized with CanCauseSignal().
|
||||
void CauseSignal(int sig) {
|
||||
switch (sig) {
|
||||
case SIGABRT: {
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
|
||||
case SIGALRM: {
|
||||
struct itimerval itimer = {};
|
||||
itimer.it_value.tv_usec = 1E3; // 1 millisecond
|
||||
if (setitimer(ITIMER_REAL, &itimer, nullptr) != 0) {
|
||||
PLOG(ERROR) << "setitimer";
|
||||
_exit(kUnexpectedExitStatus);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
sleep(std::numeric_limits<unsigned int>::max());
|
||||
}
|
||||
}
|
||||
|
||||
case SIGBUS: {
|
||||
char* mapped;
|
||||
{
|
||||
base::ScopedFD fd;
|
||||
{
|
||||
ScopedTempDir temp_dir;
|
||||
fd.reset(open(temp_dir.path().Append("empty").value().c_str(),
|
||||
O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_CLOEXEC,
|
||||
0644));
|
||||
if (fd.get() < 0) {
|
||||
PLOG(ERROR) << "open";
|
||||
}
|
||||
}
|
||||
if (fd.get() < 0) {
|
||||
_exit(kUnexpectedExitStatus);
|
||||
}
|
||||
|
||||
mapped = reinterpret_cast<char*>(mmap(nullptr,
|
||||
getpagesize(),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE,
|
||||
fd.get(),
|
||||
0));
|
||||
if (mapped == MAP_FAILED) {
|
||||
PLOG(ERROR) << "mmap";
|
||||
}
|
||||
}
|
||||
if (mapped == MAP_FAILED) {
|
||||
_exit(kUnexpectedExitStatus);
|
||||
}
|
||||
|
||||
*mapped = 0;
|
||||
|
||||
_exit(kUnexpectedExitStatus);
|
||||
break;
|
||||
}
|
||||
|
||||
#if !defined(ARCH_CPU_ARM64)
|
||||
// ARM64 has hardware integer division instructions that don’t generate a
|
||||
// trap for divide-by-zero, so this doesn’t produce SIGFPE.
|
||||
case SIGFPE: {
|
||||
// Optimization makes this tricky, so get zero from a system call likely
|
||||
// to succeed, and try to do something with the result.
|
||||
struct stat stat_buf;
|
||||
int zero = stat("/", &stat_buf);
|
||||
if (zero == -1) {
|
||||
// It’s important to check |== -1| and not |!= 0|. An optimizer is free
|
||||
// to discard an |== 0| branch entirely, because division by zero is
|
||||
// undefined behavior.
|
||||
PLOG(ERROR) << "stat";
|
||||
_exit(kUnexpectedExitStatus);
|
||||
}
|
||||
|
||||
int quotient = 2 / zero;
|
||||
fstat(quotient, &stat_buf);
|
||||
break;
|
||||
}
|
||||
#endif // ARCH_CPU_ARM64
|
||||
|
||||
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
|
||||
case SIGILL: {
|
||||
// __builtin_trap() causes SIGTRAP on arm64 on Android.
|
||||
__builtin_trap();
|
||||
break;
|
||||
}
|
||||
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
|
||||
|
||||
case SIGPIPE: {
|
||||
int pipe_fds[2];
|
||||
if (pipe(pipe_fds) != 0) {
|
||||
PLOG(ERROR) << "pipe";
|
||||
_exit(kUnexpectedExitStatus);
|
||||
}
|
||||
|
||||
if (close(pipe_fds[0]) != 0) {
|
||||
PLOG(ERROR) << "close";
|
||||
_exit(kUnexpectedExitStatus);
|
||||
}
|
||||
|
||||
char c = 0;
|
||||
ssize_t rv = write(pipe_fds[1], &c, sizeof(c));
|
||||
if (rv < 0) {
|
||||
PLOG(ERROR) << "write";
|
||||
_exit(kUnexpectedExitStatus);
|
||||
} else if (rv != sizeof(c)) {
|
||||
LOG(ERROR) << "write";
|
||||
_exit(kUnexpectedExitStatus);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SIGSEGV: {
|
||||
volatile int* i = nullptr;
|
||||
*i = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
case SIGSYS: {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
int rv = syscall(0);
|
||||
#pragma clang diagnostic pop
|
||||
if (rv != 0) {
|
||||
PLOG(ERROR) << "syscall";
|
||||
_exit(kUnexpectedExitStatus);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif // OS_MACOSX
|
||||
|
||||
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
|
||||
case SIGTRAP: {
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
asm("int3");
|
||||
#elif defined(ARCH_CPU_ARM64)
|
||||
// bkpt #0 should work for 32-bit ARCH_CPU_ARMEL, but according to
|
||||
// https://crrev.com/f53167270c44, it only causes SIGTRAP on Linux under a
|
||||
// 64-bit kernel. For a pure 32-bit armv7 system, it generates SIGBUS.
|
||||
asm("brk #0");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
|
||||
|
||||
default: {
|
||||
LOG(ERROR) << "unexpected signal " << sig;
|
||||
_exit(kUnexpectedExitStatus);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SignalsTest : public Multiprocess {
|
||||
public:
|
||||
enum class SignalSource {
|
||||
kCause,
|
||||
kRaise,
|
||||
};
|
||||
enum class TestType {
|
||||
kDefaultHandler,
|
||||
kHandlerExits,
|
||||
kHandlerReraisesToDefault,
|
||||
kHandlerReraisesToPrevious,
|
||||
};
|
||||
static constexpr int kExitingHandlerExitStatus = 2;
|
||||
|
||||
SignalsTest(TestType test_type, SignalSource signal_source, int sig)
|
||||
: Multiprocess(),
|
||||
sig_(sig),
|
||||
test_type_(test_type),
|
||||
signal_source_(signal_source) {}
|
||||
~SignalsTest() {}
|
||||
|
||||
private:
|
||||
static void SignalHandler_Exit(int sig, siginfo_t* siginfo, void* context) {
|
||||
_exit(kExitingHandlerExitStatus);
|
||||
}
|
||||
|
||||
static void SignalHandler_ReraiseToDefault(int sig,
|
||||
siginfo_t* siginfo,
|
||||
void* context) {
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
||||
}
|
||||
|
||||
static void SignalHandler_ReraiseToPrevious(int sig,
|
||||
siginfo_t* siginfo,
|
||||
void* context) {
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(
|
||||
siginfo, old_actions_.ActionForSignal(sig));
|
||||
}
|
||||
|
||||
// Multiprocess:
|
||||
void MultiprocessParent() override {}
|
||||
|
||||
void MultiprocessChild() override {
|
||||
bool (*install_handlers)(Signals::Handler, int, Signals::OldActions*);
|
||||
if (Signals::IsCrashSignal(sig_)) {
|
||||
install_handlers = Signals::InstallCrashHandlers;
|
||||
} else if (Signals::IsTerminateSignal(sig_)) {
|
||||
install_handlers = Signals::InstallTerminateHandlers;
|
||||
} else {
|
||||
_exit(kUnexpectedExitStatus);
|
||||
}
|
||||
|
||||
switch (test_type_) {
|
||||
case TestType::kDefaultHandler: {
|
||||
// Don’t rely on the default handler being active. Something may have
|
||||
// changed it (particularly on Android).
|
||||
struct sigaction action;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
action.sa_handler = SIG_DFL;
|
||||
ASSERT_EQ(0, sigaction(sig_, &action, nullptr))
|
||||
<< ErrnoMessage("sigaction");
|
||||
break;
|
||||
}
|
||||
|
||||
case TestType::kHandlerExits: {
|
||||
ASSERT_TRUE(install_handlers(SignalHandler_Exit, 0, nullptr));
|
||||
break;
|
||||
}
|
||||
|
||||
case TestType::kHandlerReraisesToDefault: {
|
||||
ASSERT_TRUE(
|
||||
install_handlers(SignalHandler_ReraiseToDefault, 0, nullptr));
|
||||
break;
|
||||
}
|
||||
|
||||
case TestType::kHandlerReraisesToPrevious: {
|
||||
ASSERT_TRUE(install_handlers(SignalHandler_Exit, 0, nullptr));
|
||||
ASSERT_TRUE(install_handlers(
|
||||
SignalHandler_ReraiseToPrevious, 0, &old_actions_));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (signal_source_) {
|
||||
case SignalSource::kCause:
|
||||
CauseSignal(sig_);
|
||||
break;
|
||||
case SignalSource::kRaise:
|
||||
raise(sig_);
|
||||
break;
|
||||
}
|
||||
|
||||
_exit(kUnexpectedExitStatus);
|
||||
}
|
||||
|
||||
int sig_;
|
||||
TestType test_type_;
|
||||
SignalSource signal_source_;
|
||||
static Signals::OldActions old_actions_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SignalsTest);
|
||||
};
|
||||
|
||||
Signals::OldActions SignalsTest::old_actions_;
|
||||
|
||||
bool ShouldTestSignal(int sig) {
|
||||
return Signals::IsCrashSignal(sig) || Signals::IsTerminateSignal(sig);
|
||||
}
|
||||
|
||||
TEST(Signals, WillSignalReraiseAutonomously) {
|
||||
const struct {
|
||||
int sig;
|
||||
int code;
|
||||
bool result;
|
||||
} kTestData[] = {
|
||||
{SIGBUS, BUS_ADRALN, true},
|
||||
{SIGFPE, FPE_FLTDIV, true},
|
||||
{SIGILL, ILL_ILLOPC, true},
|
||||
{SIGSEGV, SEGV_MAPERR, true},
|
||||
{SIGBUS, 0, false},
|
||||
{SIGFPE, -1, false},
|
||||
{SIGILL, SI_USER, false},
|
||||
{SIGSEGV, SI_QUEUE, false},
|
||||
{SIGTRAP, TRAP_BRKPT, false},
|
||||
{SIGHUP, SEGV_MAPERR, false},
|
||||
{SIGINT, SI_USER, false},
|
||||
};
|
||||
for (size_t index = 0; index < arraysize(kTestData); ++index) {
|
||||
const auto test_data = kTestData[index];
|
||||
SCOPED_TRACE(base::StringPrintf(
|
||||
"index %zu, sig %d, code %d", index, test_data.sig, test_data.code));
|
||||
siginfo_t siginfo = {};
|
||||
siginfo.si_signo = test_data.sig;
|
||||
siginfo.si_code = test_data.code;
|
||||
EXPECT_EQ(test_data.result,
|
||||
Signals::WillSignalReraiseAutonomously(&siginfo));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Signals, Cause_DefaultHandler) {
|
||||
for (int sig = 1; sig < NSIG; ++sig) {
|
||||
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
||||
|
||||
if (!CanCauseSignal(sig)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SignalsTest test(SignalsTest::TestType::kDefaultHandler,
|
||||
SignalsTest::SignalSource::kCause,
|
||||
sig);
|
||||
test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
|
||||
test.Run();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Signals, Cause_HandlerExits) {
|
||||
for (int sig = 1; sig < NSIG; ++sig) {
|
||||
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
||||
|
||||
if (!CanCauseSignal(sig)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SignalsTest test(SignalsTest::TestType::kHandlerExits,
|
||||
SignalsTest::SignalSource::kCause,
|
||||
sig);
|
||||
test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
|
||||
SignalsTest::kExitingHandlerExitStatus);
|
||||
test.Run();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Signals, Cause_HandlerReraisesToDefault) {
|
||||
for (int sig = 1; sig < NSIG; ++sig) {
|
||||
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
||||
|
||||
if (!CanCauseSignal(sig)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToDefault,
|
||||
SignalsTest::SignalSource::kCause,
|
||||
sig);
|
||||
test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
|
||||
test.Run();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Signals, Cause_HandlerReraisesToPrevious) {
|
||||
for (int sig = 1; sig < NSIG; ++sig) {
|
||||
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
||||
|
||||
if (!CanCauseSignal(sig)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToPrevious,
|
||||
SignalsTest::SignalSource::kCause,
|
||||
sig);
|
||||
test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
|
||||
SignalsTest::kExitingHandlerExitStatus);
|
||||
test.Run();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Signals, Raise_DefaultHandler) {
|
||||
for (int sig = 1; sig < NSIG; ++sig) {
|
||||
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
||||
|
||||
if (!ShouldTestSignal(sig)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SignalsTest test(SignalsTest::TestType::kDefaultHandler,
|
||||
SignalsTest::SignalSource::kRaise,
|
||||
sig);
|
||||
test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
|
||||
test.Run();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Signals, Raise_HandlerExits) {
|
||||
for (int sig = 1; sig < NSIG; ++sig) {
|
||||
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
||||
|
||||
if (!ShouldTestSignal(sig)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SignalsTest test(SignalsTest::TestType::kHandlerExits,
|
||||
SignalsTest::SignalSource::kRaise,
|
||||
sig);
|
||||
test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
|
||||
SignalsTest::kExitingHandlerExitStatus);
|
||||
test.Run();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Signals, Raise_HandlerReraisesToDefault) {
|
||||
for (int sig = 1; sig < NSIG; ++sig) {
|
||||
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
||||
|
||||
if (!ShouldTestSignal(sig)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
if (sig == SIGBUS) {
|
||||
// Signal handlers can’t distinguish between SIGBUS arising out of a
|
||||
// hardware fault and SIGBUS raised asynchronously.
|
||||
// Signals::RestoreHandlerAndReraiseSignalOnReturn() assumes that SIGBUS
|
||||
// comes from a hardware fault, but this test uses raise(), so the
|
||||
// re-raise test must be skipped.
|
||||
continue;
|
||||
}
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToDefault,
|
||||
SignalsTest::SignalSource::kRaise,
|
||||
sig);
|
||||
test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
|
||||
test.Run();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Signals, Raise_HandlerReraisesToPrevious) {
|
||||
for (int sig = 1; sig < NSIG; ++sig) {
|
||||
SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
|
||||
|
||||
if (!ShouldTestSignal(sig)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
if (sig == SIGBUS) {
|
||||
// Signal handlers can’t distinguish between SIGBUS arising out of a
|
||||
// hardware fault and SIGBUS raised asynchronously.
|
||||
// Signals::RestoreHandlerAndReraiseSignalOnReturn() assumes that SIGBUS
|
||||
// comes from a hardware fault, but this test uses raise(), so the
|
||||
// re-raise test must be skipped.
|
||||
continue;
|
||||
}
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
SignalsTest test(SignalsTest::TestType::kHandlerReraisesToPrevious,
|
||||
SignalsTest::SignalSource::kRaise,
|
||||
sig);
|
||||
test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
|
||||
SignalsTest::kExitingHandlerExitStatus);
|
||||
test.Run();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Signals, IsCrashSignal) {
|
||||
// Always crash signals.
|
||||
EXPECT_TRUE(Signals::IsCrashSignal(SIGABRT));
|
||||
EXPECT_TRUE(Signals::IsCrashSignal(SIGBUS));
|
||||
EXPECT_TRUE(Signals::IsCrashSignal(SIGFPE));
|
||||
EXPECT_TRUE(Signals::IsCrashSignal(SIGILL));
|
||||
EXPECT_TRUE(Signals::IsCrashSignal(SIGQUIT));
|
||||
EXPECT_TRUE(Signals::IsCrashSignal(SIGSEGV));
|
||||
EXPECT_TRUE(Signals::IsCrashSignal(SIGSYS));
|
||||
EXPECT_TRUE(Signals::IsCrashSignal(SIGTRAP));
|
||||
|
||||
// Always terminate signals.
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGALRM));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGHUP));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGINT));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGPIPE));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGPROF));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGTERM));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGUSR1));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGUSR2));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGVTALRM));
|
||||
|
||||
// Never crash or terminate signals.
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGCHLD));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGCONT));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGTSTP));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGTTIN));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGTTOU));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGURG));
|
||||
EXPECT_FALSE(Signals::IsCrashSignal(SIGWINCH));
|
||||
}
|
||||
|
||||
TEST(Signals, IsTerminateSignal) {
|
||||
// Always terminate signals.
|
||||
EXPECT_TRUE(Signals::IsTerminateSignal(SIGALRM));
|
||||
EXPECT_TRUE(Signals::IsTerminateSignal(SIGHUP));
|
||||
EXPECT_TRUE(Signals::IsTerminateSignal(SIGINT));
|
||||
EXPECT_TRUE(Signals::IsTerminateSignal(SIGPIPE));
|
||||
EXPECT_TRUE(Signals::IsTerminateSignal(SIGPROF));
|
||||
EXPECT_TRUE(Signals::IsTerminateSignal(SIGTERM));
|
||||
EXPECT_TRUE(Signals::IsTerminateSignal(SIGUSR1));
|
||||
EXPECT_TRUE(Signals::IsTerminateSignal(SIGUSR2));
|
||||
EXPECT_TRUE(Signals::IsTerminateSignal(SIGVTALRM));
|
||||
|
||||
// Always crash signals.
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGABRT));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGBUS));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGFPE));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGILL));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGQUIT));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGSEGV));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGSYS));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGTRAP));
|
||||
|
||||
// Never crash or terminate signals.
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGCHLD));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGCONT));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGTSTP));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGTTIN));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGTTOU));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGURG));
|
||||
EXPECT_FALSE(Signals::IsTerminateSignal(SIGWINCH));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -135,6 +135,8 @@
|
||||
'posix/drop_privileges.h',
|
||||
'posix/process_info.h',
|
||||
'posix/process_info_mac.cc',
|
||||
'posix/signals.cc',
|
||||
'posix/signals.h',
|
||||
'posix/symbolic_constants_posix.cc',
|
||||
'posix/symbolic_constants_posix.h',
|
||||
'stdlib/aligned_allocator.cc',
|
||||
@ -271,6 +273,7 @@
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
'-luser32.lib',
|
||||
'-lversion.lib',
|
||||
'-lwinhttp.lib',
|
||||
],
|
||||
},
|
||||
|
@ -74,6 +74,7 @@
|
||||
'numeric/in_range_cast_test.cc',
|
||||
'numeric/int128_test.cc',
|
||||
'posix/process_info_test.cc',
|
||||
'posix/signals_test.cc',
|
||||
'posix/symbolic_constants_posix_test.cc',
|
||||
'stdlib/aligned_allocator_test.cc',
|
||||
'stdlib/map_insert_test.cc',
|
||||
|
Loading…
x
Reference in New Issue
Block a user