// 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" 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(allocator->data()), allocator->used()); if (!ImportantFileWriter::WriteFileAtomically(file_path, contents)) { LOG(ERROR) << "Persistent histograms fail to write to file: " << file_path.value(); } } } // namespace base