• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
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/process/memory.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/time/time.h"
16 #include "build/build_config.h"
17 
18 #if BUILDFLAG(IS_WIN)
19 #include <windows.h>
20 // Must be after <windows.h>
21 #include <memoryapi.h>
22 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
23 #include <sys/mman.h>
24 #endif
25 
26 namespace {
27 
28 constexpr size_t kAllocSize = 1 << 20;  // 1 MiB
29 
AllocateLocalMemory(size_t size)30 void* AllocateLocalMemory(size_t size) {
31   void* address;
32 
33 #if BUILDFLAG(IS_WIN)
34   address =
35       ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
36   if (address)
37     return address;
38 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
39   // MAP_ANON is deprecated on Linux but MAP_ANONYMOUS is not universal on Mac.
40   // MAP_SHARED is not available on Linux <2.4 but required on Mac.
41   address = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED,
42                    -1, 0);
43   if (address != MAP_FAILED)
44     return address;
45 #else
46 #error This architecture is not (yet) supported.
47 #endif
48 
49   // As a last resort, just allocate the memory from the heap. This will
50   // achieve the same basic result but the acquired memory has to be
51   // explicitly zeroed and thus realized immediately (i.e. all pages are
52   // added to the process now instead of only when first accessed).
53   if (!base::UncheckedMalloc(size, &address))
54     return nullptr;
55   DCHECK(address);
56   memset(address, 0, size);
57   return address;
58 }
59 
60 }  // namespace
61 
62 namespace base {
63 
PersistentHistogramStorage(StringPiece allocator_name,StorageDirManagement storage_dir_management)64 PersistentHistogramStorage::PersistentHistogramStorage(
65     StringPiece allocator_name,
66     StorageDirManagement storage_dir_management)
67     : storage_dir_management_(storage_dir_management) {
68   DCHECK(!allocator_name.empty());
69   DCHECK(IsStringASCII(allocator_name));
70 
71   // This code may be executed before crash handling and/or OOM handling has
72   // been initialized for the process. Silently ignore a failed allocation
73   // (no metric persistence) rather that generating a crash that won't be
74   // caught/reported.
75   void* memory = AllocateLocalMemory(kAllocSize);
76   if (!memory)
77     return;
78 
79   GlobalHistogramAllocator::CreateWithPersistentMemory(memory, kAllocSize, 0,
80                                                        0,  // No identifier.
81                                                        allocator_name);
82   GlobalHistogramAllocator::Get()->CreateTrackingHistograms(allocator_name);
83 }
84 
~PersistentHistogramStorage()85 PersistentHistogramStorage::~PersistentHistogramStorage() {
86   PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
87   if (!allocator)
88     return;
89 
90   allocator->UpdateTrackingHistograms();
91 
92   if (disabled_)
93     return;
94 
95   // Stop if the storage base directory has not been properly set.
96   if (storage_base_dir_.empty()) {
97     LOG(ERROR)
98         << "Could not write \"" << allocator->Name()
99         << "\" persistent histograms to file as the storage base directory "
100            "is not properly set.";
101     return;
102   }
103 
104   FilePath storage_dir = storage_base_dir_.AppendASCII(allocator->Name());
105 
106   switch (storage_dir_management_) {
107     case StorageDirManagement::kCreate:
108       if (!CreateDirectory(storage_dir)) {
109         LOG(ERROR)
110             << "Could not write \"" << allocator->Name()
111             << "\" persistent histograms to file as the storage directory "
112                "cannot be created.";
113         return;
114       }
115       break;
116     case StorageDirManagement::kUseExisting:
117       if (!DirectoryExists(storage_dir)) {
118         // When the consumer of this class decides to use an existing storage
119         // directory, it should ensure the directory's existence if it's
120         // essential.
121         LOG(ERROR)
122             << "Could not write \"" << allocator->Name()
123             << "\" persistent histograms to file as the storage directory "
124                "does not exist.";
125         return;
126       }
127       break;
128   }
129 
130   // Save data using the current time as the filename. The actual filename
131   // doesn't matter (so long as it ends with the correct extension) but this
132   // works as well as anything.
133   //
134   // NOTE: Cannot use `UnlocalizedTimeFormatWithPattern()` here since `//base`
135   // cannot depend on `//base:i18n`.
136   Time::Exploded exploded;
137   Time::Now().LocalExplode(&exploded);
138   const FilePath file_path =
139       storage_dir
140           .AppendASCII(StringPrintf("%04d%02d%02d%02d%02d%02d", exploded.year,
141                                     exploded.month, exploded.day_of_month,
142                                     exploded.hour, exploded.minute,
143                                     exploded.second))
144           .AddExtension(PersistentMemoryAllocator::kFileExtension);
145 
146   StringPiece contents(static_cast<const char*>(allocator->data()),
147                        allocator->used());
148   if (!ImportantFileWriter::WriteFileAtomically(file_path, contents)) {
149     LOG(ERROR) << "Persistent histograms fail to write to file: "
150                << file_path.value();
151   }
152 }
153 
154 }  // namespace base
155