Update Crashpad to 25e67e285c01b2b0c7d654dc2238176f0f1a56c3
4c85c466b00c ios: Fix test failure on M1 ARM64 machines
460dbdceaeb4 ios: Unblock all signals corresponding to Mach exceptions
on crash
243dffb04573 ios: Stop prune and upload thread when app is inactive and
may suspend
25e67e285c01 ios: Track last NSException in ObjcException preprocessor
Change-Id: If360f268af8180c3706c35ede6c4ddde9638843d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3546087
Auto-Submit: Justin Cohen <justincohen@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Commit-Queue: Robert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/main@{#984839}
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index b0420172..1b9e7efd 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
Short Name: crashpad
URL: https://crashpad.chromium.org/
Version: unknown
-Revision: cd13ea34ebca8f993d4d60fa40dc7fde3a95bee3
+Revision: 25e67e285c01b2b0c7d654dc2238176f0f1a56c3
License: Apache 2.0
License File: crashpad/LICENSE
Security Critical: yes
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_ios.cc b/third_party/crashpad/crashpad/client/crashpad_client_ios.cc
index e98a232..af9524ef 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_ios.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_ios.cc
@@ -14,6 +14,7 @@
#include "client/crashpad_client.h"
+#include <signal.h>
#include <unistd.h>
#include <ios>
@@ -24,6 +25,7 @@
#include "base/mac/scoped_mach_port.h"
#include "client/ios_handler/exception_processor.h"
#include "client/ios_handler/in_process_handler.h"
+#include "util/ios/raw_logging.h"
#include "util/mach/exc_server_variants.h"
#include "util/mach/exception_ports.h"
#include "util/mach/mach_extensions.h"
@@ -249,8 +251,7 @@
// inherit the task exception ports, and this process isn’t prepared to
// handle them
if (task != mach_task_self()) {
- LOG(WARNING) << "task 0x" << std::hex << task << " != 0x"
- << mach_task_self();
+ CRASHPAD_RAW_LOG("MachException task != mach_task_self()");
return KERN_FAILURE;
}
@@ -264,7 +265,31 @@
old_state_count);
// Respond with KERN_FAILURE so the system will continue to handle this
- // exception as a crash.
+ // 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");
+ }
return KERN_FAILURE;
}
diff --git a/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm b/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm
index 3398132..7b689c54 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm
+++ b/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm
@@ -53,6 +53,8 @@
#include "client/annotation.h"
#include "client/simulate_crash_ios.h"
+namespace crashpad {
+
namespace {
// From 10.15.0 objc4-779.1/runtime/objc-exception.mm.
@@ -132,16 +134,67 @@
return FormatStackTrace(addresses, 1024);
}
-crashpad::ObjcExceptionDelegate* g_exception_delegate;
-objc_exception_preprocessor g_next_preprocessor;
-NSUncaughtExceptionHandler* g_next_uncaught_exception_handler;
+//! \brief Helper class to own the complex types used by the Objective-C
+//! exception preprocessor.
+class ExceptionPreprocessorState {
+ public:
+ ExceptionPreprocessorState(const ExceptionPreprocessorState&) = delete;
+ ExceptionPreprocessorState& operator=(const ExceptionPreprocessorState&) =
+ delete;
+
+ static ExceptionPreprocessorState* Get() {
+ static ExceptionPreprocessorState* instance = []() {
+ return new ExceptionPreprocessorState();
+ }();
+ return instance;
+ }
+
+ // Inform the delegate of the uncaught exception and remove the global
+ // uncaught exception handler so we don't record this twice.
+ void HandleUncaughtException(NativeCPUContext* cpu_context) {
+ exception_delegate_->HandleUncaughtNSExceptionWithContext(cpu_context);
+
+ NSSetUncaughtExceptionHandler(next_uncaught_exception_handler_);
+ next_uncaught_exception_handler_ = nullptr;
+ }
+
+ id MaybeCallNextPreprocessor(id exception) {
+ return next_preprocessor_ ? next_preprocessor_(exception) : exception;
+ }
+
+ // Register the objc_setExceptionPreprocessor and NSUncaughtExceptionHandler.
+ void Install(ObjcExceptionDelegate* delegate);
+
+ // Restore the objc_setExceptionPreprocessor and NSUncaughtExceptionHandler.
+ void Uninstall();
+
+ NSException* last_exception() { return last_exception_; }
+ void set_last_exception(NSException* exception) {
+ [last_exception_ release];
+ last_exception_ = [exception retain];
+ }
+
+ ObjcExceptionDelegate* exception_delegate() { return exception_delegate_; }
+
+ private:
+ ExceptionPreprocessorState() = default;
+ ~ExceptionPreprocessorState() = default;
+
+ // Recorded last NSException in case the exception is caught and thrown again
+ // (without using objc_exception_rethrow.)
+ NSException* last_exception_ = nil;
+
+ ObjcExceptionDelegate* exception_delegate_ = nullptr;
+ objc_exception_preprocessor next_preprocessor_ = nullptr;
+ NSUncaughtExceptionHandler* next_uncaught_exception_handler_ = nullptr;
+};
static void SetNSExceptionAnnotations(NSException* exception,
std::string& name,
std::string& reason) {
@try {
name = base::SysNSStringToUTF8(exception.name);
- static crashpad::StringAnnotation<256> nameKey("exceptionName");
+ static StringAnnotation<256> nameKey("exceptionName");
nameKey.Set(name);
} @catch (id name_exception) {
LOG(ERROR) << "Unable to read uncaught Objective-C exception name.";
@@ -149,7 +202,7 @@
@try {
reason = base::SysNSStringToUTF8(exception.reason);
- static crashpad::StringAnnotation<512> reasonKey("exceptionReason");
+ static StringAnnotation<512> reasonKey("exceptionReason");
reasonKey.Set(reason);
} @catch (id reason_exception) {
LOG(ERROR) << "Unable to read uncaught Objective-C exception reason.";
@@ -157,7 +210,7 @@
@try {
if (exception.userInfo) {
- static crashpad::StringAnnotation<512> userInfoKey("exceptionUserInfo");
+ static StringAnnotation<512> userInfoKey("exceptionUserInfo");
userInfoKey.Set(base::SysNSStringToUTF8(
[NSString stringWithFormat:@"%@", exception.userInfo]));
}
@@ -170,21 +223,24 @@
std::string name, reason;
SetNSExceptionAnnotations(exception, name, reason);
NSArray<NSNumber*>* addressArray = [exception callStackReturnAddresses];
+
+ ObjcExceptionDelegate* exception_delegate =
+ ExceptionPreprocessorState::Get()->exception_delegate();
if ([addressArray count] > 0) {
- static crashpad::StringAnnotation<256> nameKey("UncaughtNSException");
+ static StringAnnotation<256> nameKey("UncaughtNSException");
nameKey.Set("true");
std::vector<uint64_t> addresses;
for (NSNumber* address in addressArray)
addresses.push_back([address unsignedLongLongValue]);
- g_exception_delegate->HandleUncaughtNSException(&addresses[0],
- addresses.size());
+ exception_delegate->HandleUncaughtNSException(&addresses[0],
+ addresses.size());
} else {
LOG(WARNING) << "Uncaught Objective-C exception name: " << name
<< " reason: " << reason << " with no "
<< " -callStackReturnAddresses.";
- crashpad::NativeCPUContext cpu_context;
- crashpad::CaptureContext(&cpu_context);
- g_exception_delegate->HandleUncaughtNSExceptionWithContext(&cpu_context);
+ NativeCPUContext cpu_context;
+ CaptureContext(&cpu_context);
+ exception_delegate->HandleUncaughtNSExceptionWithContext(&cpu_context);
}
}
@@ -197,15 +253,13 @@
SetNSExceptionAnnotations(exception, name, reason);
LOG(WARNING) << "Handling Objective-C exception name: " << name
<< " reason: " << reason << " with sinkhole: " << sinkhole;
- crashpad::NativeCPUContext cpu_context;
- crashpad::CaptureContext(&cpu_context);
- g_exception_delegate->HandleUncaughtNSExceptionWithContext(&cpu_context);
+ NativeCPUContext cpu_context;
+ CaptureContext(&cpu_context);
- // Remove the uncaught exception handler so we don't record this twice.
- NSSetUncaughtExceptionHandler(g_next_uncaught_exception_handler);
- g_next_uncaught_exception_handler = nullptr;
-
- return g_next_preprocessor ? g_next_preprocessor(exception) : exception;
+ ExceptionPreprocessorState* preprocessor_state =
+ ExceptionPreprocessorState::Get();
+ preprocessor_state->HandleUncaughtException(&cpu_context);
+ return preprocessor_state->MaybeCallNextPreprocessor(exception);
}
// Returns true if |path| equals |sinkhole| on device. Simulator paths prepend
@@ -226,13 +280,23 @@
}
id ObjcExceptionPreprocessor(id exception) {
+ // Some sinkholes don't use objc_exception_rethrow when they should, which
+ // would otherwise prevent the exception_preprocessor from getting called
+ // again. Because of this, track the most recently seen exception and
+ // ignore it.
+ ExceptionPreprocessorState* preprocessor_state =
+ ExceptionPreprocessorState::Get();
+ if ([preprocessor_state->last_exception() isEqual:exception]) {
+ return preprocessor_state->MaybeCallNextPreprocessor(exception);
+ }
+ preprocessor_state->set_last_exception(exception);
+
static bool seen_first_exception;
- static crashpad::StringAnnotation<256> firstexception("firstexception");
- static crashpad::StringAnnotation<256> lastexception("lastexception");
- static crashpad::StringAnnotation<1024> firstexception_bt(
- "firstexception_bt");
- static crashpad::StringAnnotation<1024> lastexception_bt("lastexception_bt");
+ static StringAnnotation<256> firstexception("firstexception");
+ static StringAnnotation<256> lastexception("lastexception");
+ static StringAnnotation<1024> firstexception_bt("firstexception_bt");
+ static StringAnnotation<1024> lastexception_bt("lastexception_bt");
auto* key = seen_first_exception ? &lastexception : &firstexception;
auto* bt_key = seen_first_exception ? &lastexception_bt : &firstexception_bt;
NSString* value = [NSString
@@ -470,36 +534,41 @@
}
// Forward to the next preprocessor.
- return g_next_preprocessor ? g_next_preprocessor(exception) : exception;
+ return preprocessor_state->MaybeCallNextPreprocessor(exception);
+}
+
+void ExceptionPreprocessorState::Install(ObjcExceptionDelegate* delegate) {
+ DCHECK(!next_preprocessor_);
+ exception_delegate_ = delegate;
+
+ // Preprocessor.
+ next_preprocessor_ =
+ objc_setExceptionPreprocessor(&ObjcExceptionPreprocessor);
+
+ // Uncaught processor.
+ next_uncaught_exception_handler_ = NSGetUncaughtExceptionHandler();
+ NSSetUncaughtExceptionHandler(&ObjcUncaughtExceptionHandler);
+}
+
+void ExceptionPreprocessorState::Uninstall() {
+ DCHECK(next_preprocessor_);
+ objc_setExceptionPreprocessor(next_preprocessor_);
+ next_preprocessor_ = nullptr;
+
+ NSSetUncaughtExceptionHandler(next_uncaught_exception_handler_);
+ next_uncaught_exception_handler_ = nullptr;
+
+ exception_delegate_ = nullptr;
}
} // namespace
-namespace crashpad {
-
void InstallObjcExceptionPreprocessor(ObjcExceptionDelegate* delegate) {
- DCHECK(!g_next_preprocessor);
-
- // Preprocessor.
- g_next_preprocessor =
- objc_setExceptionPreprocessor(&ObjcExceptionPreprocessor);
-
- // Uncaught processor.
- g_exception_delegate = delegate;
- g_next_uncaught_exception_handler = NSGetUncaughtExceptionHandler();
- NSSetUncaughtExceptionHandler(&ObjcUncaughtExceptionHandler);
+ ExceptionPreprocessorState::Get()->Install(delegate);
}
void UninstallObjcExceptionPreprocessor() {
- DCHECK(g_next_preprocessor);
-
- objc_setExceptionPreprocessor(g_next_preprocessor);
- g_exception_delegate = nullptr;
-
- NSSetUncaughtExceptionHandler(g_next_uncaught_exception_handler);
- g_next_uncaught_exception_handler = nullptr;
-
- g_next_preprocessor = nullptr;
+ ExceptionPreprocessorState::Get()->Uninstall();
}
} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.cc b/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.cc
index ffcbceb..0642884 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.cc
+++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.cc
@@ -62,10 +62,7 @@
InProcessHandler::InProcessHandler() = default;
InProcessHandler::~InProcessHandler() {
- if (upload_thread_started_ && upload_thread_) {
- upload_thread_->Stop();
- }
- prune_thread_->Stop();
+ UpdatePruneAndUploadThreads(false);
}
bool InProcessHandler::Initialize(
@@ -103,13 +100,20 @@
if (!CreateDirectory(base_dir_))
return false;
+ bool is_app_extension = system_data_.IsExtension();
prune_thread_.reset(new PruneIntermediateDumpsAndCrashReportsThread(
database_.get(),
PruneCondition::GetDefault(),
base_dir_,
bundle_identifier_and_seperator_,
- system_data_.IsExtension()));
- prune_thread_->Start();
+ is_app_extension));
+ if (is_app_extension || system_data_.IsApplicationActive())
+ prune_thread_->Start();
+
+ if (!is_app_extension) {
+ system_data_.SetActiveApplicationCallback(
+ [this](bool active) { UpdatePruneAndUploadThreads(active); });
+ }
base::FilePath cached_writer_path = NewLockedFilePath();
cached_writer_ = CreateWriterWithPath(cached_writer_path);
@@ -284,9 +288,29 @@
}
void InProcessHandler::StartProcessingPendingReports() {
- if (!upload_thread_started_ && upload_thread_) {
- upload_thread_->Start();
- upload_thread_started_ = true;
+ if (!upload_thread_)
+ return;
+
+ upload_thread_enabled_ = true;
+ UpdatePruneAndUploadThreads(true);
+}
+
+void InProcessHandler::UpdatePruneAndUploadThreads(bool active) {
+ base::AutoLock lock_owner(prune_and_upload_lock_);
+ // TODO(crbug.com/crashpad/400): Consider moving prune and upload thread to
+ // BackgroundTasks and/or NSURLSession. This might allow uploads to continue
+ // in the background.
+ if (active) {
+ if (!prune_thread_->is_running())
+ prune_thread_->Start();
+ if (upload_thread_enabled_ && !upload_thread_->is_running()) {
+ upload_thread_->Start();
+ }
+ } else {
+ if (prune_thread_->is_running())
+ prune_thread_->Stop();
+ if (upload_thread_enabled_ && upload_thread_->is_running())
+ upload_thread_->Stop();
}
}
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.h b/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.h
index 096e477..f551b90 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.h
+++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.h
@@ -21,6 +21,7 @@
#include <vector>
#include "base/files/file_path.h"
+#include "base/synchronization/lock.h"
#include "client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h"
#include "handler/crash_report_upload_thread.h"
#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h"
@@ -202,6 +203,9 @@
IOSIntermediateDumpWriter* writer_;
};
+ //! \brief Manage the prune and upload thread when the active state changes.
+ void UpdatePruneAndUploadThreads(bool active);
+
//! \brief Writes a minidump to the Crashpad database from the
//! \a process_snapshot, and triggers the upload_thread_ if started.
void SaveSnapshot(ProcessSnapshotIOSIntermediateDump& process_snapshot);
@@ -230,7 +234,9 @@
// in DumpExceptionFromMachException after aquiring the cached_writer_.
void (*mach_exception_callback_for_testing_)() = nullptr;
- bool upload_thread_started_ = false;
+ // Used to synchronize access to UpdatePruneAndUploadThreads().
+ base::Lock prune_and_upload_lock_;
+ std::atomic_bool upload_thread_enabled_ = false;
std::map<std::string, std::string> annotations_;
base::FilePath base_dir_;
std::string cached_writer_path_;
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc
index 54af2c7..e61a604 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc
+++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc
@@ -104,12 +104,18 @@
EXPECT_STREQ(system->CPUVendor().c_str(), "GenuineIntel");
#elif defined(ARCH_CPU_ARM64)
EXPECT_EQ(system->GetCPUArchitecture(), kCPUArchitectureARM64);
- utsname uts;
- ASSERT_EQ(uname(&uts), 0);
- EXPECT_STREQ(system->MachineDescription().c_str(), uts.machine);
#else
#error Port to your CPU architecture
#endif
+#if TARGET_OS_SIMULATOR
+ EXPECT_EQ(system->MachineDescription().substr(0, 13),
+ std::string("iOS Simulator"));
+#elif TARGET_OS_IPHONE
+ utsname uts;
+ ASSERT_EQ(uname(&uts), 0);
+ EXPECT_STREQ(system->MachineDescription().c_str(), uts.machine);
+#endif
+
EXPECT_EQ(system->GetOperatingSystem(), SystemSnapshot::kOperatingSystemIOS);
}
diff --git a/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc b/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc
index 1b7977cf..651b3907 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc
+++ b/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc
@@ -28,6 +28,9 @@
// The file extension used to indicate a file is locked.
constexpr char kLockedExtension[] = ".locked";
+// Prune onces a day.
+constexpr time_t prune_interval = 60 * 60 * 24;
+
// If the client finds a locked file matching it's own bundle id, unlock it
// after 24 hours.
constexpr time_t matching_bundle_locked_ttl = 60 * 60 * 24;
@@ -96,11 +99,12 @@
base::FilePath pending_path,
std::string bundle_identifier_and_seperator,
bool is_extension)
- : thread_(60 * 60 * 24, this),
+ : thread_(prune_interval, this),
condition_(std::move(condition)),
pending_path_(pending_path),
bundle_identifier_and_seperator_(bundle_identifier_and_seperator),
initial_work_delay_(is_extension ? extension_delay : app_delay),
+ last_start_time_(0),
database_(database) {}
PruneIntermediateDumpsAndCrashReportsThread::
@@ -116,9 +120,25 @@
void PruneIntermediateDumpsAndCrashReportsThread::DoWork(
const WorkerThread* thread) {
+ // This thread may be stopped and started a number of times throughout the
+ // lifetime of the process to prevent 0xdead10cc kills (see
+ // crbug.com/crashpad/400), but it should only run once per prune_interval
+ // after initial_work_delay_.
+ time_t now = time(nullptr);
+ if (now - last_start_time_ < prune_interval)
+ return;
+ last_start_time_ = now;
+
internal::ScopedBackgroundTask scoper("PruneThread");
database_->CleanDatabase(60 * 60 * 24 * 3);
+
+ // Here and below, respect Stop() being called after each task.
+ if (!thread_.is_running())
+ return;
PruneCrashReportDatabase(database_, condition_.get());
+
+ if (!thread_.is_running())
+ return;
if (!clean_old_intermediate_dumps_) {
clean_old_intermediate_dumps_ = true;
UnlockOldIntermediateDumps(pending_path_, bundle_identifier_and_seperator_);
diff --git a/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h b/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h
index b276423..cc40980 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h
+++ b/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h
@@ -84,6 +84,9 @@
//! It is expected to only be called from the same thread that called Start().
void Stop() override;
+ //! \return `true` if the thread is running, `false` if it is not.
+ bool is_running() const { return thread_.is_running(); }
+
private:
// WorkerThread::Delegate:
void DoWork(const WorkerThread* thread) override;
@@ -94,6 +97,7 @@
std::string bundle_identifier_and_seperator_;
bool clean_old_intermediate_dumps_;
double initial_work_delay_;
+ time_t last_start_time_;
CrashReportDatabase* database_; // weak
};
diff --git a/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h
index 70f1628..2af958d 100644
--- a/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h
+++ b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h
@@ -108,6 +108,9 @@
//! It is expected to only be called from the same thread that called Start().
void Stop() override;
+ //! \return `true` if the thread is running, `false` if it is not.
+ bool is_running() const { return thread_.is_running(); }
+
private:
//! \brief The result code from UploadReport().
enum class UploadResult {
diff --git a/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.h b/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.h
index 035f9d8..916a674 100644
--- a/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.h
+++ b/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.h
@@ -17,6 +17,7 @@
#import <CoreFoundation/CoreFoundation.h>
+#include <functional>
#include <string>
namespace crashpad {
@@ -41,28 +42,25 @@
int DaylightOffsetSeconds() const { return daylight_offset_seconds_; }
const std::string& StandardName() const { return standard_name_; }
const std::string& DaylightName() const { return daylight_name_; }
+ bool IsApplicationActive() const { return active_; }
// Currently unused by minidump.
int Orientation() const { return orientation_; }
- private:
- // Notification handlers.
- void InstallHandlers();
- static void SystemTimeZoneDidChangeNotificationHandler(
- CFNotificationCenterRef center,
- void* observer,
- CFStringRef name,
- const void* object,
- CFDictionaryRef userInfo);
- void SystemTimeZoneDidChangeNotification();
+ // A completion callback that takes a bool indicating that the application has
+ // become active or inactive.
+ using ActiveApplicationCallback = std::function<void(bool)>;
- static void OrientationDidChangeNotificationHandler(
- CFNotificationCenterRef center,
- void* observer,
- CFStringRef name,
- const void* object,
- CFDictionaryRef userInfo);
+ void SetActiveApplicationCallback(ActiveApplicationCallback callback) {
+ active_application_callback_ = callback;
+ }
+
+ private:
+ // Notification handlers for time zone, orientation and active state.
+ void InstallHandlers();
+ void SystemTimeZoneDidChangeNotification();
void OrientationDidChangeNotification();
+ void ApplicationDidChangeActiveNotification();
int major_version_;
int minor_version_;
@@ -72,6 +70,7 @@
bool is_extension_;
std::string machine_description_;
int orientation_;
+ bool active_;
int processor_count_;
std::string cpu_vendor_;
bool has_next_daylight_saving_time_;
@@ -80,6 +79,7 @@
int daylight_offset_seconds_;
std::string standard_name_;
std::string daylight_name_;
+ ActiveApplicationCallback active_application_callback_;
};
} // namespace internal
diff --git a/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.mm b/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.mm
index 366be46..6d39b6d2 100644
--- a/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.mm
+++ b/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.mm
@@ -49,6 +49,24 @@
return value;
}
+template <typename T, void (T::*M)(void)>
+void AddObserver(CFStringRef notification_name, T* observer) {
+ CFNotificationCenterAddObserver(
+ CFNotificationCenterGetLocalCenter(),
+ observer,
+ [](CFNotificationCenterRef center,
+ void* observer_vp,
+ CFNotificationName name,
+ const void* object,
+ CFDictionaryRef userInfo) {
+ T* observer = reinterpret_cast<T*>(observer_vp);
+ (observer->*M)();
+ },
+ notification_name,
+ nullptr,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+}
+
} // namespace
namespace crashpad {
@@ -131,35 +149,30 @@
void IOSSystemDataCollector::InstallHandlers() {
// Timezone.
- CFNotificationCenterAddObserver(
- CFNotificationCenterGetLocalCenter(),
- this,
- IOSSystemDataCollector::SystemTimeZoneDidChangeNotificationHandler,
- reinterpret_cast<CFStringRef>(NSSystemTimeZoneDidChangeNotification),
- nullptr,
- CFNotificationSuspensionBehaviorDeliverImmediately);
+ AddObserver<IOSSystemDataCollector,
+ &IOSSystemDataCollector::SystemTimeZoneDidChangeNotification>(
+ (__bridge CFStringRef)NSSystemTimeZoneDidChangeNotification, this);
SystemTimeZoneDidChangeNotification();
// Orientation.
- CFNotificationCenterAddObserver(
- CFNotificationCenterGetLocalCenter(),
- this,
- IOSSystemDataCollector::OrientationDidChangeNotificationHandler,
- reinterpret_cast<CFStringRef>(UIDeviceOrientationDidChangeNotification),
- nullptr,
- CFNotificationSuspensionBehaviorDeliverImmediately);
+ AddObserver<IOSSystemDataCollector,
+ &IOSSystemDataCollector::OrientationDidChangeNotification>(
+ (__bridge CFStringRef)UIDeviceOrientationDidChangeNotification, this);
OrientationDidChangeNotification();
-}
-// static
-void IOSSystemDataCollector::SystemTimeZoneDidChangeNotificationHandler(
- CFNotificationCenterRef center,
- void* observer,
- CFStringRef name,
- const void* object,
- CFDictionaryRef userInfo) {
- static_cast<IOSSystemDataCollector*>(observer)
- ->SystemTimeZoneDidChangeNotification();
+ // Foreground/Background. Extensions shouldn't use UIApplication*.
+ if (!is_extension_) {
+ AddObserver<
+ IOSSystemDataCollector,
+ &IOSSystemDataCollector::ApplicationDidChangeActiveNotification>(
+ (__bridge CFStringRef)UIApplicationDidBecomeActiveNotification, this);
+ AddObserver<
+ IOSSystemDataCollector,
+ &IOSSystemDataCollector::ApplicationDidChangeActiveNotification>(
+ (__bridge CFStringRef)UIApplicationDidEnterBackgroundNotification,
+ this);
+ ApplicationDidChangeActiveNotification();
+ }
}
void IOSSystemDataCollector::SystemTimeZoneDidChangeNotification() {
@@ -197,21 +210,20 @@
}
}
-// static
-void IOSSystemDataCollector::OrientationDidChangeNotificationHandler(
- CFNotificationCenterRef center,
- void* observer,
- CFStringRef name,
- const void* object,
- CFDictionaryRef userInfo) {
- static_cast<IOSSystemDataCollector*>(observer)
- ->OrientationDidChangeNotification();
-}
-
void IOSSystemDataCollector::OrientationDidChangeNotification() {
orientation_ =
base::saturated_cast<int>([[UIDevice currentDevice] orientation]);
}
+void IOSSystemDataCollector::ApplicationDidChangeActiveNotification() {
+ dispatch_assert_queue_debug(dispatch_get_main_queue());
+ bool old_active = active_;
+ active_ = [UIApplication sharedApplication].applicationState ==
+ UIApplicationStateActive;
+ if (active_ != old_active && active_application_callback_) {
+ active_application_callback_(active_);
+ }
+}
+
} // namespace internal
} // namespace crashpad