From 2dbd019bc2303a0463a396b5c47b4eb16a1a1e27 Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Wed, 24 Mar 2021 18:22:41 -0400 Subject: [PATCH] ios: Add iOS crashpad overview documentation. Adds an overview of the limitations of crashpad on the iOS platform, including explanations of the in-process client and handler, and the intermediate dump format used. Bug: crashpad: 31 Change-Id: I1eb6add115570147e2de1be1002c5a4aa366184b Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2706018 Commit-Queue: Justin Cohen Reviewed-by: Mark Mentovai --- client/crashpad_client.h | 27 ++++++-- client/crashpad_client_ios.cc | 12 +++- client/crashpad_client_ios_test.mm | 5 +- client/simulate_crash_ios.h | 21 +++++- doc/ios_overview_design.md | 104 +++++++++++++++++++++++++++++ doc/overview_design.md | 25 ++++++- 6 files changed, 184 insertions(+), 10 deletions(-) create mode 100644 doc/ios_overview_design.md diff --git a/client/crashpad_client.h b/client/crashpad_client.h index cbe05516..ec6c5e56 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -485,18 +485,37 @@ class CrashpadClient { //! \brief Requests that the handler begin in-process uploading of any //! pending reports. //! + //! Once called the handler will start looking for pending reports to upload + //! on another thread. This method does not block. + //! //! A handler must have already been installed before calling this method. - void EnableUploading(); + void StartProcesingPendingReports(); - // TODO(justincohen): This method is purely for bringing up iOS interfaces. - //! \brief Requests that the handler capture a dump even though there hasn't - //! been a crash. + //! \brief Requests that the handler capture an intermediate dump even though + //! there hasn't been a crash. The intermediate dump will be converted + //! to a mindump immediately. If StartProcesingPendingReports() has been + //! called, this will also trigger an upload. + //! + //! For internal use only. Clients should use CRASHPAD_SIMULATE_CRASH(). //! //! A handler must have already been installed before calling this method. //! //! \param[in] context A NativeCPUContext, generally captured by //! CaptureContext() or similar. static void DumpWithoutCrash(NativeCPUContext* context); + + //! \brief Requests that the handler capture an intermediate dump even though + //! there hasn't been a crash. The intermediate dump will not be converted + //! to a mindump until ProcessIntermediateDumps() is called. + //! + //! For internal use only. Clients should use + //! CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING(). + //! + //! A handler must have already been installed before calling this method. + //! + //! \param[in] context A NativeCPUContext, generally captured by + //! CaptureContext() or similar. + static void DumpWithoutCrashAndDeferProcessing(NativeCPUContext* context); #endif #if defined(OS_APPLE) || DOXYGEN diff --git a/client/crashpad_client_ios.cc b/client/crashpad_client_ios.cc index 588834c3..6cbbb418 100644 --- a/client/crashpad_client_ios.cc +++ b/client/crashpad_client_ios.cc @@ -234,7 +234,7 @@ void CrashpadClient::ProcessIntermediateDumps( } // static -void CrashpadClient::EnableUploading() { +void CrashpadClient::StartProcesingPendingReports() { // TODO(justincohen): Start the CrashReportUploadThread. } @@ -243,7 +243,17 @@ void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) { CrashHandler* crash_handler = CrashHandler::Get(); DCHECK(crash_handler); crash_handler->DumpWithoutCrash(context); + // TODO(justincohen): Change this to only process the dump from above, not all + // intermediate dump files. crash_handler->ProcessIntermediateDumps(); } +// static +void CrashpadClient::DumpWithoutCrashAndDeferProcessing( + NativeCPUContext* context) { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + crash_handler->DumpWithoutCrash(context); +} + } // namespace crashpad diff --git a/client/crashpad_client_ios_test.mm b/client/crashpad_client_ios_test.mm index 5bc3946a..30fabae0 100644 --- a/client/crashpad_client_ios_test.mm +++ b/client/crashpad_client_ios_test.mm @@ -18,6 +18,7 @@ #include +#include "client/simulate_crash.h" #include "gtest/gtest.h" #include "test/scoped_temp_dir.h" #include "testing/platform_test.h" @@ -33,9 +34,7 @@ TEST_F(CrashpadIOSClient, DumpWithoutCrash) { ScopedTempDir database_dir; client.StartCrashpadInProcessHandler( base::FilePath(database_dir.path()), "", {}); - NativeCPUContext context; - CaptureContext(&context); - client.DumpWithoutCrash(&context); + CRASHPAD_SIMULATE_CRASH(); } // This test is covered by a similar XCUITest, but for development purposes it's diff --git a/client/simulate_crash_ios.h b/client/simulate_crash_ios.h index 3cd1a69a..87a94578 100644 --- a/client/simulate_crash_ios.h +++ b/client/simulate_crash_ios.h @@ -20,7 +20,11 @@ //! \file -//! \brief Captures the CPU context and captures a dump without an exception. +//! \brief Captures the CPU context and creates a minidump dump without an +//! exception. The minidump will immediately become eligible for further +//! processing, including upload. +//! +//! \sa CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING #define CRASHPAD_SIMULATE_CRASH() \ do { \ crashpad::NativeCPUContext cpu_context; \ @@ -28,4 +32,19 @@ crashpad::CrashpadClient::DumpWithoutCrash(&cpu_context); \ } while (false) +//! \brief Captures the CPU context and captures an intermediate dump without an +//! exception. Does not convert the intermediate dump into a minidump. +//! +//! Deferring processing is useful when the application may be in an unstable +//! state, such as during a hang. +//! +//! \sa CRASHPAD_SIMULATE_CRASH +#define CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING() \ + do { \ + crashpad::NativeCPUContext cpu_context; \ + crashpad::CaptureContext(&cpu_context); \ + crashpad::CrashpadClient::DumpWithoutCrashAndDeferProcessing( \ + &cpu_context); \ + } while (false) + #endif // CRASHPAD_CLIENT_SIMULATE_CRASH_IOS_H_ diff --git a/doc/ios_overview_design.md b/doc/ios_overview_design.md new file mode 100644 index 00000000..be23d09a --- /dev/null +++ b/doc/ios_overview_design.md @@ -0,0 +1,104 @@ + + +# iOS Crashpad Overview Design + +[TOC] + +## iOS Limitations + +Crashpad on other platforms captures exceptions out-of-process. The iOS sandbox, +however, restricts applications from delegating work to separate processes. +This limitation means Crashpad on iOS must combine the work of the handler and +the client into the same process as the main application. + +## The Crashpad In-Process Handler + +In-process handling comes with a number of limitations and difficulties. It is +not possible to catch the specific Mach exception `EXC_CRASH`, so certain groups +of crashes cannot be captured. This includes some major ones, like out-of-memory +crashes. This also introduces difficulties in capturing all the relevant crash +data and writing the minidump, as the process itself is in an unsafe state. + +While handling an exception, the handler may not, for example: + + - Allocate memory. + - Use libc, or most any library call. + +While handling an exception, the handler may only: + + - Use audited syscalls. + - access memory via `vm_read`. + +In conjunction with Crashpad’s existing minidump writer and structural +limitations of the minidump format, it is not possible to write a minidump +immediately from the crash handler. Instead, an intermediate dump is written +when a handler would normally write a minidump (such as during an exception or a +forced dump without crashing). The intermediate dump file will be converted to +a minidump on the next run (or when the application decides it's safe to do so). + +During Crashpad initialization, the handler gathers basic system information +and opens a pending intermediate dump adjacent to the Crashpad database. + +## The Crashpad IntermediateDump Format + +Due to the limitations of in-process handling, an intermediate dump file is +written during exceptions. The data is streamed to a file, which will be used to +generate a final minidump when appropriate. + +The file format is similar to binary JSON, supporting keyed properties, maps and +arrays. + + - `Property` [key:int, length:int, value:intarray] + - `StartMap` [key:int], followed by repeating Properties until `EndMap` + - `StartArray` [key:int], followed by repeating Maps until `EndArray` + - `EndMap`, `EndArray`, `EndDocument` + +Similar to JSON, maps can contain other maps, arrays and properties. + +## The Crashpad In-Process Client + +Other Crashpad platforms handle exceptions and upload minidumps out-of-process. +On iOS, everything must happen in-process. Once started, the client will +automatically handle exceptions and capture the crashed process state in an +intermediate dump file. Converting that intermediate dump file into a minidump +is likely not safe to do from within a crashed process, and uploading a minidump +is definitely unsafe to do at crash time. Applications are expected to process +intermediate dumps into pending minidumps and begin processing pending +minidumps, possibly for upload, at suitable times following the next application +restart. + + +### `ProcessIntermediateDumps` +For performance and stability reasons applications may choose the correct time +to convert intermediate dumps, as well as append metadata to the pending +intermediate dumps. This is expected to happen during application startup, when +suitable. After converting, a minidump will be written to the Crashpad database, +similar to how other platforms write a minidump on exception handling. If +uploading is enabled, this minidump will also be immediately uploaded. New +intermediate dumps generated by exceptions or by +`CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING` will will not be processed until +the next call to `ProcessIntermediateDumps`. Conversely, +`CRASHPAD_SIMULATE_CRASH` can be called when the client has no performance or +stability concerns. In this case, intermediate dumps are automatically +converted to minidumps and immediately eligable for uploading. + +### `StartProcesingPendingReports` +For similar reasons, applications may choose the correct time to begin uploading +pending reports, such as when ideal network conditions exist. By default, +clients start with uploading disabled. Applications should call this API when +it is determined that it is appropriate to do so (such as on a few seconds after +startup, or when network connectivity is appropriate). diff --git a/doc/overview_design.md b/doc/overview_design.md index 9d0f0edd..01dba173 100644 --- a/doc/overview_design.md +++ b/doc/overview_design.md @@ -93,7 +93,8 @@ The handler runs in a separate process from the client or clients. It is responsible for snapshotting the crashing client process’ state on a crash, saving it to a crash dump, and transmitting the crash dump to an upstream server. Clients register with the handler to allow it to capture and upload -their crashes. +their crashes. On iOS, there is no separate process for the handler. +[This is a limitation of iOS.](ios_overview_design.md#ios-limitations) ### The Crashpad handler @@ -213,6 +214,12 @@ Crashpad provides a facility for a process to disassociate (unregister) with an existing crash handler, which can be necessary when an older client spawns an updated version. +#### iOS + +iOS registers both a signal handler for `SIGABRT` and a Mach exception handler +with a subset of available exceptions. [This is a limitation of +iOS.](ios_overview_design.md#ios-limitations) + #### Windows There are two modes of registration on Windows. In both cases the handler is @@ -272,6 +279,14 @@ dispatched to the Mach port by the kernel, on macOS, exceptions can be handled entirely from the Crashpad handler without the need to run any code in the crash process at the time of the exception. +#### iOS + +On iOS, the operating system will notify the handler of crashes via the Mach +exception port or the signal handler. As exceptions are handled in-process, an +intermediate dump file is generated rather than a minidump. See more +information about the [iOS in-process +handler.](ios_overview_design.md#ios-in-process-handler) + #### Windows On Windows, the OS dispatches exceptions in the context of the crashing thread. @@ -415,6 +430,14 @@ details of how these properties are stored vary between platforms. The macOS implementation simply stores database properties on the minidump files in filesystem extended attributes. +#### iOS + +The iOS implementation also stores database properties of minidump files in +filesystem extended attributes. Separate from the database, iOS also stores its +intermediate dump files adjacent to the database. See more information about +[iOS intermediate +dumps.](ios_overview_design.md#the-crashpad-intermediatedump-format) + #### Windows The Windows implementation stores database properties in a binary file named