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