2022-04-02 01:21:55 +00:00
|
|
|
|
// Copyright 2020 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 "client/crashpad_client.h"
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#include <signal.h>
|
2022-04-02 01:21:55 +00:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#include <atomic>
|
2022-04-02 01:21:55 +00:00
|
|
|
|
#include <ios>
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#include <iterator>
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
#include "base/logging.h"
|
|
|
|
|
#include "base/mac/mach_logging.h"
|
|
|
|
|
#include "base/mac/scoped_mach_port.h"
|
|
|
|
|
#include "client/ios_handler/exception_processor.h"
|
|
|
|
|
#include "client/ios_handler/in_process_handler.h"
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#include "util/ios/raw_logging.h"
|
2022-04-02 01:21:55 +00:00
|
|
|
|
#include "util/mach/exc_server_variants.h"
|
|
|
|
|
#include "util/mach/exception_ports.h"
|
|
|
|
|
#include "util/mach/mach_extensions.h"
|
|
|
|
|
#include "util/mach/mach_message.h"
|
|
|
|
|
#include "util/mach/mach_message_server.h"
|
|
|
|
|
#include "util/misc/initialization_state_dcheck.h"
|
|
|
|
|
#include "util/posix/signals.h"
|
|
|
|
|
#include "util/thread/thread.h"
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
bool IsBeingDebugged() {
|
|
|
|
|
kinfo_proc kern_proc_info;
|
|
|
|
|
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
|
|
|
|
|
size_t len = sizeof(kern_proc_info);
|
2022-08-16 00:48:53 +00:00
|
|
|
|
if (sysctl(mib, std::size(mib), &kern_proc_info, &len, nullptr, 0) == 0)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
return kern_proc_info.kp_proc.p_flag & P_TRACED;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
// A base class for signal handler and Mach exception server.
|
|
|
|
|
class CrashHandler : public Thread,
|
|
|
|
|
public UniversalMachExcServer::Interface,
|
|
|
|
|
public ObjcExceptionDelegate {
|
|
|
|
|
public:
|
|
|
|
|
CrashHandler(const CrashHandler&) = delete;
|
|
|
|
|
CrashHandler& operator=(const CrashHandler&) = delete;
|
|
|
|
|
|
|
|
|
|
static CrashHandler* Get() {
|
2022-08-16 00:48:53 +00:00
|
|
|
|
if (!instance_)
|
|
|
|
|
instance_ = new CrashHandler();
|
|
|
|
|
return instance_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ResetForTesting() {
|
|
|
|
|
delete instance_;
|
|
|
|
|
instance_ = nullptr;
|
2022-04-02 01:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Initialize(const base::FilePath& database,
|
|
|
|
|
const std::string& url,
|
|
|
|
|
const std::map<std::string, std::string>& annotations) {
|
|
|
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
2022-08-16 00:48:53 +00:00
|
|
|
|
if (!in_process_handler_.Initialize(database, url, annotations) ||
|
2022-04-02 01:21:55 +00:00
|
|
|
|
!InstallMachExceptionHandler() ||
|
2022-08-16 00:48:53 +00:00
|
|
|
|
// xnu turns hardware faults into Mach exceptions, so the only signal
|
|
|
|
|
// left to register is SIGABRT, which never starts off as a hardware
|
|
|
|
|
// fault. Installing a handler for other signals would lead to
|
|
|
|
|
// recording exceptions twice. As a consequence, Crashpad will not
|
|
|
|
|
// generate intermediate dumps for anything manually calling
|
|
|
|
|
// raise(SIG*). In practice, this doesn’t actually happen for crash
|
|
|
|
|
// signals that originate as hardware faults.
|
|
|
|
|
!Signals::InstallHandler(
|
|
|
|
|
SIGABRT, CatchAndReraiseSignal, 0, &old_action_)) {
|
2022-04-02 01:21:55 +00:00
|
|
|
|
LOG(ERROR) << "Unable to initialize Crashpad.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-08-16 00:48:53 +00:00
|
|
|
|
|
|
|
|
|
// For applications that haven't ignored or set a handler for SIGPIPE:
|
|
|
|
|
// It’s OK for an application to set its own SIGPIPE handler (including
|
|
|
|
|
// SIG_IGN) before initializing Crashpad, because Crashpad will discover the
|
|
|
|
|
// existing handler and not install its own.
|
|
|
|
|
// It’s OK for Crashpad to install its own SIGPIPE handler and for the
|
|
|
|
|
// application to subsequently install its own (including SIG_IGN)
|
|
|
|
|
// afterwards, because its handler will replace Crashpad’s.
|
|
|
|
|
// This is useful to cover the default situation where nobody installs a
|
|
|
|
|
// SIGPIPE handler and the disposition is at SIG_DFL, because SIGPIPE is a
|
|
|
|
|
// “kill” signal (bsd/sys/signalvar.h sigprop). In that case, without
|
|
|
|
|
// Crashpad, SIGPIPE results in a silent and unreported kill (and not even
|
|
|
|
|
// ReportCrash will record it), but developers probably want to be alerted
|
|
|
|
|
// to the conditon.
|
|
|
|
|
struct sigaction sa;
|
|
|
|
|
if (sigaction(SIGPIPE, nullptr, &sa) == 0 && sa.sa_handler == SIG_DFL) {
|
|
|
|
|
Signals::InstallHandler(
|
|
|
|
|
SIGPIPE, CatchAndReraiseSignalDefaultAction, 0, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-02 01:21:55 +00:00
|
|
|
|
InstallObjcExceptionPreprocessor(this);
|
|
|
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ProcessIntermediateDumps(
|
|
|
|
|
const std::map<std::string, std::string>& annotations) {
|
|
|
|
|
in_process_handler_.ProcessIntermediateDumps(annotations);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ProcessIntermediateDump(
|
|
|
|
|
const base::FilePath& file,
|
|
|
|
|
const std::map<std::string, std::string>& annotations) {
|
|
|
|
|
in_process_handler_.ProcessIntermediateDump(file, annotations);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DumpWithoutCrash(NativeCPUContext* context, bool process_dump) {
|
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
2022-08-16 00:48:53 +00:00
|
|
|
|
base::FilePath path;
|
|
|
|
|
if (!in_process_handler_.DumpExceptionFromSimulatedMachException(
|
|
|
|
|
context, kMachExceptionSimulated, &path)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (process_dump) {
|
|
|
|
|
in_process_handler_.ProcessIntermediateDump(path);
|
2022-04-02 01:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DumpWithoutCrashAtPath(NativeCPUContext* context,
|
|
|
|
|
const base::FilePath& path) {
|
2022-08-16 00:48:53 +00:00
|
|
|
|
in_process_handler_.DumpExceptionFromSimulatedMachExceptionAtPath(
|
|
|
|
|
context, kMachExceptionSimulated, path);
|
2022-04-02 01:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StartProcessingPendingReports() {
|
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
in_process_handler_.StartProcessingPendingReports();
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
void SetMachExceptionCallbackForTesting(void (*callback)()) {
|
|
|
|
|
in_process_handler_.SetMachExceptionCallbackForTesting(callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t GetThreadIdForTesting() { return Thread::GetThreadIdForTesting(); }
|
|
|
|
|
|
2022-04-02 01:21:55 +00:00
|
|
|
|
private:
|
|
|
|
|
CrashHandler() = default;
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
~CrashHandler() {
|
|
|
|
|
UninstallObjcExceptionPreprocessor();
|
|
|
|
|
Signals::InstallDefaultHandler(SIGABRT);
|
|
|
|
|
UninstallMachExceptionHandler();
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-02 01:21:55 +00:00
|
|
|
|
bool InstallMachExceptionHandler() {
|
|
|
|
|
exception_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
|
|
|
|
if (!exception_port_.is_valid()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kern_return_t kr = mach_port_insert_right(mach_task_self(),
|
|
|
|
|
exception_port_.get(),
|
|
|
|
|
exception_port_.get(),
|
|
|
|
|
MACH_MSG_TYPE_MAKE_SEND);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(ERROR, kr) << "mach_port_insert_right";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Use SwapExceptionPort instead and put back EXC_MASK_BREAKPOINT.
|
|
|
|
|
// Until then, remove |EXC_MASK_BREAKPOINT| while attached to a debugger.
|
|
|
|
|
exception_mask_t mask =
|
|
|
|
|
ExcMaskAll() &
|
|
|
|
|
~(EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_RPC_ALERT |
|
|
|
|
|
EXC_MASK_GUARD | (IsBeingDebugged() ? EXC_MASK_BREAKPOINT : 0));
|
|
|
|
|
|
|
|
|
|
ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);
|
|
|
|
|
if (!exception_ports.GetExceptionPorts(mask, &original_handlers_) ||
|
|
|
|
|
!exception_ports.SetExceptionPort(
|
|
|
|
|
mask,
|
|
|
|
|
exception_port_.get(),
|
|
|
|
|
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
|
|
|
|
|
MACHINE_THREAD_STATE)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
mach_handler_running_ = true;
|
2022-04-02 01:21:55 +00:00
|
|
|
|
Start();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
void UninstallMachExceptionHandler() {
|
|
|
|
|
mach_handler_running_ = false;
|
|
|
|
|
exception_port_.reset();
|
|
|
|
|
Join();
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-02 01:21:55 +00:00
|
|
|
|
// Thread:
|
|
|
|
|
|
|
|
|
|
void ThreadMain() override {
|
|
|
|
|
UniversalMachExcServer universal_mach_exc_server(this);
|
2022-08-16 00:48:53 +00:00
|
|
|
|
while (mach_handler_running_) {
|
2022-04-02 01:21:55 +00:00
|
|
|
|
mach_msg_return_t mr =
|
|
|
|
|
MachMessageServer::Run(&universal_mach_exc_server,
|
|
|
|
|
exception_port_.get(),
|
|
|
|
|
MACH_MSG_OPTION_NONE,
|
|
|
|
|
MachMessageServer::kPersistent,
|
|
|
|
|
MachMessageServer::kReceiveLargeIgnore,
|
|
|
|
|
kMachMessageTimeoutWaitIndefinitely);
|
2022-08-16 00:48:53 +00:00
|
|
|
|
MACH_CHECK(
|
|
|
|
|
mach_handler_running_
|
|
|
|
|
? mr == MACH_SEND_INVALID_DEST // This shouldn't happen for
|
|
|
|
|
// exception messages that come
|
|
|
|
|
// from the kernel itself, but if
|
|
|
|
|
// something else in-process sends
|
|
|
|
|
// exception messages and breaks,
|
|
|
|
|
// handle that case.
|
|
|
|
|
: (mr == MACH_RCV_PORT_CHANGED || // Port was closed while the
|
|
|
|
|
// thread was listening.
|
|
|
|
|
mr == MACH_RCV_INVALID_NAME), // Port was closed before the
|
|
|
|
|
// thread started listening.
|
|
|
|
|
mr)
|
|
|
|
|
<< "MachMessageServer::Run";
|
2022-04-02 01:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UniversalMachExcServer::Interface:
|
|
|
|
|
|
|
|
|
|
kern_return_t CatchMachException(exception_behavior_t behavior,
|
|
|
|
|
exception_handler_t exception_port,
|
|
|
|
|
thread_t thread,
|
|
|
|
|
task_t task,
|
|
|
|
|
exception_type_t exception,
|
|
|
|
|
const mach_exception_data_type_t* code,
|
|
|
|
|
mach_msg_type_number_t code_count,
|
|
|
|
|
thread_state_flavor_t* flavor,
|
|
|
|
|
ConstThreadState old_state,
|
|
|
|
|
mach_msg_type_number_t old_state_count,
|
|
|
|
|
thread_state_t new_state,
|
|
|
|
|
mach_msg_type_number_t* new_state_count,
|
|
|
|
|
const mach_msg_trailer_t* trailer,
|
|
|
|
|
bool* destroy_complex_request) override {
|
|
|
|
|
*destroy_complex_request = true;
|
|
|
|
|
|
|
|
|
|
// TODO(justincohen): Forward exceptions to original_handlers_ with
|
|
|
|
|
// UniversalExceptionRaise.
|
|
|
|
|
|
|
|
|
|
// iOS shouldn't have any child processes, but just in case, those will
|
|
|
|
|
// inherit the task exception ports, and this process isn’t prepared to
|
|
|
|
|
// handle them
|
|
|
|
|
if (task != mach_task_self()) {
|
2022-08-16 00:48:53 +00:00
|
|
|
|
CRASHPAD_RAW_LOG("MachException task != mach_task_self()");
|
2022-04-02 01:21:55 +00:00
|
|
|
|
return KERN_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HandleMachException(behavior,
|
|
|
|
|
thread,
|
|
|
|
|
exception,
|
|
|
|
|
code,
|
|
|
|
|
code_count,
|
|
|
|
|
*flavor,
|
|
|
|
|
old_state,
|
|
|
|
|
old_state_count);
|
|
|
|
|
|
|
|
|
|
// Respond with KERN_FAILURE so the system will continue to handle this
|
2022-08-16 00:48:53 +00:00
|
|
|
|
// exception. xnu will turn this Mach exception into a signal and take the
|
|
|
|
|
// default action to terminate the process. However, if sigprocmask is
|
|
|
|
|
// called before this Mach exception returns (such as by another thread
|
|
|
|
|
// calling abort, see: Libc-1506.40.4/stdlib/FreeBSD/abort.c), the Mach
|
|
|
|
|
// exception will be converted into a signal but delivery will be blocked.
|
|
|
|
|
// Since concurrent exceptions lead to the losing thread sleeping
|
|
|
|
|
// indefinitely, if the abort thread never returns, the thread that
|
|
|
|
|
// triggered this Mach exception will repeatedly trap and the process will
|
|
|
|
|
// never terminate. If the abort thread didn’t have a user-space signal
|
|
|
|
|
// handler that slept forever, the abort would terminate the process even if
|
|
|
|
|
// all other signals had been blocked. Instead, unblock all signals
|
|
|
|
|
// corresponding to all Mach exceptions Crashpad is registered for before
|
|
|
|
|
// returning KERN_FAILURE. There is still racy behavior possible with this
|
|
|
|
|
// call to sigprocmask, but the repeated calls to CatchMachException here
|
|
|
|
|
// will eventually lead to termination.
|
|
|
|
|
sigset_t unblock_set;
|
|
|
|
|
sigemptyset(&unblock_set);
|
|
|
|
|
sigaddset(&unblock_set, SIGILL); // EXC_BAD_INSTRUCTION
|
|
|
|
|
sigaddset(&unblock_set, SIGTRAP); // EXC_BREAKPOINT
|
|
|
|
|
sigaddset(&unblock_set, SIGFPE); // EXC_ARITHMETIC
|
|
|
|
|
sigaddset(&unblock_set, SIGBUS); // EXC_BAD_ACCESS
|
|
|
|
|
sigaddset(&unblock_set, SIGSEGV); // EXC_BAD_ACCESS
|
|
|
|
|
if (sigprocmask(SIG_UNBLOCK, &unblock_set, nullptr) != 0) {
|
|
|
|
|
CRASHPAD_RAW_LOG("sigprocmask");
|
|
|
|
|
}
|
2022-04-02 01:21:55 +00:00
|
|
|
|
return KERN_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HandleMachException(exception_behavior_t behavior,
|
|
|
|
|
thread_t thread,
|
|
|
|
|
exception_type_t exception,
|
|
|
|
|
const mach_exception_data_type_t* code,
|
|
|
|
|
mach_msg_type_number_t code_count,
|
|
|
|
|
thread_state_flavor_t flavor,
|
|
|
|
|
ConstThreadState old_state,
|
|
|
|
|
mach_msg_type_number_t old_state_count) {
|
2022-08-16 00:48:53 +00:00
|
|
|
|
in_process_handler_.DumpExceptionFromMachException(behavior,
|
2022-04-02 01:21:55 +00:00
|
|
|
|
thread,
|
|
|
|
|
exception,
|
|
|
|
|
code,
|
|
|
|
|
code_count,
|
|
|
|
|
flavor,
|
|
|
|
|
old_state,
|
|
|
|
|
old_state_count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HandleUncaughtNSException(const uint64_t* frames,
|
|
|
|
|
const size_t num_frames) override {
|
2022-08-16 00:48:53 +00:00
|
|
|
|
in_process_handler_.DumpExceptionFromNSExceptionWithFrames(frames,
|
|
|
|
|
num_frames);
|
2022-04-02 01:21:55 +00:00
|
|
|
|
// After uncaught exceptions are reported, the system immediately triggers a
|
|
|
|
|
// call to std::terminate()/abort(). Remove the abort handler so a second
|
|
|
|
|
// dump isn't generated.
|
|
|
|
|
CHECK(Signals::InstallDefaultHandler(SIGABRT));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HandleUncaughtNSExceptionWithContext(
|
|
|
|
|
NativeCPUContext* context) override {
|
2022-08-16 00:48:53 +00:00
|
|
|
|
base::FilePath path;
|
|
|
|
|
in_process_handler_.DumpExceptionFromSimulatedMachException(
|
|
|
|
|
context, kMachExceptionFromNSException, &path);
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
// After uncaught exceptions are reported, the system immediately triggers a
|
|
|
|
|
// call to std::terminate()/abort(). Remove the abort handler so a second
|
|
|
|
|
// dump isn't generated.
|
|
|
|
|
CHECK(Signals::InstallDefaultHandler(SIGABRT));
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
void HandleUncaughtNSExceptionWithContextAtPath(
|
|
|
|
|
NativeCPUContext* context,
|
|
|
|
|
const base::FilePath& path) override {
|
|
|
|
|
in_process_handler_.DumpExceptionFromSimulatedMachExceptionAtPath(
|
|
|
|
|
context, kMachExceptionFromNSException, path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MoveIntermediateDumpAtPathToPending(
|
|
|
|
|
const base::FilePath& path) override {
|
|
|
|
|
if (in_process_handler_.MoveIntermediateDumpAtPathToPending(path)) {
|
|
|
|
|
// After uncaught exceptions are reported, the system immediately triggers
|
|
|
|
|
// a call to std::terminate()/abort(). Remove the abort handler so a
|
|
|
|
|
// second dump isn't generated.
|
|
|
|
|
CHECK(Signals::InstallDefaultHandler(SIGABRT));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-02 01:21:55 +00:00
|
|
|
|
// The signal handler installed at OS-level.
|
2022-08-16 00:48:53 +00:00
|
|
|
|
static void CatchAndReraiseSignal(int signo,
|
|
|
|
|
siginfo_t* siginfo,
|
|
|
|
|
void* context) {
|
|
|
|
|
Get()->HandleAndReraiseSignal(signo,
|
|
|
|
|
siginfo,
|
|
|
|
|
reinterpret_cast<ucontext_t*>(context),
|
|
|
|
|
&(Get()->old_action_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CatchAndReraiseSignalDefaultAction(int signo,
|
|
|
|
|
siginfo_t* siginfo,
|
|
|
|
|
void* context) {
|
2022-04-02 01:21:55 +00:00
|
|
|
|
Get()->HandleAndReraiseSignal(
|
2022-08-16 00:48:53 +00:00
|
|
|
|
signo, siginfo, reinterpret_cast<ucontext_t*>(context), nullptr);
|
2022-04-02 01:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HandleAndReraiseSignal(int signo,
|
|
|
|
|
siginfo_t* siginfo,
|
2022-08-16 00:48:53 +00:00
|
|
|
|
ucontext_t* context,
|
|
|
|
|
struct sigaction* old_action) {
|
|
|
|
|
in_process_handler_.DumpExceptionFromSignal(siginfo, context);
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
// Always call system handler.
|
2022-08-16 00:48:53 +00:00
|
|
|
|
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, old_action);
|
2022-04-02 01:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base::mac::ScopedMachReceiveRight exception_port_;
|
|
|
|
|
ExceptionPorts::ExceptionHandlerVector original_handlers_;
|
|
|
|
|
struct sigaction old_action_ = {};
|
|
|
|
|
internal::InProcessHandler in_process_handler_;
|
2022-08-16 00:48:53 +00:00
|
|
|
|
static CrashHandler* instance_;
|
|
|
|
|
std::atomic<bool> mach_handler_running_ = false;
|
2022-04-02 01:21:55 +00:00
|
|
|
|
InitializationStateDcheck initialized_;
|
|
|
|
|
};
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
CrashHandler* CrashHandler::instance_ = nullptr;
|
|
|
|
|
|
2022-04-02 01:21:55 +00:00
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
CrashpadClient::CrashpadClient() {}
|
|
|
|
|
|
|
|
|
|
CrashpadClient::~CrashpadClient() {}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
bool CrashpadClient::StartCrashpadInProcessHandler(
|
|
|
|
|
const base::FilePath& database,
|
|
|
|
|
const std::string& url,
|
|
|
|
|
const std::map<std::string, std::string>& annotations) {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
return crash_handler->Initialize(database, url, annotations);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void CrashpadClient::ProcessIntermediateDumps(
|
|
|
|
|
const std::map<std::string, std::string>& annotations) {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->ProcessIntermediateDumps(annotations);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void CrashpadClient::ProcessIntermediateDump(
|
|
|
|
|
const base::FilePath& file,
|
|
|
|
|
const std::map<std::string, std::string>& annotations) {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->ProcessIntermediateDump(file, annotations);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void CrashpadClient::StartProcessingPendingReports() {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->StartProcessingPendingReports();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->DumpWithoutCrash(context, /*process_dump=*/true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void CrashpadClient::DumpWithoutCrashAndDeferProcessing(
|
|
|
|
|
NativeCPUContext* context) {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->DumpWithoutCrash(context, /*process_dump=*/false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void CrashpadClient::DumpWithoutCrashAndDeferProcessingAtPath(
|
|
|
|
|
NativeCPUContext* context,
|
|
|
|
|
const base::FilePath path) {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->DumpWithoutCrashAtPath(context, path);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 00:48:53 +00:00
|
|
|
|
void CrashpadClient::ResetForTesting() {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->ResetForTesting();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CrashpadClient::SetMachExceptionCallbackForTesting(void (*callback)()) {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->SetMachExceptionCallbackForTesting(callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t CrashpadClient::GetThreadIdForTesting() {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
return crash_handler->GetThreadIdForTesting();
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-02 01:21:55 +00:00
|
|
|
|
} // namespace crashpad
|