// 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 #include #include #include "base/logging.h" namespace crashpad { void PruneCrashReportDatabase(CrashReportDatabase* database, PruneCondition* condition) { std::vector 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 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::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((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