diff options
Diffstat (limited to 'base/files/important_file_writer.cc')
-rw-r--r-- | base/files/important_file_writer.cc | 165 |
1 files changed, 127 insertions, 38 deletions
diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc index b46846277b..235bb8d36b 100644 --- a/base/files/important_file_writer.cc +++ b/base/files/important_file_writer.cc @@ -11,6 +11,7 @@ #include <utility> #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/critical_closure.h" #include "base/debug/alias.h" #include "base/files/file.h" @@ -18,6 +19,7 @@ #include "base/files/file_util.h" #include "base/logging.h" #include "base/macros.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" @@ -32,7 +34,7 @@ namespace base { namespace { -const int kDefaultCommitIntervalMs = 10000; +constexpr auto kDefaultCommitInterval = TimeDelta::FromSeconds(10); // This enum is used to define the buckets for an enumerated UMA histogram. // Hence, @@ -48,10 +50,50 @@ enum TempFileFailure { TEMP_FILE_FAILURE_MAX }; -void LogFailure(const FilePath& path, TempFileFailure failure_code, +// Helper function to write samples to a histogram with a dynamically assigned +// histogram name. Works with different error code types convertible to int +// which is the actual argument type of UmaHistogramExactLinear. +template <typename SampleType> +void UmaHistogramExactLinearWithSuffix(const char* histogram_name, + StringPiece histogram_suffix, + SampleType add_sample, + SampleType max_sample) { + static_assert(std::is_convertible<SampleType, int>::value, + "SampleType should be convertible to int"); + DCHECK(histogram_name); + std::string histogram_full_name(histogram_name); + if (!histogram_suffix.empty()) { + histogram_full_name.append("."); + histogram_full_name.append(histogram_suffix.data(), + histogram_suffix.length()); + } + UmaHistogramExactLinear(histogram_full_name, static_cast<int>(add_sample), + static_cast<int>(max_sample)); +} + +// Helper function to write samples to a histogram with a dynamically assigned +// histogram name. Works with short timings from 1 ms up to 10 seconds (50 +// buckets) which is the actual argument type of UmaHistogramTimes. +void UmaHistogramTimesWithSuffix(const char* histogram_name, + StringPiece histogram_suffix, + TimeDelta sample) { + DCHECK(histogram_name); + std::string histogram_full_name(histogram_name); + if (!histogram_suffix.empty()) { + histogram_full_name.append("."); + histogram_full_name.append(histogram_suffix.data(), + histogram_suffix.length()); + } + UmaHistogramTimes(histogram_full_name, sample); +} + +void LogFailure(const FilePath& path, + StringPiece histogram_suffix, + TempFileFailure failure_code, StringPiece message) { - UMA_HISTOGRAM_ENUMERATION("ImportantFile.TempFileFailures", failure_code, - TEMP_FILE_FAILURE_MAX); + UmaHistogramExactLinearWithSuffix("ImportantFile.TempFileFailures", + histogram_suffix, failure_code, + TEMP_FILE_FAILURE_MAX); DPLOG(WARNING) << "temp file failure: " << path.value() << " : " << message; } @@ -61,21 +103,38 @@ void WriteScopedStringToFileAtomically( const FilePath& path, std::unique_ptr<std::string> data, Closure before_write_callback, - Callback<void(bool success)> after_write_callback) { + Callback<void(bool success)> after_write_callback, + const std::string& histogram_suffix) { if (!before_write_callback.is_null()) before_write_callback.Run(); - bool result = ImportantFileWriter::WriteFileAtomically(path, *data); + TimeTicks start_time = TimeTicks::Now(); + bool result = + ImportantFileWriter::WriteFileAtomically(path, *data, histogram_suffix); + if (result) { + UmaHistogramTimesWithSuffix("ImportantFile.TimeToWrite", histogram_suffix, + TimeTicks::Now() - start_time); + } if (!after_write_callback.is_null()) after_write_callback.Run(result); } +void DeleteTmpFile(const FilePath& tmp_file_path, + StringPiece histogram_suffix) { + if (!DeleteFile(tmp_file_path, false)) { + UmaHistogramExactLinearWithSuffix( + "ImportantFile.FileDeleteError", histogram_suffix, + -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX); + } +} + } // namespace // static bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, - StringPiece data) { + StringPiece data, + StringPiece histogram_suffix) { #if defined(OS_CHROMEOS) // On Chrome OS, chrome gets killed when it cannot finish shutdown quickly, // and this function seems to be one of the slowest shutdown steps. @@ -96,13 +155,21 @@ bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, // is securely created. FilePath tmp_file_path; if (!CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) { - LogFailure(path, FAILED_CREATING, "could not create temporary file"); + UmaHistogramExactLinearWithSuffix( + "ImportantFile.FileCreateError", histogram_suffix, + -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX); + LogFailure(path, histogram_suffix, FAILED_CREATING, + "could not create temporary file"); return false; } File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE); if (!tmp_file.IsValid()) { - LogFailure(path, FAILED_OPENING, "could not open temporary file"); + UmaHistogramExactLinearWithSuffix( + "ImportantFile.FileOpenError", histogram_suffix, + -tmp_file.error_details(), -base::File::FILE_ERROR_MAX); + LogFailure(path, histogram_suffix, FAILED_OPENING, + "could not open temporary file"); DeleteFile(tmp_file_path, false); return false; } @@ -110,25 +177,35 @@ bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, // If this fails in the wild, something really bad is going on. const int data_length = checked_cast<int32_t>(data.length()); int bytes_written = tmp_file.Write(0, data.data(), data_length); + if (bytes_written < data_length) { + UmaHistogramExactLinearWithSuffix( + "ImportantFile.FileWriteError", histogram_suffix, + -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX); + } bool flush_success = tmp_file.Flush(); tmp_file.Close(); if (bytes_written < data_length) { - LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" + - IntToString(bytes_written)); - DeleteFile(tmp_file_path, false); + LogFailure(path, histogram_suffix, FAILED_WRITING, + "error writing, bytes_written=" + IntToString(bytes_written)); + DeleteTmpFile(tmp_file_path, histogram_suffix); return false; } if (!flush_success) { - LogFailure(path, FAILED_FLUSHING, "error flushing"); - DeleteFile(tmp_file_path, false); + LogFailure(path, histogram_suffix, FAILED_FLUSHING, "error flushing"); + DeleteTmpFile(tmp_file_path, histogram_suffix); return false; } - if (!ReplaceFile(tmp_file_path, path, nullptr)) { - LogFailure(path, FAILED_RENAMING, "could not rename temporary file"); - DeleteFile(tmp_file_path, false); + base::File::Error replace_file_error = base::File::FILE_OK; + if (!ReplaceFile(tmp_file_path, path, &replace_file_error)) { + UmaHistogramExactLinearWithSuffix("ImportantFile.FileRenameError", + histogram_suffix, -replace_file_error, + -base::File::FILE_ERROR_MAX); + LogFailure(path, histogram_suffix, FAILED_RENAMING, + "could not rename temporary file"); + DeleteTmpFile(tmp_file_path, histogram_suffix); return false; } @@ -137,26 +214,29 @@ bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, ImportantFileWriter::ImportantFileWriter( const FilePath& path, - scoped_refptr<SequencedTaskRunner> task_runner) - : ImportantFileWriter( - path, - std::move(task_runner), - TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)) {} + scoped_refptr<SequencedTaskRunner> task_runner, + const char* histogram_suffix) + : ImportantFileWriter(path, + std::move(task_runner), + kDefaultCommitInterval, + histogram_suffix) {} ImportantFileWriter::ImportantFileWriter( const FilePath& path, scoped_refptr<SequencedTaskRunner> task_runner, - TimeDelta interval) + TimeDelta interval, + const char* histogram_suffix) : path_(path), task_runner_(std::move(task_runner)), serializer_(nullptr), commit_interval_(interval), + histogram_suffix_(histogram_suffix ? histogram_suffix : ""), weak_factory_(this) { - DCHECK(CalledOnValidThread()); DCHECK(task_runner_); } ImportantFileWriter::~ImportantFileWriter() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // We're usually a member variable of some other object, which also tends // to be our serializer. It may not be safe to call back to the parent object // being destructed. @@ -164,23 +244,21 @@ ImportantFileWriter::~ImportantFileWriter() { } bool ImportantFileWriter::HasPendingWrite() const { - DCHECK(CalledOnValidThread()); - return timer_.IsRunning(); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return timer().IsRunning(); } void ImportantFileWriter::WriteNow(std::unique_ptr<std::string> data) { - DCHECK(CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!IsValueInRangeForNumericType<int32_t>(data->length())) { NOTREACHED(); return; } - if (HasPendingWrite()) - timer_.Stop(); - - Closure task = Bind(&WriteScopedStringToFileAtomically, path_, Passed(&data), - Passed(&before_next_write_callback_), - Passed(&after_next_write_callback_)); + Closure task = AdaptCallbackForRepeating( + BindOnce(&WriteScopedStringToFileAtomically, path_, std::move(data), + std::move(before_next_write_callback_), + std::move(after_next_write_callback_), histogram_suffix_)); if (!task_runner_->PostTask(FROM_HERE, MakeCriticalClosure(task))) { // Posting the task to background message loop is not expected @@ -190,17 +268,19 @@ void ImportantFileWriter::WriteNow(std::unique_ptr<std::string> data) { task.Run(); } + ClearPendingWrite(); } void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) { - DCHECK(CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(serializer); serializer_ = serializer; - if (!timer_.IsRunning()) { - timer_.Start(FROM_HERE, commit_interval_, this, - &ImportantFileWriter::DoScheduledWrite); + if (!timer().IsRunning()) { + timer().Start( + FROM_HERE, commit_interval_, + Bind(&ImportantFileWriter::DoScheduledWrite, Unretained(this))); } } @@ -213,7 +293,7 @@ void ImportantFileWriter::DoScheduledWrite() { DLOG(WARNING) << "failed to serialize data to be saved in " << path_.value(); } - serializer_ = nullptr; + ClearPendingWrite(); } void ImportantFileWriter::RegisterOnNextWriteCallbacks( @@ -223,4 +303,13 @@ void ImportantFileWriter::RegisterOnNextWriteCallbacks( after_next_write_callback_ = after_next_write_callback; } +void ImportantFileWriter::ClearPendingWrite() { + timer().Stop(); + serializer_ = nullptr; +} + +void ImportantFileWriter::SetTimerForTesting(Timer* timer_override) { + timer_override_ = timer_override; +} + } // namespace base |