ios: Create a PruneIntermediateDumpsAndCrashReportsThread.

This will prune the database on a daily basis, in accordance with the
specified condition. This will also unlock any leftover intermediate
dump files.

Bug: crashpad:31
Change-Id: I229f8b8006b44d31062fbf73bb9d316d69ab2dcf
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3231618
Commit-Queue: Justin Cohen <justincohen@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Justin Cohen 2021-10-25 11:35:19 -04:00 committed by Crashpad LUCI CQ
parent 2c45d05875
commit 2d5a30fb17
7 changed files with 244 additions and 0 deletions

View File

@ -39,6 +39,8 @@ crashpad_static_library("client") {
"ios_handler/in_process_handler.h", "ios_handler/in_process_handler.h",
"ios_handler/in_process_intermediate_dump_handler.cc", "ios_handler/in_process_intermediate_dump_handler.cc",
"ios_handler/in_process_intermediate_dump_handler.h", "ios_handler/in_process_intermediate_dump_handler.h",
"ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc",
"ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h",
"simulate_crash_ios.h", "simulate_crash_ios.h",
] ]
} }

View File

@ -19,6 +19,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "client/ios_handler/in_process_intermediate_dump_handler.h" #include "client/ios_handler/in_process_intermediate_dump_handler.h"
#include "client/prune_crash_reports.h"
#include "client/settings.h" #include "client/settings.h"
#include "minidump/minidump_file_writer.h" #include "minidump/minidump_file_writer.h"
#include "util/file/directory_reader.h" #include "util/file/directory_reader.h"
@ -85,6 +86,14 @@ bool InProcessHandler::Initialize(
base_dir_ = database.Append(kPendingSerializediOSDump); base_dir_ = database.Append(kPendingSerializediOSDump);
CreateDirectory(base_dir_); CreateDirectory(base_dir_);
prune_thread_.reset(new PruneIntermediateDumpsAndCrashReportsThread(
database_.get(),
PruneCondition::GetDefault(),
base_dir_,
bundle_identifier_and_seperator_,
system_data.IsExtension()));
prune_thread_->Start();
if (!OpenNewFile()) if (!OpenNewFile())
return false; return false;

View File

@ -19,6 +19,7 @@
#include <vector> #include <vector>
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h"
#include "handler/crash_report_upload_thread.h" #include "handler/crash_report_upload_thread.h"
#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h" #include "snapshot/ios/process_snapshot_ios_intermediate_dump.h"
#include "util/ios/ios_intermediate_dump_writer.h" #include "util/ios/ios_intermediate_dump_writer.h"
@ -193,6 +194,7 @@ class InProcessHandler {
std::unique_ptr<IOSIntermediateDumpWriter> writer_; std::unique_ptr<IOSIntermediateDumpWriter> writer_;
std::unique_ptr<IOSIntermediateDumpWriter> alternate_mach_writer_; std::unique_ptr<IOSIntermediateDumpWriter> alternate_mach_writer_;
std::unique_ptr<CrashReportUploadThread> upload_thread_; std::unique_ptr<CrashReportUploadThread> upload_thread_;
std::unique_ptr<PruneIntermediateDumpsAndCrashReportsThread> prune_thread_;
std::unique_ptr<CrashReportDatabase> database_; std::unique_ptr<CrashReportDatabase> database_;
std::string bundle_identifier_and_seperator_; std::string bundle_identifier_and_seperator_;
InitializationStateDcheck initialized_; InitializationStateDcheck initialized_;

View File

@ -0,0 +1,126 @@
// Copyright 2021 The Crashpad Authors. All rights reserved.
//
// 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 "client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h"
#include <utility>
#include "client/prune_crash_reports.h"
#include "util/file/directory_reader.h"
#include "util/file/filesystem.h"
namespace crashpad {
namespace {
// The file extension used to indicate a file is locked.
constexpr char kLockedExtension[] = ".locked";
// 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;
// Unlock any locked intermediate dump after 60 days.
constexpr time_t max_locked_ttl = 60 * 60 * 24 * 60;
// The initial thread delay for applications. Delay the thread's file i/o to
// not interfere with application startup.
constexpr double app_delay = 60;
// The initial thread delay for app extensions. Because iOS extensions are often
// very short lived, do not wait the full |app_delay|, and instead use a shorter
// time.
constexpr double extension_delay = 5;
//! \brief Unlocks old intermediate dumps.
//!
//! This function can unlock (remove the .locked extension) intermediate dumps
//! that are either too old to be useful, or are likely leftover dumps from
//! clean app exits.
//!
//! \param[in] pending_path The path to any locked intermediate dump files.
//! \param[in] bundle_identifier_and_seperator The identifier for this client,
//! used to determine when locked files are considered stale.
void UnlockOldIntermediateDumps(base::FilePath pending_path,
std::string bundle_identifier_and_seperator) {
DirectoryReader reader;
std::vector<base::FilePath> files;
if (!reader.Open(pending_path)) {
return;
}
base::FilePath file;
DirectoryReader::Result result;
while ((result = reader.NextFile(&file)) ==
DirectoryReader::Result::kSuccess) {
if (file.FinalExtension() != kLockedExtension)
continue;
const base::FilePath file_path(pending_path.Append(file));
timespec file_time;
time_t now = time(nullptr);
if (!FileModificationTime(file_path, &file_time)) {
continue;
}
if ((file.value().compare(0,
bundle_identifier_and_seperator.size(),
bundle_identifier_and_seperator) == 0 &&
file_time.tv_sec <= now - matching_bundle_locked_ttl) ||
(file_time.tv_sec <= now - max_locked_ttl)) {
base::FilePath new_path = file_path.RemoveFinalExtension();
MoveFileOrDirectory(file_path, new_path);
continue;
}
}
}
} // namespace
PruneIntermediateDumpsAndCrashReportsThread::
PruneIntermediateDumpsAndCrashReportsThread(
CrashReportDatabase* database,
std::unique_ptr<PruneCondition> condition,
base::FilePath pending_path,
std::string bundle_identifier_and_seperator,
bool is_extension)
: thread_(60 * 60 * 24, 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),
database_(database) {}
PruneIntermediateDumpsAndCrashReportsThread::
~PruneIntermediateDumpsAndCrashReportsThread() {}
void PruneIntermediateDumpsAndCrashReportsThread::Start() {
thread_.Start(initial_work_delay_);
}
void PruneIntermediateDumpsAndCrashReportsThread::Stop() {
thread_.Stop();
}
void PruneIntermediateDumpsAndCrashReportsThread::DoWork(
const WorkerThread* thread) {
database_->CleanDatabase(60 * 60 * 24 * 3);
PruneCrashReportDatabase(database_, condition_.get());
if (!clean_old_intermediate_dumps_) {
clean_old_intermediate_dumps_ = true;
UnlockOldIntermediateDumps(pending_path_, bundle_identifier_and_seperator_);
}
}
} // namespace crashpad

View File

@ -0,0 +1,102 @@
// Copyright 2021 The Crashpad Authors. All rights reserved.
//
// 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.
#ifndef CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_
#define CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_
#include <memory>
#include "base/files/file_path.h"
#include "util/thread/stoppable.h"
#include "util/thread/worker_thread.h"
namespace crashpad {
class CrashReportDatabase;
class PruneCondition;
//! \brief A thread that periodically prunes crash reports from the database
//! using the specified condition, and any leftover locked intermediate
//! dumps.
//!
//! After the thread is started, the database is pruned using the condition
//! every 24 hours. Upon calling Start(), the thread waits 5 seconds before
//! performing the initial prune operation.
//!
//! Locked intermediate dump files are unlocked only once, not periodically.
//! Locked dumps that match this bundle id can be unlocked if they are over a
//! day old. Otherwise, unlock dumps that are over 60 days old.
class PruneIntermediateDumpsAndCrashReportsThread
: public WorkerThread::Delegate,
public Stoppable {
public:
//! \brief Constructs a new object.
//!
//! \param[in] database The database to prune crash reports from.
//! \param[in] condition The condition used to evaluate crash reports for
//! pruning.
//! \param[in] pending_path The path to any locked intermediate dump files.
//! \param[in] bundle_identifier_and_seperator The identifier for this client,
//! used to determine when locked files are considered stale, with a
//! seperator at the end to allow for substring searches.
PruneIntermediateDumpsAndCrashReportsThread(
CrashReportDatabase* database,
std::unique_ptr<PruneCondition> condition,
base::FilePath pending_path,
std::string bundle_identifier_and_seperator,
bool is_extension);
PruneIntermediateDumpsAndCrashReportsThread(
const PruneIntermediateDumpsAndCrashReportsThread&) = delete;
PruneIntermediateDumpsAndCrashReportsThread& operator=(
const PruneIntermediateDumpsAndCrashReportsThread&) = delete;
~PruneIntermediateDumpsAndCrashReportsThread();
// Stoppable:
//! \brief Starts a dedicated pruning thread.
//!
//! The thread waits before running the initial prune, so as to not interfere
//! with any startup-related IO performed by the client.
//!
//! This method may only be be called on a newly-constructed object or after
//! a call to Stop().
void Start() override;
//! \brief Stops the pruning thread.
//!
//! This method must only be called after Start(). If Start() has been called,
//! this method must be called before destroying an object of this class.
//!
//! This method may be called from any thread other than the pruning thread.
//! It is expected to only be called from the same thread that called Start().
void Stop() override;
private:
// WorkerThread::Delegate:
void DoWork(const WorkerThread* thread) override;
WorkerThread thread_;
std::unique_ptr<PruneCondition> condition_;
base::FilePath pending_path_;
std::string bundle_identifier_and_seperator_;
bool clean_old_intermediate_dumps_;
double initial_work_delay_;
CrashReportDatabase* database_; // weak
};
} // namespace crashpad
#endif // CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_

View File

@ -33,6 +33,7 @@ class IOSSystemDataCollector {
int ProcessorCount() const { return processor_count_; } int ProcessorCount() const { return processor_count_; }
const std::string& Build() const { return build_; } const std::string& Build() const { return build_; }
const std::string& BundleIdentifier() const { return bundle_identifier_; } const std::string& BundleIdentifier() const { return bundle_identifier_; }
bool IsExtension() const { return is_extension_; }
const std::string& CPUVendor() const { return cpu_vendor_; } const std::string& CPUVendor() const { return cpu_vendor_; }
bool HasDaylightSavingTime() const { return has_next_daylight_saving_time_; } bool HasDaylightSavingTime() const { return has_next_daylight_saving_time_; }
bool IsDaylightSavingTime() const { return is_daylight_saving_time_; } bool IsDaylightSavingTime() const { return is_daylight_saving_time_; }
@ -68,6 +69,7 @@ class IOSSystemDataCollector {
int patch_version_; int patch_version_;
std::string build_; std::string build_;
std::string bundle_identifier_; std::string bundle_identifier_;
bool is_extension_;
std::string machine_description_; std::string machine_description_;
int orientation_; int orientation_;
int processor_count_; int processor_count_;

View File

@ -79,6 +79,7 @@ IOSSystemDataCollector::IOSSystemDataCollector()
build_ = ReadStringSysctlByName("kern.osversion"); build_ = ReadStringSysctlByName("kern.osversion");
bundle_identifier_ = bundle_identifier_ =
base::SysNSStringToUTF8([[NSBundle mainBundle] bundleIdentifier]); base::SysNSStringToUTF8([[NSBundle mainBundle] bundleIdentifier]);
is_extension_ = [[NSBundle mainBundle].bundlePath hasSuffix:@"appex"];
#if defined(ARCH_CPU_X86_64) #if defined(ARCH_CPU_X86_64)
cpu_vendor_ = ReadStringSysctlByName("machdep.cpu.vendor"); cpu_vendor_ = ReadStringSysctlByName("machdep.cpu.vendor");