2022-09-06 19:14:07 -04:00
|
|
|
// Copyright 2021 The Crashpad Authors
|
2021-10-14 10:55:03 -04:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
#include <mach/mach.h>
|
2021-10-14 10:55:03 -04:00
|
|
|
#include <stdint.h>
|
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
#include <atomic>
|
2022-09-09 15:25:08 -06:00
|
|
|
#include <functional>
|
2021-10-14 10:55:03 -04:00
|
|
|
#include <map>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "base/files/file_path.h"
|
2022-03-22 22:37:00 -04:00
|
|
|
#include "base/synchronization/lock.h"
|
2021-10-25 11:35:19 -04:00
|
|
|
#include "client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h"
|
2022-09-15 13:14:19 -06:00
|
|
|
#include "client/upload_behavior_ios.h"
|
2021-10-14 14:10:22 -04:00
|
|
|
#include "handler/crash_report_upload_thread.h"
|
2021-10-14 10:55:03 -04:00
|
|
|
#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h"
|
|
|
|
#include "util/ios/ios_intermediate_dump_writer.h"
|
|
|
|
#include "util/ios/ios_system_data_collector.h"
|
2022-03-08 18:20:41 -05:00
|
|
|
#include "util/misc/capture_context.h"
|
2021-10-14 10:55:03 -04:00
|
|
|
#include "util/misc/initialization_state_dcheck.h"
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
//! \brief Manage intermediate minidump generation, and own the crash report
|
|
|
|
//! upload thread and database.
|
|
|
|
class InProcessHandler {
|
|
|
|
public:
|
|
|
|
InProcessHandler();
|
|
|
|
~InProcessHandler();
|
|
|
|
InProcessHandler(const InProcessHandler&) = delete;
|
|
|
|
InProcessHandler& operator=(const InProcessHandler&) = delete;
|
|
|
|
|
2022-09-09 15:25:08 -06:00
|
|
|
//! \brief Observation callback invoked each time this object finishes
|
|
|
|
//! processing and attempting to upload on-disk crash reports (whether or
|
|
|
|
//! not the uploads succeeded).
|
|
|
|
//!
|
|
|
|
//! This callback is copied into this object. Any references or pointers
|
|
|
|
//! inside must outlive this object.
|
|
|
|
//!
|
|
|
|
//! The callback might be invoked on a background thread, so clients must
|
|
|
|
//! synchronize appropriately.
|
|
|
|
using ProcessPendingReportsObservationCallback = std::function<void()>;
|
|
|
|
|
2021-10-14 10:55:03 -04:00
|
|
|
//! \brief Initializes the in-process handler.
|
|
|
|
//!
|
|
|
|
//! This method must be called only once, and must be successfully called
|
|
|
|
//! before any other method in this class may be called.
|
|
|
|
//!
|
|
|
|
//! \param[in] database The path to a Crashpad database.
|
|
|
|
//! \param[in] url The URL of an upload server.
|
|
|
|
//! \param[in] annotations Process annotations to set in each crash report.
|
2022-09-09 15:25:08 -06:00
|
|
|
//! \param[in] callback Optional callback invoked zero or more times
|
|
|
|
//! on a background thread each time this object finishes
|
|
|
|
//! processing and attempting to upload on-disk crash reports.
|
2021-10-14 10:55:03 -04:00
|
|
|
//! \return `true` if a handler to a pending intermediate dump could be
|
|
|
|
//! opened.
|
|
|
|
bool Initialize(const base::FilePath& database,
|
|
|
|
const std::string& url,
|
2022-09-09 15:25:08 -06:00
|
|
|
const std::map<std::string, std::string>& annotations,
|
|
|
|
ProcessPendingReportsObservationCallback callback =
|
|
|
|
ProcessPendingReportsObservationCallback());
|
2021-10-14 10:55:03 -04:00
|
|
|
|
|
|
|
//! \brief Generate an intermediate dump from a signal handler exception.
|
2022-03-08 18:20:41 -05:00
|
|
|
//! Writes the dump with the cached writer does not allow concurrent
|
|
|
|
//! exceptions to be written. It is expected the system will terminate
|
|
|
|
//! the application after this call.
|
2021-10-14 10:55:03 -04:00
|
|
|
//!
|
|
|
|
//! \param[in] siginfo A pointer to a `siginfo_t` object received by a signal
|
|
|
|
//! handler.
|
|
|
|
//! \param[in] context A pointer to a `ucontext_t` object received by a
|
|
|
|
//! signal.
|
2022-03-10 14:12:57 -05:00
|
|
|
void DumpExceptionFromSignal(siginfo_t* siginfo, ucontext_t* context);
|
2021-10-14 10:55:03 -04:00
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
//! \brief Generate an intermediate dump from a mach exception. Writes the
|
|
|
|
//! dump with the cached writer does not allow concurrent exceptions to be
|
|
|
|
//! written. It is expected the system will terminate the application
|
|
|
|
//! after this call.
|
2021-10-14 10:55:03 -04:00
|
|
|
//!
|
|
|
|
//! \param[in] behavior
|
|
|
|
//! \param[in] thread
|
|
|
|
//! \param[in] exception
|
|
|
|
//! \param[in] code
|
|
|
|
//! \param[in] code_count
|
|
|
|
//! \param[in,out] flavor
|
|
|
|
//! \param[in] old_state
|
|
|
|
//! \param[in] old_state_count
|
2022-03-10 14:12:57 -05:00
|
|
|
void DumpExceptionFromMachException(exception_behavior_t behavior,
|
2021-10-14 10:55:03 -04:00
|
|
|
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);
|
|
|
|
|
|
|
|
//! \brief Generate an intermediate dump from an uncaught NSException.
|
|
|
|
//!
|
|
|
|
//! When the ObjcExceptionPreprocessor does not detect an NSException as it is
|
|
|
|
//! thrown, the last-chance uncaught exception handler passes a list of call
|
|
|
|
//! stack frame addresses. Record them in the intermediate dump so a minidump
|
2022-03-08 18:20:41 -05:00
|
|
|
//! with a 'fake' call stack is generated. Writes the dump with the cached
|
|
|
|
//! writer does not allow concurrent exceptions to be written. It is expected
|
|
|
|
//! the system will terminate the application after this call.
|
|
|
|
|
2021-10-14 10:55:03 -04:00
|
|
|
//!
|
|
|
|
//! \param[in] frames An array of call stack frame addresses.
|
|
|
|
//! \param[in] num_frames The number of frames in |frames|.
|
2022-03-10 14:12:57 -05:00
|
|
|
void DumpExceptionFromNSExceptionWithFrames(const uint64_t* frames,
|
|
|
|
const size_t num_frames);
|
2021-10-14 10:55:03 -04:00
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
//! \brief Generate a simulated intermediate dump similar to a Mach exception
|
|
|
|
//! in the same base directory as other exceptions. Does not use the
|
|
|
|
//! cached writer.
|
|
|
|
//!
|
|
|
|
//! \param[in] context A pointer to a NativeCPUContext object for this
|
|
|
|
//! simulated exception.
|
2022-03-24 19:43:01 -04:00
|
|
|
//! \param[in] exception
|
2022-03-08 18:20:41 -05:00
|
|
|
//! \param[out] path The path of the intermediate dump generated.
|
|
|
|
//! \return `true` if the pending intermediate dump could be written.
|
2022-03-10 14:12:57 -05:00
|
|
|
bool DumpExceptionFromSimulatedMachException(const NativeCPUContext* context,
|
2022-03-24 19:43:01 -04:00
|
|
|
exception_type_t exception,
|
2022-03-10 14:12:57 -05:00
|
|
|
base::FilePath* path);
|
2022-03-08 18:20:41 -05:00
|
|
|
|
|
|
|
//! \brief Generate a simulated intermediate dump similar to a Mach exception
|
|
|
|
//! at a specific path. Does not use the cached writer.
|
|
|
|
//!
|
|
|
|
//! \param[in] context A pointer to a NativeCPUContext object for this
|
|
|
|
//! simulated exception.
|
2022-03-24 19:43:01 -04:00
|
|
|
//! \param[in] exception
|
2022-03-08 18:20:41 -05:00
|
|
|
//! \param[in] path Path to where the intermediate dump should be written.
|
|
|
|
//! \return `true` if the pending intermediate dump could be written.
|
|
|
|
bool DumpExceptionFromSimulatedMachExceptionAtPath(
|
|
|
|
const NativeCPUContext* context,
|
2022-03-24 19:43:01 -04:00
|
|
|
exception_type_t exception,
|
2022-03-08 18:20:41 -05:00
|
|
|
const base::FilePath& path);
|
|
|
|
|
2022-03-24 19:43:01 -04:00
|
|
|
//! \brief Moves an intermediate dump to the pending directory. This is meant
|
|
|
|
//! to be used by the UncaughtExceptionHandler, when NSException caught
|
|
|
|
//! by the preprocessor matches the UncaughtExceptionHandler.
|
|
|
|
//!
|
|
|
|
//! \param[in] path Path to the specific intermediate dump.
|
|
|
|
bool MoveIntermediateDumpAtPathToPending(const base::FilePath& path);
|
|
|
|
|
2021-10-14 10:55:03 -04:00
|
|
|
//! \brief Requests that the handler convert all intermediate dumps into
|
|
|
|
//! minidumps and trigger an upload if possible.
|
|
|
|
//!
|
|
|
|
//! \param[in] annotations Process annotations to set in each crash report.
|
|
|
|
void ProcessIntermediateDumps(
|
|
|
|
const std::map<std::string, std::string>& annotations);
|
|
|
|
|
|
|
|
//! \brief Requests that the handler convert a specific intermediate dump into
|
|
|
|
//! a minidump and trigger an upload if possible.
|
|
|
|
//!
|
|
|
|
//! \param[in] path Path to the specific intermediate dump.
|
|
|
|
//! \param[in] annotations Process annotations to set in each crash report.
|
|
|
|
void ProcessIntermediateDump(
|
|
|
|
const base::FilePath& path,
|
|
|
|
const std::map<std::string, std::string>& annotations = {});
|
|
|
|
|
|
|
|
//! \brief Requests that the handler begin in-process uploading of any
|
|
|
|
//! pending reports.
|
2022-09-15 13:14:19 -06:00
|
|
|
//!
|
|
|
|
//! \param[in] upload_behavior Controls when the upload thread will run and
|
|
|
|
//! process pending reports. By default, only uploads pending reports
|
|
|
|
//! when the application is active.
|
|
|
|
void StartProcessingPendingReports(
|
|
|
|
UploadBehavior upload_behavior = UploadBehavior::kUploadWhenAppIsActive);
|
2021-10-14 10:55:03 -04:00
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
//! \brief Inject a callback into Mach handling. Intended to be used by
|
|
|
|
//! tests to trigger a reentrant exception.
|
|
|
|
void SetMachExceptionCallbackForTesting(void (*callback)()) {
|
|
|
|
mach_exception_callback_for_testing_ = callback;
|
|
|
|
}
|
2021-10-14 10:55:03 -04:00
|
|
|
|
|
|
|
private:
|
|
|
|
//! \brief Helper to start and end intermediate reports.
|
|
|
|
class ScopedReport {
|
|
|
|
public:
|
|
|
|
ScopedReport(IOSIntermediateDumpWriter* writer,
|
|
|
|
const IOSSystemDataCollector& system_data,
|
2021-11-10 13:04:10 -05:00
|
|
|
const std::map<std::string, std::string>& annotations,
|
2021-10-14 10:55:03 -04:00
|
|
|
const uint64_t* frames = nullptr,
|
|
|
|
const size_t num_frames = 0);
|
2021-11-22 15:18:15 -05:00
|
|
|
~ScopedReport();
|
2021-10-14 10:55:03 -04:00
|
|
|
ScopedReport(const ScopedReport&) = delete;
|
|
|
|
ScopedReport& operator=(const ScopedReport&) = delete;
|
|
|
|
|
|
|
|
private:
|
2021-11-22 15:18:15 -05:00
|
|
|
IOSIntermediateDumpWriter* writer_;
|
|
|
|
const uint64_t* frames_;
|
|
|
|
const size_t num_frames_;
|
2021-10-14 10:55:03 -04:00
|
|
|
IOSIntermediateDumpWriter::ScopedRootMap rootMap_;
|
|
|
|
};
|
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
//! \brief Helper to manage closing the intermediate dump writer and unlocking
|
|
|
|
//! the dump file (renaming the file) after the report is written.
|
|
|
|
class ScopedLockedWriter {
|
|
|
|
public:
|
|
|
|
ScopedLockedWriter(IOSIntermediateDumpWriter* writer,
|
|
|
|
const char* writer_path,
|
|
|
|
const char* writer_unlocked_path);
|
2021-10-14 10:55:03 -04:00
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
//! \brief Close the writer_ and rename to the file with path without the
|
|
|
|
//! .locked extension.
|
|
|
|
~ScopedLockedWriter();
|
2021-10-14 10:55:03 -04:00
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
ScopedLockedWriter(const ScopedLockedWriter&) = delete;
|
|
|
|
ScopedLockedWriter& operator=(const ScopedLockedWriter&) = delete;
|
|
|
|
|
|
|
|
IOSIntermediateDumpWriter* GetWriter() { return writer_; }
|
2021-10-14 10:55:03 -04:00
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
private:
|
|
|
|
const char* writer_path_;
|
|
|
|
const char* writer_unlocked_path_;
|
|
|
|
IOSIntermediateDumpWriter* writer_;
|
|
|
|
};
|
|
|
|
|
2022-03-22 22:37:00 -04:00
|
|
|
//! \brief Manage the prune and upload thread when the active state changes.
|
2022-09-15 13:14:19 -06:00
|
|
|
//!
|
|
|
|
//! \param[in] active `true` if the application is actively running in the
|
|
|
|
//! foreground, `false` otherwise.
|
|
|
|
//! \param[in] upload_behavior Controls when the upload thread will run and
|
|
|
|
//! process pending reports.
|
|
|
|
void UpdatePruneAndUploadThreads(bool active, UploadBehavior upload_behavior);
|
2022-03-22 22:37:00 -04:00
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
//! \brief Writes a minidump to the Crashpad database from the
|
|
|
|
//! \a process_snapshot, and triggers the upload_thread_ if started.
|
2021-10-14 10:55:03 -04:00
|
|
|
void SaveSnapshot(ProcessSnapshotIOSIntermediateDump& process_snapshot);
|
2021-11-11 06:32:46 -05:00
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
//! \brief Process a maximum of 20 pending intermediate dumps. Dumps named
|
|
|
|
//! with our bundle id get first priority to prevent spamming.
|
2021-10-14 10:55:03 -04:00
|
|
|
std::vector<base::FilePath> PendingFiles();
|
2021-11-11 06:32:46 -05:00
|
|
|
|
2022-03-08 18:20:41 -05:00
|
|
|
//! \brief Lock access to the cached intermediate dump writer from
|
|
|
|
//! concurrent signal, Mach exception and uncaught NSExceptions so that
|
|
|
|
//! the first exception wins. If the same thread triggers another
|
|
|
|
//! reentrant exception, ignore it. If a different thread triggers a
|
|
|
|
//! concurrent exception, sleep indefinitely.
|
|
|
|
IOSIntermediateDumpWriter* GetCachedWriter();
|
|
|
|
|
|
|
|
//! \brief Open a new intermediate dump writer from \a writer_path.
|
|
|
|
std::unique_ptr<IOSIntermediateDumpWriter> CreateWriterWithPath(
|
|
|
|
const base::FilePath& writer_path);
|
|
|
|
|
|
|
|
//! \brief Generates a new file path to be used by an intermediate dump
|
|
|
|
//! writer built from base_dir_,, bundle_identifier_and_seperator_, a new
|
|
|
|
//! UUID, with a .locked extension.
|
|
|
|
const base::FilePath NewLockedFilePath();
|
|
|
|
|
|
|
|
// Intended to be used by tests triggering a reentrant exception. Called
|
|
|
|
// in DumpExceptionFromMachException after aquiring the cached_writer_.
|
|
|
|
void (*mach_exception_callback_for_testing_)() = nullptr;
|
2021-10-14 10:55:03 -04:00
|
|
|
|
2022-03-22 22:37:00 -04:00
|
|
|
// Used to synchronize access to UpdatePruneAndUploadThreads().
|
|
|
|
base::Lock prune_and_upload_lock_;
|
|
|
|
std::atomic_bool upload_thread_enabled_ = false;
|
2021-10-14 10:55:03 -04:00
|
|
|
std::map<std::string, std::string> annotations_;
|
|
|
|
base::FilePath base_dir_;
|
2022-03-08 18:20:41 -05:00
|
|
|
std::string cached_writer_path_;
|
|
|
|
std::string cached_writer_unlocked_path_;
|
|
|
|
std::unique_ptr<IOSIntermediateDumpWriter> cached_writer_;
|
|
|
|
std::atomic<uint64_t> exception_thread_id_ = 0;
|
2021-10-14 10:55:03 -04:00
|
|
|
std::unique_ptr<CrashReportUploadThread> upload_thread_;
|
2021-10-25 11:35:19 -04:00
|
|
|
std::unique_ptr<PruneIntermediateDumpsAndCrashReportsThread> prune_thread_;
|
2021-10-14 10:55:03 -04:00
|
|
|
std::unique_ptr<CrashReportDatabase> database_;
|
2021-10-19 13:53:37 -04:00
|
|
|
std::string bundle_identifier_and_seperator_;
|
2022-03-10 14:12:57 -05:00
|
|
|
IOSSystemDataCollector system_data_;
|
2021-10-14 10:55:03 -04:00
|
|
|
InitializationStateDcheck initialized_;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace crashpad
|