crashpad/util/ios/ios_system_data_collector.mm
Justin Cohen 30b2f4ba38 ios: Add crashpad_uptime_ns crash key to iOS reports.
This CL introduces a new crash key 'crashpad_uptime_ns' that records the
number of nanoseconds between when Crashpad was initialized and when a
snapshot is generated.

Crashpad minidumps record the MDRawMiscInfo process_create_time using a
sysctl(KERN_PROC).kp_proc.p_starttime. This time is used to display the
'uptime' of a process.  However, iOS 15 and later has a feature that
'prewarms' the app to reduce the amount of time the user waits before
the app is usable. This mean crashes that may happen immediately on
startup would appear to happen minutes or hours after process creation
time.

While initial implementations of prewarming would include some parts of
main, since iOS16 prewarming is complete before main, and therefore
before Crashpad is typically initialized.

Bug: crashpad:472
Change-Id: Iff960e37ae40121bd5927d319a2767d1cafce846
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/5171091
Reviewed-by: Ben Hamilton <benhamilton@google.com>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Justin Cohen <justincohen@chromium.org>
2024-01-11 16:42:54 +00:00

242 lines
8.0 KiB
Plaintext

// Copyright 2020 The Crashpad Authors
//
// 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/ios/ios_system_data_collector.h"
#include <sys/sysctl.h>
#include <sys/utsname.h>
#import <Foundation/Foundation.h>
#include <TargetConditionals.h>
#import <UIKit/UIKit.h>
#include "base/apple/mach_logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "build/build_config.h"
#include "util/misc/clock.h"
namespace {
std::string ReadStringSysctlByName(const char* name) {
size_t buf_len;
if (sysctlbyname(name, nullptr, &buf_len, nullptr, 0) != 0) {
PLOG(WARNING) << "sysctlbyname (size) " << name;
return std::string();
}
if (buf_len == 0) {
return std::string();
}
std::string value(buf_len - 1, '\0');
if (sysctlbyname(name, &value[0], &buf_len, nullptr, 0) != 0) {
PLOG(WARNING) << "sysctlbyname " << name;
return std::string();
}
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 {
namespace internal {
IOSSystemDataCollector::IOSSystemDataCollector()
: major_version_(0),
minor_version_(0),
patch_version_(0),
build_(),
machine_description_(),
orientation_(0),
processor_count_(0),
cpu_vendor_(),
has_next_daylight_saving_time_(false),
is_daylight_saving_time_(false),
standard_offset_seconds_(0),
daylight_offset_seconds_(0),
standard_name_(),
daylight_name_(),
initialization_time_ns_(ClockMonotonicNanoseconds()) {
NSOperatingSystemVersion version =
[[NSProcessInfo processInfo] operatingSystemVersion];
major_version_ = base::saturated_cast<int>(version.majorVersion);
minor_version_ = base::saturated_cast<int>(version.minorVersion);
patch_version_ = base::saturated_cast<int>(version.patchVersion);
processor_count_ =
base::saturated_cast<int>([[NSProcessInfo processInfo] processorCount]);
build_ = ReadStringSysctlByName("kern.osversion");
bundle_identifier_ =
base::SysNSStringToUTF8([[NSBundle mainBundle] bundleIdentifier]);
is_extension_ = [[NSBundle mainBundle].bundlePath hasSuffix:@"appex"];
#if defined(ARCH_CPU_X86_64)
cpu_vendor_ = ReadStringSysctlByName("machdep.cpu.vendor");
#endif
uint32_t addressable_bits = 0;
size_t len = sizeof(uint32_t);
// `machdep.virtual_address_size` is the number of addressable bits in
// userspace virtual addresses
if (sysctlbyname(
"machdep.virtual_address_size", &addressable_bits, &len, NULL, 0) !=
0) {
addressable_bits = 0;
}
address_mask_ = ~((1UL << addressable_bits) - 1);
#if TARGET_OS_SIMULATOR
// TODO(justincohen): Consider adding board and model information to
// |machine_description| as well (similar to MacModelAndBoard in
// util/mac/mac_util.cc).
const char* model = getenv("SIMULATOR_MODEL_IDENTIFIER");
if (model == nullptr) {
switch ([[UIDevice currentDevice] userInterfaceIdiom]) {
case UIUserInterfaceIdiomPhone:
model = "iPhone";
break;
case UIUserInterfaceIdiomPad:
model = "iPad";
break;
default:
model = "Unknown";
break;
}
}
machine_description_ = base::StringPrintf("iOS Simulator (%s)", model);
#elif TARGET_OS_IPHONE
utsname uts;
if (uname(&uts) == 0) {
machine_description_ = uts.machine;
}
#else
#error "Unexpected target type OS."
#endif
InstallHandlers();
}
IOSSystemDataCollector::~IOSSystemDataCollector() {
CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetLocalCenter(),
this);
}
void IOSSystemDataCollector::OSVersion(int* major,
int* minor,
int* bugfix) const {
*major = major_version_;
*minor = minor_version_;
*bugfix = patch_version_;
}
void IOSSystemDataCollector::InstallHandlers() {
// Timezone.
AddObserver<IOSSystemDataCollector,
&IOSSystemDataCollector::SystemTimeZoneDidChangeNotification>(
(__bridge CFStringRef)NSSystemTimeZoneDidChangeNotification, this);
SystemTimeZoneDidChangeNotification();
// Orientation.
AddObserver<IOSSystemDataCollector,
&IOSSystemDataCollector::OrientationDidChangeNotification>(
(__bridge CFStringRef)UIDeviceOrientationDidChangeNotification, this);
OrientationDidChangeNotification();
// 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() {
NSTimeZone* time_zone = NSTimeZone.localTimeZone;
NSDate* transition =
[time_zone nextDaylightSavingTimeTransitionAfterDate:[NSDate date]];
if (transition == nil) {
has_next_daylight_saving_time_ = false;
is_daylight_saving_time_ = false;
standard_offset_seconds_ =
base::saturated_cast<int>([time_zone secondsFromGMTForDate:transition]);
standard_name_ = base::SysNSStringToUTF8([time_zone abbreviation]);
daylight_offset_seconds_ = standard_offset_seconds_;
daylight_name_ = standard_name_;
} else {
has_next_daylight_saving_time_ = true;
is_daylight_saving_time_ = time_zone.isDaylightSavingTime;
if (time_zone.isDaylightSavingTime) {
standard_offset_seconds_ = base::saturated_cast<int>(
[time_zone secondsFromGMTForDate:transition]);
standard_name_ =
base::SysNSStringToUTF8([time_zone abbreviationForDate:transition]);
daylight_offset_seconds_ =
base::saturated_cast<int>([time_zone secondsFromGMT]);
daylight_name_ = base::SysNSStringToUTF8([time_zone abbreviation]);
} else {
standard_offset_seconds_ =
base::saturated_cast<int>([time_zone secondsFromGMT]);
standard_name_ = base::SysNSStringToUTF8([time_zone abbreviation]);
daylight_offset_seconds_ = base::saturated_cast<int>(
[time_zone secondsFromGMTForDate:transition]);
daylight_name_ =
base::SysNSStringToUTF8([time_zone abbreviationForDate:transition]);
}
}
}
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