1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/metrics/persistent_histogram_storage.h"
6
7 #include "base/files/file_util.h"
8 #include "base/files/important_file_writer.h"
9 #include "base/logging.h"
10 #include "base/metrics/persistent_histogram_allocator.h"
11 #include "base/metrics/persistent_memory_allocator.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/time/time.h"
15 #include "build/build_config.h"
16
17 namespace {
18
19 constexpr size_t kAllocSize = 1 << 20; // 1 MiB
20
21 } // namespace
22
23 namespace base {
24
PersistentHistogramStorage(StringPiece allocator_name,StorageDirManagement storage_dir_management)25 PersistentHistogramStorage::PersistentHistogramStorage(
26 StringPiece allocator_name,
27 StorageDirManagement storage_dir_management)
28 : storage_dir_management_(storage_dir_management) {
29 DCHECK(!allocator_name.empty());
30 DCHECK(IsStringASCII(allocator_name));
31
32 GlobalHistogramAllocator::CreateWithLocalMemory(kAllocSize,
33 0, // No identifier.
34 allocator_name);
35 GlobalHistogramAllocator::Get()->CreateTrackingHistograms(allocator_name);
36 }
37
~PersistentHistogramStorage()38 PersistentHistogramStorage::~PersistentHistogramStorage() {
39 PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
40 allocator->UpdateTrackingHistograms();
41
42 // TODO(chengx): Investigate making early return depend on whethere there are
43 // metrics to report at this point or not.
44 if (disabled_)
45 return;
46
47 // Stop if the storage base directory has not been properly set.
48 if (storage_base_dir_.empty()) {
49 LOG(ERROR)
50 << "Could not write \"" << allocator->Name()
51 << "\" persistent histograms to file as the storage base directory "
52 "is not properly set.";
53 return;
54 }
55
56 FilePath storage_dir = storage_base_dir_.AppendASCII(allocator->Name());
57
58 switch (storage_dir_management_) {
59 case StorageDirManagement::kCreate:
60 if (!CreateDirectory(storage_dir)) {
61 LOG(ERROR)
62 << "Could not write \"" << allocator->Name()
63 << "\" persistent histograms to file as the storage directory "
64 "cannot be created.";
65 return;
66 }
67 break;
68 case StorageDirManagement::kUseExisting:
69 if (!DirectoryExists(storage_dir)) {
70 // When the consumer of this class decides to use an existing storage
71 // directory, it should ensure the directory's existence if it's
72 // essential.
73 LOG(ERROR)
74 << "Could not write \"" << allocator->Name()
75 << "\" persistent histograms to file as the storage directory "
76 "does not exist.";
77 return;
78 }
79 break;
80 }
81
82 // Save data using the current time as the filename. The actual filename
83 // doesn't matter (so long as it ends with the correct extension) but this
84 // works as well as anything.
85 Time::Exploded exploded;
86 Time::Now().LocalExplode(&exploded);
87 const FilePath file_path =
88 storage_dir
89 .AppendASCII(StringPrintf("%04d%02d%02d%02d%02d%02d", exploded.year,
90 exploded.month, exploded.day_of_month,
91 exploded.hour, exploded.minute,
92 exploded.second))
93 .AddExtension(PersistentMemoryAllocator::kFileExtension);
94
95 StringPiece contents(static_cast<const char*>(allocator->data()),
96 allocator->used());
97 if (!ImportantFileWriter::WriteFileAtomically(file_path, contents)) {
98 LOG(ERROR) << "Persistent histograms fail to write to file: "
99 << file_path.value();
100 }
101 }
102
103 } // namespace base
104