summaryrefslogtreecommitdiff
path: root/base/files/important_file_writer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/files/important_file_writer.cc')
-rw-r--r--base/files/important_file_writer.cc165
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