blob: 8abb646c7e4d5e8167fe06f936854ed630f03efb [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/metrics/persistent_histogram_storage.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
#include "base/logging.h"
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "build/build_config.h"
#if defined(STARBOARD)
#include "starboard/file.h"
#endif
namespace {
constexpr size_t kAllocSize = 1 << 20; // 1 MiB
} // namespace
namespace base {
PersistentHistogramStorage::PersistentHistogramStorage(
StringPiece allocator_name,
StorageDirManagement storage_dir_management)
: storage_dir_management_(storage_dir_management) {
DCHECK(!allocator_name.empty());
DCHECK(IsStringASCII(allocator_name));
GlobalHistogramAllocator::CreateWithLocalMemory(kAllocSize,
0, // No identifier.
allocator_name);
GlobalHistogramAllocator::Get()->CreateTrackingHistograms(allocator_name);
}
PersistentHistogramStorage::~PersistentHistogramStorage() {
PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
allocator->UpdateTrackingHistograms();
// TODO(chengx): Investigate making early return depend on whethere there are
// metrics to report at this point or not.
if (disabled_)
return;
// Stop if the storage base directory has not been properly set.
if (storage_base_dir_.empty()) {
LOG(ERROR)
<< "Could not write \"" << allocator->Name()
<< "\" persistent histograms to file as the storage base directory "
"is not properly set.";
return;
}
FilePath storage_dir = storage_base_dir_.AppendASCII(allocator->Name());
switch (storage_dir_management_) {
case StorageDirManagement::kCreate:
if (!CreateDirectory(storage_dir)) {
LOG(ERROR)
<< "Could not write \"" << allocator->Name()
<< "\" persistent histograms to file as the storage directory "
"cannot be created.";
return;
}
break;
case StorageDirManagement::kUseExisting:
if (!DirectoryExists(storage_dir)) {
// When the consumer of this class decides to use an existing storage
// directory, it should ensure the directory's existence if it's
// essential.
LOG(ERROR)
<< "Could not write \"" << allocator->Name()
<< "\" persistent histograms to file as the storage directory "
"does not exist.";
return;
}
break;
}
// Save data using the current time as the filename. The actual filename
// doesn't matter (so long as it ends with the correct extension) but this
// works as well as anything.
Time::Exploded exploded;
Time::Now().LocalExplode(&exploded);
const FilePath file_path =
storage_dir
.AppendASCII(StringPrintf("%04d%02d%02d%02d%02d%02d", exploded.year,
exploded.month, exploded.day_of_month,
exploded.hour, exploded.minute,
exploded.second))
.AddExtension(PersistentMemoryAllocator::kFileExtension);
StringPiece contents(static_cast<const char*>(allocator->data()),
allocator->used());
#if defined(STARBOARD)
// All path should be UTF8 above Starboard.
SbFileError error;
bool out_created;
starboard::ScopedFile sb_file(file_path.AsUTF8Unsafe().c_str(),
kSbFileCreateAlways | kSbFileWrite,
&out_created, &error);
int bytes_written = sb_file.WriteAll(contents.data(), contents.size());
if (bytes_written == contents.size()) {
#else
if (!ImportantFileWriter::WriteFileAtomically(file_path, contents)) {
#endif
LOG(ERROR) << "Persistent histograms fail to write to file: "
<< file_path.value();
}
}
} // namespace base