Index: client/prune_crash_reports.cc |
diff --git a/client/prune_crash_reports.cc b/client/prune_crash_reports.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..dadb93d7423e3bbcb3690753ea8f91ee953f0f67 |
--- /dev/null |
+++ b/client/prune_crash_reports.cc |
@@ -0,0 +1,132 @@ |
+// Copyright 2015 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/prune_crash_reports.h" |
+ |
+#include <sys/stat.h> |
+ |
+#include <algorithm> |
+#include <vector> |
+ |
+#include "base/logging.h" |
+ |
+namespace crashpad { |
+ |
+void PruneCrashReportDatabase(CrashReportDatabase* database, |
+ PruneCondition* condition) { |
+ std::vector<CrashReportDatabase::Report> all_reports; |
+ CrashReportDatabase::OperationStatus status; |
+ |
+ status = database->GetPendingReports(&all_reports); |
+ if (status != CrashReportDatabase::kNoError) { |
+ LOG(ERROR) << "PruneCrashReportDatabase: Failed to get pending reports"; |
+ return; |
+ } |
+ |
+ std::vector<CrashReportDatabase::Report> completed_reports; |
+ status = database->GetCompletedReports(&completed_reports); |
+ if (status != CrashReportDatabase::kNoError) { |
+ LOG(ERROR) << "PruneCrashReportDatabase: Failed to get completed reports"; |
+ return; |
+ } |
+ all_reports.insert(all_reports.end(), completed_reports.begin(), |
+ completed_reports.end()); |
+ |
+ std::sort(all_reports.begin(), all_reports.end(), |
+ [](const CrashReportDatabase::Report& lhs, |
+ const CrashReportDatabase::Report& rhs) { |
+ return lhs.creation_time > rhs.creation_time; |
+ }); |
+ |
+ for (const auto& report : all_reports) { |
+ if (condition->ShouldPruneReport(report)) { |
+ status = database->DeleteReport(report.uuid); |
+ if (status != CrashReportDatabase::kNoError) { |
+ LOG(ERROR) << "Database Pruning: Failed to remove report " |
+ << report.uuid.ToString(); |
+ } |
+ } |
+ } |
+ |
+ // TODO(rsesek): For databases that do not use a directory structure, |
+ // it is possible for the metadata sidecar to become corrupted and thus |
+ // leave orphaned crash report files on-disk. |
+ // https://code.google.com/p/crashpad/issues/detail?id=66 |
+} |
+ |
+// static |
+scoped_ptr<PruneCondition> PruneCondition::GetDefault() { |
+ // DatabaseSizePruneCondition must be the LHS so that it is always evaluated, |
+ // due to the short-circuting behavior of BinaryPruneCondition. |
+ return make_scoped_ptr(new BinaryPruneCondition(BinaryPruneCondition::OR, |
+ new DatabaseSizePruneCondition(1024 * 128), new AgePruneCondition(365))); |
+} |
+ |
+static const time_t kSecondsInDay = 60 * 60 * 24; |
+ |
+AgePruneCondition::AgePruneCondition(int max_age_in_days) |
+ : oldest_report_time_( |
+ ((time(nullptr) - (max_age_in_days * kSecondsInDay)) |
+ / kSecondsInDay) * kSecondsInDay) {} |
+ |
+AgePruneCondition::~AgePruneCondition() {} |
+ |
+bool AgePruneCondition::ShouldPruneReport( |
+ const CrashReportDatabase::Report& report) { |
+ return report.creation_time < oldest_report_time_; |
+} |
+ |
+DatabaseSizePruneCondition::DatabaseSizePruneCondition(size_t max_size_in_kb) |
+ : max_size_in_kb_(max_size_in_kb), measured_size_in_kb_(0) {} |
+ |
+DatabaseSizePruneCondition::~DatabaseSizePruneCondition() {} |
+ |
+bool DatabaseSizePruneCondition::ShouldPruneReport( |
+ const CrashReportDatabase::Report& report) { |
+#if defined(OS_POSIX) |
+ struct stat statbuf; |
+ if (stat(report.file_path.value().c_str(), &statbuf) == 0) { |
+#elif defined(OS_WIN) |
+ struct _stati64 statbuf; |
+ if (_wstat64(report.file_path.value().c_str(), &statbuf) == 0) { |
+#else |
+#error "Not implemented" |
+#endif |
+ // Round up fractional KB to the next 1-KB boundary. |
+ measured_size_in_kb_ += |
+ static_cast<size_t>((statbuf.st_size + 1023) / 1024); |
+ } |
+ return measured_size_in_kb_ > max_size_in_kb_; |
+} |
+ |
+BinaryPruneCondition::BinaryPruneCondition( |
+ Operator op, PruneCondition* lhs, PruneCondition* rhs) |
+ : op_(op), lhs_(lhs), rhs_(rhs) {} |
+ |
+BinaryPruneCondition::~BinaryPruneCondition() {} |
+ |
+bool BinaryPruneCondition::ShouldPruneReport( |
+ const CrashReportDatabase::Report& report) { |
+ switch (op_) { |
+ case AND: |
+ return lhs_->ShouldPruneReport(report) && rhs_->ShouldPruneReport(report); |
+ case OR: |
+ return lhs_->ShouldPruneReport(report) || rhs_->ShouldPruneReport(report); |
+ default: |
+ NOTREACHED(); |
+ return false; |
+ } |
+} |
+ |
+} // namespace crashpad |