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>
diff --git a/client/ios_handler/in_process_handler.cc b/client/ios_handler/in_process_handler.cc
index 668acc6..90fec76 100644
--- a/client/ios_handler/in_process_handler.cc
+++ b/client/ios_handler/in_process_handler.cc
@@ -468,9 +468,12 @@
num_frames_(num_frames),
rootMap_(writer) {
DCHECK(writer);
+ // Grab the report creation time before writing the report.
+ uint64_t report_time_nanos = ClockMonotonicNanoseconds();
InProcessIntermediateDumpHandler::WriteHeader(writer);
InProcessIntermediateDumpHandler::WriteProcessInfo(writer, annotations);
- InProcessIntermediateDumpHandler::WriteSystemInfo(writer, system_data);
+ InProcessIntermediateDumpHandler::WriteSystemInfo(
+ writer, system_data, report_time_nanos);
}
InProcessHandler::ScopedReport::~ScopedReport() {
diff --git a/client/ios_handler/in_process_intermediate_dump_handler.cc b/client/ios_handler/in_process_intermediate_dump_handler.cc
index 1e46ddb..e91cc02 100644
--- a/client/ios_handler/in_process_intermediate_dump_handler.cc
+++ b/client/ios_handler/in_process_intermediate_dump_handler.cc
@@ -616,7 +616,8 @@
// static
void InProcessIntermediateDumpHandler::WriteSystemInfo(
IOSIntermediateDumpWriter* writer,
- const IOSSystemDataCollector& system_data) {
+ const IOSSystemDataCollector& system_data,
+ uint64_t report_time_nanos) {
IOSIntermediateDumpWriter::ScopedMap system_map(
writer, IntermediateDumpKey::kSystemInfo);
@@ -702,6 +703,11 @@
} else {
CRASHPAD_RAW_LOG("host_statistics");
}
+
+ uint64_t crashpad_uptime_nanos =
+ report_time_nanos - system_data.InitializationTime();
+ WriteProperty(
+ writer, IntermediateDumpKey::kCrashpadUptime, &crashpad_uptime_nanos);
}
// static
diff --git a/client/ios_handler/in_process_intermediate_dump_handler.h b/client/ios_handler/in_process_intermediate_dump_handler.h
index 83ebff2..1cf9180 100644
--- a/client/ios_handler/in_process_intermediate_dump_handler.h
+++ b/client/ios_handler/in_process_intermediate_dump_handler.h
@@ -56,8 +56,12 @@
//! \brief Write SystemSnapshot data to the intermediate dump.
//!
//! \param[in] writer The dump writer
+ //! \param[in] system_data An object containing various system data points.
+ //! \param[in] report_time Report creation time in nanoseconds as returned by
+ //! ClockMonotonicNanoseconds().
static void WriteSystemInfo(IOSIntermediateDumpWriter* writer,
- const IOSSystemDataCollector& system_data);
+ const IOSSystemDataCollector& system_data,
+ uint64_t report_time_nanos);
//! \brief Write ThreadSnapshot data to the intermediate dump.
//!
diff --git a/client/ios_handler/in_process_intermediate_dump_handler_test.cc b/client/ios_handler/in_process_intermediate_dump_handler_test.cc
index 3b007fc..90f76d5 100644
--- a/client/ios_handler/in_process_intermediate_dump_handler_test.cc
+++ b/client/ios_handler/in_process_intermediate_dump_handler_test.cc
@@ -61,8 +61,8 @@
InProcessIntermediateDumpHandler::WriteHeader(writer_.get());
InProcessIntermediateDumpHandler::WriteProcessInfo(
writer_.get(), {{"before_dump", "pre"}});
- InProcessIntermediateDumpHandler::WriteSystemInfo(writer_.get(),
- system_data_);
+ InProcessIntermediateDumpHandler::WriteSystemInfo(
+ writer_.get(), system_data_, ClockMonotonicNanoseconds());
InProcessIntermediateDumpHandler::WriteThreadInfo(writer_.get(), 0, 0);
InProcessIntermediateDumpHandler::WriteModuleInfo(writer_.get());
}
@@ -161,9 +161,10 @@
path(), {{"after_dump", "post"}}));
auto process_map = process_snapshot.AnnotationsSimpleMap();
- EXPECT_EQ(process_map.size(), 2u);
+ EXPECT_EQ(process_map.size(), 3u);
EXPECT_EQ(process_map["before_dump"], "pre");
EXPECT_EQ(process_map["after_dump"], "post");
+ EXPECT_TRUE(process_map.find("crashpad_uptime_ns") != process_map.end());
std::map<std::string, std::string> all_annotations_simple_map;
std::vector<AnnotationSnapshot> all_annotations;
diff --git a/snapshot/ios/process_snapshot_ios_intermediate_dump.cc b/snapshot/ios/process_snapshot_ios_intermediate_dump.cc
index c502b3c..609ea65 100644
--- a/snapshot/ios/process_snapshot_ios_intermediate_dump.cc
+++ b/snapshot/ios/process_snapshot_ios_intermediate_dump.cc
@@ -122,6 +122,9 @@
}
system_.Initialize(system_info);
+ annotations_simple_map_["crashpad_uptime_ns"] =
+ std::to_string(system_.CrashpadUptime());
+
// Threads
const IOSIntermediateDumpList* thread_list =
GetListFromMap(root_map, Key::kThreads);
diff --git a/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc b/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
index 6d64b1d..238c52e 100644
--- a/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
+++ b/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
@@ -160,6 +160,10 @@
EXPECT_TRUE(writer->AddProperty(Key::kWired, &count));
EXPECT_TRUE(writer->AddProperty(Key::kFree, &count));
}
+
+ uint64_t crashpad_report_time_nanos = 1234567890;
+ EXPECT_TRUE(
+ writer->AddProperty(Key::kCrashpadUptime, &crashpad_report_time_nanos));
}
void WriteAnnotations(IOSIntermediateDumpWriter* writer,
@@ -491,6 +495,9 @@
ExpectModules(
snapshot.Modules(), expect_module_path, expect_long_annotations);
ExpectMachException(*snapshot.Exception());
+
+ auto map = snapshot.AnnotationsSimpleMap();
+ EXPECT_EQ(map["crashpad_uptime_ns"], "1234567890");
}
void CloseWriter() { EXPECT_TRUE(writer_->Close()); }
diff --git a/snapshot/ios/system_snapshot_ios_intermediate_dump.cc b/snapshot/ios/system_snapshot_ios_intermediate_dump.cc
index 15f6699..6a9f2c1 100644
--- a/snapshot/ios/system_snapshot_ios_intermediate_dump.cc
+++ b/snapshot/ios/system_snapshot_ios_intermediate_dump.cc
@@ -119,6 +119,8 @@
}
}
+ GetDataValueFromMap(system_data, Key::kCrashpadUptime, &crashpad_uptime_ns_);
+
INITIALIZATION_STATE_SET_VALID(initialized_);
}
@@ -249,5 +251,10 @@
return address_mask_;
}
+uint64_t SystemSnapshotIOSIntermediateDump::CrashpadUptime() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return crashpad_uptime_ns_;
+}
+
} // namespace internal
} // namespace crashpad
diff --git a/snapshot/ios/system_snapshot_ios_intermediate_dump.h b/snapshot/ios/system_snapshot_ios_intermediate_dump.h
index 6cc09ac..339139b 100644
--- a/snapshot/ios/system_snapshot_ios_intermediate_dump.h
+++ b/snapshot/ios/system_snapshot_ios_intermediate_dump.h
@@ -75,6 +75,10 @@
std::string* daylight_name) const override;
uint64_t AddressMask() const override;
+ //! \brief Returns the number of nanoseconds between Crashpad initialization
+ //! and snapshot generation.
+ uint64_t CrashpadUptime() const;
+
private:
std::string os_version_build_;
std::string machine_description_;
@@ -93,6 +97,7 @@
std::string standard_name_;
std::string daylight_name_;
uint64_t address_mask_;
+ uint64_t crashpad_uptime_ns_;
InitializationStateDcheck initialized_;
};
diff --git a/util/ios/ios_intermediate_dump_format.h b/util/ios/ios_intermediate_dump_format.h
index 9fe7ccf..2f0a898 100644
--- a/util/ios/ios_intermediate_dump_format.h
+++ b/util/ios/ios_intermediate_dump_format.h
@@ -86,6 +86,7 @@
TD(kInactive, 5018) \
TD(kWired, 5019) \
TD(kAddressMask, 5020) \
+ TD(kCrashpadUptime, 5021) \
TD(kThreads, 6000) \
TD(kDebugState, 6001) \
TD(kFloatState, 6002) \
diff --git a/util/ios/ios_system_data_collector.h b/util/ios/ios_system_data_collector.h
index 2dfc373..7bef9a2 100644
--- a/util/ios/ios_system_data_collector.h
+++ b/util/ios/ios_system_data_collector.h
@@ -44,6 +44,7 @@
const std::string& DaylightName() const { return daylight_name_; }
bool IsApplicationActive() const { return active_; }
uint64_t AddressMask() const { return address_mask_; }
+ uint64_t InitializationTime() const { return initialization_time_ns_; }
// Currently unused by minidump.
int Orientation() const { return orientation_; }
@@ -82,6 +83,12 @@
std::string daylight_name_;
ActiveApplicationCallback active_application_callback_;
uint64_t address_mask_;
+
+ // Time in nanoseconds as returned by ClockMonotonicNanoseconds() to store the
+ // crashpad start time. This clock increments monotonically but pauses while
+ // the system is asleep. It should not be compared to other system time
+ // sources.
+ uint64_t initialization_time_ns_;
};
} // namespace internal
diff --git a/util/ios/ios_system_data_collector.mm b/util/ios/ios_system_data_collector.mm
index 28ede18..2ec6de5 100644
--- a/util/ios/ios_system_data_collector.mm
+++ b/util/ios/ios_system_data_collector.mm
@@ -26,6 +26,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "build/build_config.h"
+#include "util/misc/clock.h"
namespace {
@@ -86,7 +87,8 @@
standard_offset_seconds_(0),
daylight_offset_seconds_(0),
standard_name_(),
- daylight_name_() {
+ daylight_name_(),
+ initialization_time_ns_(ClockMonotonicNanoseconds()) {
NSOperatingSystemVersion version =
[[NSProcessInfo processInfo] operatingSystemVersion];
major_version_ = base::saturated_cast<int>(version.majorVersion);