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